[
  {
    "path": ".config/dotnet-tools.json",
    "content": "{\n  \"version\": 1,\n  \"isRoot\": true,\n  \"tools\": {\n    \"csharpier\": {\n      \"version\": \"1.2.4\",\n      \"commands\": [\n        \"csharpier\"\n      ],\n      \"rollForward\": false\n    }\n  }\n}"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"[BUG]-\"\nlabels: bug\nassignees: ''\n\n---\n\n**Was this bug identified in a specific build version?**\nPlease note the build version where this bug was identified\n\n**Describe the bug**\nA clear and concise description of what the bug is.  \n\n**Stacktrace**\n\nInclude a stack trace of the exception if possible.\n```\nPaste stack trace here\n```\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[FEATURE]-\"\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\nIf this is a request for support for a new device or type, be as specific as possible and include any pertinent manufacturer and model information.\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/rfi_request.md",
    "content": "---\nname: Request for Information\nabout: Request specific information about capabilities of the framework\ntitle: \"[RFI]-\"\nlabels: RFI\nassignees: ''\n\n---\n\n**What is your request?**\nPlease provide as much detail as possible.\n\n\n**What is the intended use case**\n- [ ] Essentials Standalone Application\n- [ ] Essentials + SIMPL Windows Hybrid\n\n**User Interface Requirements**\n- [ ] Not Applicable (logic only)\n- [ ] Crestron Smart Graphics Touchpanel\n- [ ] Cisco Touch10\n- [ ] Mobile Control\n- [ ] Crestron CH5 Touchpanel interface\n\n\n**Additional context**\nAdd any other context or screenshots about the request here.\n"
  },
  {
    "path": ".github/scripts/GenerateVersionNumber-2.0.0.ps1",
    "content": "$latestVersion = [version]\"2.0.0\"\n\n$newVersion = [version]$latestVersion\n$phase = \"\"\n$newVersionString = \"\"\n\nswitch -regex ($Env:GITHUB_REF) {\n  '^refs\\/pull\\/*.' {\n    $phase = 'beta';\n    $newVersionString = \"{0}-{1}-{2}\" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER\n  }\n  '^refs\\/heads\\/feature-2.0.0\\/*.' {\n    $phase = 'alpha'\n    $newVersionString = \"{0}-{1}-{2}\" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER\n  }\n  'development-2.0.0' {\n    $phase = 'beta'\n    $newVersionString = \"{0}-{1}-{2}\" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER\n  }  \n}\n\n\nWrite-Output $newVersionString\n"
  },
  {
    "path": ".github/scripts/GenerateVersionNumber.ps1",
    "content": "$latestVersions = $(git tag --merged origin/main)\n$latestVersion = [version]\"0.0.0\"\nWrite-Host \"GITHUB_REF: $($Env:GITHUB_REF)\"\nWrite-Host \"GITHUB_HEAD_REF: $($Env:GITHUB_HEAD_REF)\"\nWrite-Host \"GITHUB_BASE_REF: $($Env:GITHUB_BASE_REF)\"\nForeach ($version in $latestVersions) {\n  Write-Host $version\n  try {\n    if (([version]$version) -ge $latestVersion) {\n      $latestVersion = $version\n      Write-Host \"Setting latest version to: $latestVersion\"\n    }\n  }\n  catch {\n    Write-Host \"Unable to convert $($version). Skipping\"\n    continue;\n  }\n}\n\n$newVersion = [version]$latestVersion\n$phase = \"\"\n$newVersionString = \"\"\n\nswitch -regex ($Env:GITHUB_REF) {\n  '^refs\\/pull\\/*.' {\n    $splitRef = $Env:GITHUB_REF -split \"/\"\n    $phase = \"pr$($splitRef[2])\"\n    $newVersionString = \"{0}.{1}.{2}-{3}-{4}\" -f $newVersion.Major, $newVersion.Minor, ($newVersion.Build + 1), $phase, $Env:GITHUB_RUN_NUMBER\n  }\n  '^refs\\/heads\\/main*.' {\n    $newVersionString = \"{0}.{1}.{2}\" -f $newVersion.Major, $newVersion.Minor, $newVersion.Build\n  }\n  '^refs\\/heads\\/feature\\/*.' {\n    $phase = 'alpha'\n    $newVersionString = \"{0}.{1}.{2}-{3}-{4}\" -f $newVersion.Major, $newVersion.Minor, ($newVersion.Build + 1), $phase, $Env:GITHUB_RUN_NUMBER\n  }\n  '^refs\\/heads\\/release\\/*.' {\n    $splitRef = $Env:GITHUB_REF -split \"/\"\n    $version = [version]($splitRef[-1] -replace \"v\", \"\")\n    $phase = 'rc'\n    $newVersionString = \"{0}.{1}.{2}-{3}-{4}\" -f $version.Major, $version.Minor, $version.Build, $phase, $Env:GITHUB_RUN_NUMBER\n  }\n  '^refs\\/heads\\/development*.' {\n    $phase = 'beta'\n    $newVersionString = \"{0}.{1}.{2}-{3}-{4}\" -f $newVersion.Major, $newVersion.Minor, ($newVersion.Build + 1), $phase, $Env:GITHUB_RUN_NUMBER\n  }\n  '^refs\\/heads\\/hotfix\\/*.' {\n    $phase = 'hotfix'\n    $newVersionString = \"{0}.{1}.{2}-{3}-{4}\" -f $newVersion.Major, $newVersion.Minor, ($newVersion.Build + 1), $phase, $Env:GITHUB_RUN_NUMBER\n  }\n  '^refs\\/heads\\/bugfix\\/*.' {\n    $phase = 'hotfix'\n    $newVersionString = \"{0}.{1}.{2}-{3}-{4}\" -f $newVersion.Major, $newVersion.Minor, ($newVersion.Build + 1), $phase, $Env:GITHUB_RUN_NUMBER\n  }\n  \n}\n\n\nWrite-Output $newVersionString\n"
  },
  {
    "path": ".github/scripts/UpdateAssemblyVersion.ps1",
    "content": "﻿function Update-SourceVersion {\n    Param ([string]$Version)\n    #$fullVersion = $Version\n    $baseVersion = [regex]::Match($Version, \"(\\d+.\\d+.\\d+).*\").captures.groups[1].value\n    $NewAssemblyVersion = ‘AssemblyVersion(\"‘ + $baseVersion + ‘.*\")’\n    Write-Output \"AssemblyVersion = $NewAssemblyVersion\"\n    $NewAssemblyInformationalVersion = ‘AssemblyInformationalVersion(\"‘ + $Version + ‘\")’\n    Write-Output \"AssemblyInformationalVersion = $NewAssemblyInformationalVersion\"\n\n    foreach ($o in $input) {\n        Write-output $o.FullName\n        $TmpFile = $o.FullName + “.tmp”\n        get-content $o.FullName |\n        ForEach-Object {\n            $_ -replace ‘AssemblyVersion\\(\".*\"\\)’, $NewAssemblyVersion } |\n        ForEach-Object {\n            $_ -replace ‘AssemblyInformationalVersion\\(\".*\"\\)’, $NewAssemblyInformationalVersion\n        }  > $TmpFile \n        move-item $TmpFile $o.FullName -force\n    }\n}\n\nfunction Update-AllAssemblyInfoFiles ( $version ) {\n    foreach ($file in “AssemblyInfo.cs”, “AssemblyInfo.vb” ) {\n        get-childitem -Path $Env:GITHUB_WORKSPACE -recurse | Where-Object { $_.Name -eq $file } | Update-SourceVersion $version ;\n    }\n}\n\n# validate arguments\n$r = [System.Text.RegularExpressions.Regex]::Match($args[0], \"\\d+\\.\\d+\\.\\d+.*\");\nif ($r.Success) {\n    Write-Output \"Updating Assembly Version to $args ...\";\n    Update-AllAssemblyInfoFiles $args[0];\n}\nelse {\n    Write-Output ” “;\n    Write-Output “Error: Input version does not match x.y.z format!”\n    Write-Output ” “;\n    Write-Output \"Unable to apply version to AssemblyInfo.cs files\";\n}\n"
  },
  {
    "path": ".github/scripts/ZipBuildOutput.ps1",
    "content": "# Uncomment these for local testing\n# $Env:GITHUB_WORKSPACE = \"C:\\Working Directories\\PD\\essentials\"\n# $Env:SOLUTION_FILE = \"PepperDashEssentials\"\n# $Env:VERSION = \"0.0.0-buildType-test\"\n\n# Sets the root directory for the operation\n$destination = \"$($Env:GITHUB_HOME)\\output\"\nNew-Item -ItemType Directory -Force -Path ($destination)\nGet-ChildItem ($destination)\n$exclusions = @(git submodule foreach --quiet 'echo $name')\n$exclusions += \"Newtonsoft.Compact.Json.dll\"\n# Trying to get any .json schema files (not currently working)\n# Gets any files with the listed extensions.\nGet-ChildItem -recurse -Path \"$($Env:GITHUB_WORKSPACE)\" -include \"*.clz\", \"*.cpz\", \"*.cplz\", \"*.dll\", \"*.nuspec\" | ForEach-Object {\n  $allowed = $true;\n  # Exclude any files in submodules\n  foreach ($exclude in $exclusions) {\n    if ((Split-Path $_.FullName -Parent).contains(\"$($exclude)\")) {\n      $allowed = $false;\n      break;\n    }\n  }\n  if ($allowed) {\n    Write-Host \"allowing $($_)\"\n    $_;\n  }\n} | Copy-Item -Destination ($destination) -Force\nWrite-Host \"Getting matching files...\"\n# Get any files from the output folder that match the following extensions\nGet-ChildItem -Path $destination | Where-Object { ($_.Extension -eq \".clz\") -or ($_.Extension -eq \".cpz\" -or ($_.Extension -eq \".cplz\")) } | ForEach-Object { \n  # Replace the extensions with dll and xml and create an array \n  # Removed dll file capture, as previous step should capture all of them. Add if needed-> $($_ -replace \"cpz|clz|cplz\", \"dll\"),\n  $filenames = @($($_ -replace \"cpz|clz|cplz\", \"xml\"))\n  Write-Host \"Filenames:\"\n  Write-Host $filenames\n  if ($filenames.length -gt 0) {\n    # Attempt to get the files and return them to the output directory\n    Get-ChildItem -Recurse -Path \"$($Env:GITHUB_WORKSPACE)\" -include $filenames | Copy-Item -Destination ($destination) -Force\n  }\n}\n\nGet-ChildItem -Path $destination\\*.cpz | Rename-Item -NewName { \"$($_.BaseName)-$($Env:VERSION)$($_.Extension)\" }\nCompress-Archive -Path $destination -DestinationPath \"$($Env:GITHUB_WORKSPACE)\\$($Env:SOLUTION_FILE)-$($Env:VERSION).zip\" -Force\nWrite-Host \"Output Contents post Zip\"\nGet-ChildItem -Path $destination"
  },
  {
    "path": ".github/workflows/EssentialsPlugins-builds-4-series-caller.yml",
    "content": "name: Build PepperDash Essentials\n\non:\n  push:\n    branches:\n      - '**'\n\njobs:\n  getVersion:\n    uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-getversion.yml@main\n    secrets: inherit\n  build-4Series:\n    uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-4Series-builds.yml@main\n    secrets: inherit\n    needs: getVersion\n    if: needs.getVersion.outputs.newVersion == 'true'\n    with:\n      newVersion: ${{ needs.getVersion.outputs.newVersion }}\n      version: ${{ needs.getVersion.outputs.version }}\n      tag: ${{ needs.getVersion.outputs.tag }}\n      channel: ${{ needs.getVersion.outputs.channel }}\n      bypassPackageCheck: true\n      devToolsVersion: ${{ vars.ESSENTIALSDEVTOOLSVERSION }}"
  },
  {
    "path": ".github/workflows/publish-docs.yml",
    "content": "name: Publish Docs\n\n# Trigger the action on push to main\non:\n  push:\n    branches:      \n      - main\n\n# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages\npermissions:\n  actions: read\n  pages: write\n  id-token: write\n\n# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.\n# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: false\n  \njobs:\n  publish-docs:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-latest\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v4\n    - name: Dotnet Setup\n      uses: actions/setup-dotnet@v4\n      with:\n        dotnet-version: 8.x\n\n    - run: dotnet tool update -g docfx\n    - run: docfx ./docs/docfx.json\n\n    - name: Upload artifact\n      uses: actions/upload-pages-artifact@v3\n      with:\n        # Upload entire repository\n        path: './docs/_site'\n    - name: Deploy to GitHub Pages\n      id: deployment\n      uses: actions/deploy-pages@v4"
  },
  {
    "path": ".gitignore",
    "content": "#ignore thumbnails created by windows\nThumbs.db\n#Ignore files build by Visual Studio\n*.user\n*.aps\n*.pch\n*.vspscc\n*_i.c\n*_p.c\n*.ncb\n*.suo\n*.bak\n*.cache\n*.ilk\n*.log\n[Bb]in\n[Dd]ebug*/\n*.sbr\nobj/\n[Rr]elease*/\n_ReSharper*/\nSIMPLSharpLogs/\n*.projectinfo\nessentials-framework/EssentialDMTestConfig/\noutput/\npackages/\n\nPepperDashEssentials-0.0.0-buildType-test.zip\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Ww][Ii][Nn]32/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# ASP.NET Scaffolding\nScaffoldingReadMe.txt\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Coverlet is a free, cross platform Code Coverage Tool\ncoverage*.json\ncoverage*.xml\ncoverage*.info\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Fody - auto-generated XML schema\nFodyWeavers.xsd\nessentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/PepperDash_Essentials_Interfaces.csproj\n.DS_Store\n/._PepperDash.Essentials.sln\n.vscode/settings.json\n_site/\napi/\n*.DS_Store\n/._PepperDash.Essentials.4Series.sln\ndotnet\n"
  },
  {
    "path": ".gitmodules",
    "content": ""
  },
  {
    "path": ".releaserc.json",
    "content": "{\n  \"plugins\": [\n    [\n      \"@semantic-release/commit-analyzer\",\n      {\n        \"releaseRules\": [\n          { \"scope\": \"force-patch\", \"release\": \"patch\" },\n          { \"scope\": \"no-release\", \"release\": false }\n        ]\n      }\n    ],\n    \"@semantic-release/release-notes-generator\",    \n    [\"@semantic-release/changelog\", \n      {\n        \"changelogFile\": \"CHANGELOG.md\"\n      }\n    ],\n    [\n      \"@semantic-release/exec\",\n      {\n        \"verifyReleaseCmd\": \"echo \\\"newVersion=true\\\" >> $GITHUB_OUTPUT\",\n        \"publishCmd\": \"echo \\\"version=${nextRelease.version}\\\" >> $GITHUB_OUTPUT && echo \\\"tag=${nextRelease.gitTag}\\\" >> $GITHUB_OUTPUT && echo \\\"type=${nextRelease.type}\\\" >> $GITHUB_OUTPUT && echo \\\"channel=${nextRelease.channel}\\\" >> $GITHUB_OUTPUT\"\n      }\n    ]\n  ],  \n  \"branches\": [\n    \"main\",\n    {\"name\": \"development\", \"prerelease\": \"beta\", \"channel\": \"beta\"},\n    {\"name\": \"release\", \"prerelease\": \"rc\", \"channel\": \"rc\"},\n    {\n      \"name\": \"replace-me-feature-branch\",\n      \"prerelease\": \"replace-me-prerelease\",\n      \"channel\": \"replace-me-prerelease\"\n    }\n  ]\n}"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"ms-dotnettools.vscode-dotnet-runtime\",\n    \"ms-dotnettools.csharp\",\n    \"ms-dotnettools.csdevkit\",\n    \"vivaxy.vscode-conventional-commits\",\n    \"mhutchie.git-graph\"\n  ]\n}"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributors Guide\n\nEssentials is an open source project. If you are interested in making it better,\nthere are many ways you can contribute. For example, you can:\n\n- Submit a bug report\n- Suggest a new feature\n- Provide feedback by commenting on feature requests/proposals\n- Propose a patch by submitting a pull request\n- Suggest or submit documentation improvements\n- Review outstanding pull requests\n- Answer questions from other users\n- Share the software with other users who are interested\n- Teach others to use the software\n\n## Bugs and Feature Requests\n\nIf you believe that you have found a bug or wish to propose a new feature,\nplease first search the existing [issues] to see if it has already been\nreported. If you are unable to find an existing issue, consider using one of\nthe provided templates to create a new issue and provide as many details as you\ncan to assist in reproducing the bug or explaining your proposed feature.\n\n## Patch Submission tips\n\nPatches should be submitted in the form of Pull Requests to the Essentials\n[repository] on GitHub. But first, consider the following tips to ensure a\nsmooth process when submitting a patch:\n\n- Ensure that the patch compiles and does not break any build-time tests.\n- Be understanding, patient, and friendly; developers may need time to review\n  your submissions before they can take action or respond. This does not mean\n  your contribution is not valued. If your contribution has not received a\n  response in a reasonable time, consider commenting with a polite inquiry for\n  an update.\n- Limit your patches to the smallest reasonable change to achieve your intended\n  goal. For example, do not make unnecessary indentation changes; but don't go\n  out of your way to make the patch so minimal that it isn't easy to read,\n  either. Consider the reviewer's perspective.\n- Before submission, please squash your commits to using a message that starts\n  with the issue number and a description of the changes.\n- Isolate multiple patches from each other. If you wish to make several\n  independent patches, do so in separate, smaller pull requests that can be\n  reviewed more easily.\n- Be prepared to answer questions from reviewers. They may have further\n  questions before accepting your patch, and may even propose changes. Please\n  accept this feedback constructively, and not as a rejection of your proposed\n  change.\n  \n## GitFlow Branch Model\nThis repository adheres to the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model and is intitialized for GitFlow to make for consistent branch name prefixes.  Please take time to familiarize yourself with this model.\n\n- `master` will contain the latest stable version of the framework and release builds will be created from tagged commits on `master`.\n- HotFix/Patch Pull Requests should target `master` as the base branch.\n- All other Pull Requests (bug fixes, enhancements, etc.) should target `development` as the base branch.\n- `release/vX.Y.X` branches will be used for release candidates when moving new features from `development` to `master`.  \n  Beta builds will be created from tagged commits on release candidate branches.\n\n## Review\n\n- We welcome code reviews from anyone. A committer is required to formally\n  accept and merge the changes.\n- Reviewers will be looking for things like threading issues, performance\n  implications, API design, duplication of existing functionality, readability\n  and code style, avoidance of bloat (scope-creep), etc.\n- Reviewers will likely ask questions to better understand your change.\n- Reviewers will make comments about changes to your patch:\n    - MUST means that the change is required\n    - SHOULD means that the change is suggested, further discussion on the\n      subject may be required\n    - COULD means that the change is optional\n\n## Timeline and Managing Expectations\n\nAs we continue to engage contributors and learn best practices for running a successful open source project, our processes \nand guidance will likely evolve. We will try to communicate expectations as we are able and to always be responsive. We \nhope that the community will share their suggestions for improving this engagement.  Based on the level of initial interest \nwe receive and the availability of resources to evaluate contributions, we anticipate the following:\n\n- We will initially prioritize pull requests that include small bug fixes and code that addresses potential vulnerabilities   \n  as well as pull requests that include improvements for processor language specifications because these require a \n  reasonable amount of effort to evaluate and will help us exercise and revise our process for accepting contributions.  In \n  other words, we are going to start small in order to work out the kinks first.\n- We are committed to maintaining the integrity and security of our code base.  In addition to the careful review the \n  maintainers will give to code contributions to make sure they do not introduce new bugs or vulnerabilities, we will be \n  trying to identify best practices to incorporate with our open source project so that contributors can have more control \n  over whether their contributions are accepted. These might include things like style guides and requirements for tests and \n  documentation to accompany some code contributions.  As a result, it may take a long time for some contributions to be \n  accepted.  This does not mean we are ignoring them.\n- We are committed to integrating this GitHub project with our team's regular development work flow so that the open source \n  project remains dynamic and relevant.  This may  affect our responsiveness and ability to accept pull requests \n  quickly.  This does not mean we are ignoring them.\n- Not all innovative ideas need to be accepted as pull requests into this GitHub project to be valuable to the community.        \n  There may be times when we recommend that you just share your code for some enhancement to Essentials from your own \n  repository. As we identify and recognize extensions that are of general interest to Essentials, we \n  may seek to incorporate them with our baseline.\n\n## Legal\n\nConsistent with Section D.6. of the GitHub Terms of Service as of 2019, and the MIT license, the project maintainer for this project accepts contributions using the inbound=outbound model.\nWhen you submit a pull request to this repository (inbound), you are agreeing to license your contribution under the same terms as specified in [LICENSE] (outbound).\n\nThis is an open source project.\nContributions you make to this repository are completely voluntary.\nWhen you submit an issue, bug report, question, enhancement, pull request, etc., you are offering your contribution without expectation of payment, you expressly waive any future pay claims against PepperDash related to your contribution, and you acknowledge that this does not create an obligation on the part of PepperDash of any kind.\nFurthermore, your contributing to this project does not create an employer-employee relationship between the PepperDash and the contributor.\n\n[issues]: https://github.com/PepperDash/Essentials/issues\n[repository]: https://github.com/PepperDash/Essentials\n[LICENSE]: https://github.com/PepperDash/Essentials/blob/master/LICENSE.md\n"
  },
  {
    "path": "Crestron-Library-Usage-Analysis.md",
    "content": "# Crestron Library Usage Analysis - PepperDash Essentials\n\nThis document provides a comprehensive analysis of Crestron classes and interfaces used throughout the PepperDash Essentials framework, organized by namespace and library component.\n\n## Executive Summary\n\nThe PepperDash Essentials framework extensively leverages Crestron SDK components across 100+ files, providing abstractions for:\n- Control system hardware (processors, touchpanels, IO devices)\n- Communication interfaces (Serial, TCP/IP, SSH, CEC, IR)\n- Device management and routing\n- User interface components and smart objects\n- System monitoring and diagnostics\n\n## 1. Core Crestron Libraries\n\n### 1.1 Crestron.SimplSharp\n\n**Primary Usage**: Foundational framework components, collections, and basic types.\n\n**Key Files**:\n- Multiple files across all projects use `Crestron.SimplSharp` namespaces\n- Provides basic C# runtime support for Crestron processors\n\n### 1.2 Crestron.SimplSharpPro\n\n**Primary Usage**: Main hardware abstraction layer for Crestron devices.\n\n**Key Classes Used**:\n\n#### CrestronControlSystem\n- **File**: `/src/PepperDash.Essentials/ControlSystem.cs`\n- **Usage**: Base class for the main control system implementation\n- **Implementation**: `public class ControlSystem : CrestronControlSystem, ILoadConfig`\n\n#### Device (Base Class)\n- **Files**: 50+ files inherit from or use this class\n- **Key Implementations**:\n  - `/src/PepperDash.Core/Device.cs` - Core device abstraction\n  - `/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs` - Extended device base\n  - `/src/PepperDash.Essentials.Core/Room/Room.cs` - Room device implementation\n  - `/src/PepperDash.Essentials.Core/Devices/CrestronProcessor.cs` - Processor device wrapper\n\n#### BasicTriList\n- **Files**: 30+ files use this class extensively\n- **Primary Usage**: Touchpanel communication and SIMPL bridging\n- **Key Files**:\n  - `/src/PepperDash.Essentials.Core/Touchpanels/TriListExtensions.cs` - Extension methods for signal handling\n  - `/src/PepperDash.Essentials.Core/Devices/EssentialsBridgeableDevice.cs` - Bridge interface\n  - `/src/PepperDash.Essentials.Core/Touchpanels/ModalDialog.cs` - UI dialog implementation\n\n#### BasicTriListWithSmartObject\n- **Files**: Multiple touchpanel and UI files\n- **Usage**: Enhanced touchpanel support with smart object integration\n- **Key Files**:\n  - `/src/PepperDash.Essentials.Core/Touchpanels/Interfaces.cs` - Interface definitions\n  - `/src/PepperDash.Essentials.Core/SmartObjects/SubpageReferenceList/SubpageReferenceList.cs`\n\n## 2. Communication Hardware\n\n### 2.1 Serial Communication (ComPort)\n\n**Primary Class**: `ComPort`\n**Key Files**:\n- `/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs`\n- `/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs`\n\n**Usage Pattern**:\n```csharp\npublic class ComPortController : Device, IBasicCommunicationWithStreamDebugging\npublic static ComPort GetComPort(EssentialsControlPropertiesConfig config)\n```\n\n**Interface Support**: `IComPorts` - Used for devices that provide multiple COM ports\n\n### 2.2 IR Communication (IROutputPort)\n\n**Primary Class**: `IROutputPort`\n**Key Files**:\n- `/src/PepperDash.Essentials.Core/Devices/IrOutputPortController.cs`\n- `/src/PepperDash.Essentials.Core/Devices/GenericIRController.cs`\n- `/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs`\n\n**Usage Pattern**:\n```csharp\npublic class IrOutputPortController : Device\nIROutputPort IrPort;\npublic IrOutputPortController(string key, IROutputPort port, string irDriverFilepath)\n```\n\n### 2.3 CEC Communication (ICec)\n\n**Primary Interface**: `ICec`\n**Key Files**:\n- `/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs`\n- `/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs`\n\n**Usage Pattern**:\n```csharp\npublic class CecPortController : Device, IBasicCommunicationWithStreamDebugging\npublic static ICec GetCecPort(ControlPropertiesConfig config)\n```\n\n## 3. Input/Output Hardware\n\n### 3.1 Digital Input\n\n**Primary Interface**: `IDigitalInput`\n**Key Files**:\n- `/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs`\n- `/src/PepperDash.Essentials.Core/Microphone Privacy/MicrophonePrivacyController.cs`\n\n**Usage Pattern**:\n```csharp\npublic List<IDigitalInput> Inputs { get; private set; }\nvoid AddInput(IDigitalInput input)\n```\n\n### 3.2 Versiport Support\n\n**Key Files**:\n- `/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs`\n- `/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportAnalogInputDevice.cs`\n- `/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportOutputDevice.cs`\n\n**Usage**: Provides flexible I/O port configuration for various signal types\n\n## 4. Touchpanel Hardware\n\n### 4.1 MPC3 Touchpanel\n\n**Primary Class**: `MPC3Basic`\n**Key File**: `/src/PepperDash.Essentials.Core/Touchpanels/Mpc3Touchpanel.cs`\n\n**Usage Pattern**:\n```csharp\npublic class Mpc3TouchpanelController : Device\nreadonly MPC3Basic _touchpanel;\n_touchpanel = processor.ControllerTouchScreenSlotDevice as MPC3Basic;\n```\n\n### 4.2 TSW Series Support\n\n**Evidence**: References found in messenger files and mobile control components\n**Usage**: Integrated through mobile control messaging system for TSW touchpanel features\n\n## 5. Timer and Threading\n\n### 5.1 CTimer\n\n**Primary Class**: `CTimer`\n**Key File**: `/src/PepperDash.Core/PasswordManagement/PasswordManager.cs`\n\n**Usage Pattern**:\n```csharp\nDebug.Console(1, string.Format(\"PasswordManager.UpdatePassword: CTimer Started\"));\nDebug.Console(1, string.Format(\"PasswordManager.UpdatePassword: CTimer Reset\"));\n```\n\n## 6. Networking and Communication\n\n### 6.1 Ethernet Communication\n\n**Libraries Used**:\n- `Crestron.SimplSharpPro.EthernetCommunication`\n- `Crestron.SimplSharp.Net.Utilities.EthernetHelper`\n\n**Key Files**:\n- `/src/PepperDash.Core/Comm/GenericTcpIpClient.cs`\n- `/src/PepperDash.Core/Comm/GenericTcpIpServer.cs`\n- `/src/PepperDash.Core/Comm/GenericSecureTcpIpClient.cs`\n- `/src/PepperDash.Core/Comm/GenericSshClient.cs`\n- `/src/PepperDash.Core/Comm/GenericUdpServer.cs`\n\n**Usage Pattern**:\n```csharp\npublic class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect\npublic class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect\n```\n\n## 7. Device Management Libraries\n\n### 7.1 DeviceSupport\n\n**Library**: `Crestron.SimplSharpPro.DeviceSupport`\n**Usage**: Core device support infrastructure used throughout the framework\n\n### 7.2 DM (DigitalMedia)\n\n**Library**: `Crestron.SimplSharpPro.DM`\n**Usage**: Digital media routing and switching support\n**Evidence**: Found in routing configuration and DM output card references\n\n## 8. User Interface Libraries\n\n### 8.1 UI Components\n\n**Library**: `Crestron.SimplSharpPro.UI`\n**Usage**: User interface elements and touchpanel controls\n\n### 8.2 Smart Objects\n\n**Key Files**:\n- `/src/PepperDash.Essentials.Core/SmartObjects/SmartObjectDynamicList.cs`\n- `/src/PepperDash.Essentials.Core/SmartObjects/SubpageReferenceList/SubpageReferenceList.cs`\n\n**Usage**: Advanced UI components with dynamic content\n\n## 9. System Monitoring and Diagnostics\n\n### 9.1 Diagnostics\n\n**Library**: `Crestron.SimplSharpPro.Diagnostics`\n**Usage**: System health monitoring and performance tracking\n\n### 9.2 System Information\n\n**Key Files**:\n- `/src/PepperDash.Essentials.Core/Monitoring/SystemMonitorController.cs`\n\n**Usage**: Provides system status, Ethernet information, and program details\n\n## 10. Integration Patterns\n\n### 10.1 SIMPL Bridging\n\n**Pattern**: Extensive use of `BasicTriList` for SIMPL integration\n**Files**: Bridge classes throughout the framework implement `LinkToApi` methods:\n```csharp\npublic abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);\n```\n\n### 10.2 Device Factory Pattern\n\n**Implementation**: Factory classes create hardware-specific implementations\n**Example**: `CommFactory.cs` provides communication device creation\n\n### 10.3 Extension Methods\n\n**Pattern**: Extensive use of extension methods for Crestron classes\n**Example**: `TriListExtensions.cs` adds 30+ extension methods to `BasicTriList`\n\n## 11. Signal Processing\n\n### 11.1 Signal Types\n\n**Bool Signals**: Digital control and feedback\n**UShort Signals**: Analog values and numeric data\n**String Signals**: Text and configuration data\n\n**Implementation**: Comprehensive signal handling in `TriListExtensions.cs`\n\n## 12. Error Handling and Logging\n\n**Pattern**: Consistent use of Crestron's Debug logging throughout\n**Examples**:\n```csharp\nDebug.LogMessage(LogEventLevel.Information, \"Device {0} is not a valid device\", dc.PortDeviceKey);\nDebug.LogMessage(LogEventLevel.Debug, \"Error Waking Panel. Maybe testing with Xpanel?\");\n```\n\n## 13. Threading and Synchronization\n\n**Components**:\n- CTimer for time-based operations\n- Thread-safe collections and patterns\n- Event-driven programming models\n\n## Conclusion\n\nThe PepperDash Essentials framework demonstrates sophisticated integration with the Crestron ecosystem, leveraging:\n\n- **Core Infrastructure**: CrestronControlSystem, Device base classes\n- **Communication**: COM, IR, CEC, TCP/IP, SSH protocols\n- **Hardware Abstraction**: Touchpanels, I/O devices, processors\n- **User Interface**: Smart objects, signal processing, SIMPL bridging\n- **System Services**: Monitoring, diagnostics, device management\n\nThis analysis shows that Essentials serves as a comprehensive middleware layer, abstracting Crestron hardware complexities while providing modern software development patterns and practices.\n\n---\n*Generated: [Current Date]*\n*Framework Version: PepperDash Essentials (Based on codebase analysis)*\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) <2020> PepperDash Technology Corporation\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "PepperDash.Essentials.4Series.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.4.33213.308\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PepperDash.Essentials.Devices.Common\", \"src\\PepperDash.Essentials.Devices.Common\\PepperDash.Essentials.Devices.Common.csproj\", \"{53E204B7-97DD-441D-A96C-721DF014DF82}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PepperDash.Essentials\", \"src\\PepperDash.Essentials\\PepperDash.Essentials.csproj\", \"{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PepperDash.Essentials.Core\", \"src\\PepperDash.Essentials.Core\\PepperDash.Essentials.Core.csproj\", \"{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Mobile Control\", \"Mobile Control\", \"{B24989D7-32B5-48D5-9AE1-5F3B17D25206}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"PepperDash.Essentials.MobileControl\", \"src\\PepperDash.Essentials.MobileControl\\PepperDash.Essentials.MobileControl.csproj\", \"{F6D362DE-2256-44B1-927A-8CE4705D839A}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"PepperDash.Essentials.MobileControl.Messengers\", \"src\\PepperDash.Essentials.MobileControl.Messengers\\PepperDash.Essentials.MobileControl.Messengers.csproj\", \"{B438694F-8FF7-464A-9EC8-10427374471F}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Essentials\", \"Essentials\", \"{AD98B742-8D85-481C-A69D-D8D8ABED39EA}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Core\", \"Core\", \"{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"PepperDash.Core\", \"src\\PepperDash.Core\\PepperDash.Core.csproj\", \"{E5336563-1194-501E-BC4A-79AD9283EF90}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug 4.7.2|Any CPU = Debug 4.7.2|Any CPU\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug 4.7.2|Any CPU.ActiveCfg = Debug 4.7.2|Any CPU\n\t\t{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug 4.7.2|Any CPU.Build.0 = Debug 4.7.2|Any CPU\n\t\t{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{53E204B7-97DD-441D-A96C-721DF014DF82}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{53E204B7-97DD-441D-A96C-721DF014DF82}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug 4.7.2|Any CPU.ActiveCfg = Debug 4.7.2|Any CPU\n\t\t{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug 4.7.2|Any CPU.Build.0 = Debug 4.7.2|Any CPU\n\t\t{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug 4.7.2|Any CPU.ActiveCfg = Debug 4.7.2|Any CPU\n\t\t{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug 4.7.2|Any CPU.Build.0 = Debug 4.7.2|Any CPU\n\t\t{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B438694F-8FF7-464A-9EC8-10427374471F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B438694F-8FF7-464A-9EC8-10427374471F}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B438694F-8FF7-464A-9EC8-10427374471F}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{B438694F-8FF7-464A-9EC8-10427374471F}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{53E204B7-97DD-441D-A96C-721DF014DF82} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}\n\t\t{CB3B11BA-625C-4D35-B663-FDC5BE9A230E} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}\n\t\t{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}\n\t\t{F6D362DE-2256-44B1-927A-8CE4705D839A} = {B24989D7-32B5-48D5-9AE1-5F3B17D25206}\n\t\t{B438694F-8FF7-464A-9EC8-10427374471F} = {B24989D7-32B5-48D5-9AE1-5F3B17D25206}\n\t\t{E5336563-1194-501E-BC4A-79AD9283EF90} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {6907A4BF-7201-47CF-AAB1-3597F3B8E1C3}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "README.md",
    "content": "# PepperDash Essentials Framework (c) 2020\n\n## [Latest Release](https://github.com/PepperDash/Essentials/releases/latest)\n\n## License\n\nProvided under MIT license\n\n## Overview\n\nPepperDash Essentials is an open source Crestron framework that can be configured as a standalone program capable of running a wide variety of system designs and can also be utilized as a plug-in architecture to augment other Simpl# Pro and Simpl Windows programs.\n\nEssentials Framework is a collection of C# / Simpl# Pro libraries that can be utilized in several different manners. It is currently operating as a 100% configuration-driven system, and can be extended to add different workflows and behaviors, either through the addition of further device \"types\" or via the plug-in mechanism. The framework is a collection of \"things\" that are all related and interconnected, but in general do not have dependencies on each other.\n\n## Minimum Requirements\n\n- Essentials Framework runs on any Crestron 3-series processor, **4-series** processor or Crestron's VC-4 platform.\n- To edit and compile the source, Microsoft Visual Studio 2008 Professional with SP1 is required.\n- Crestron's Simpl# Plugin is also required (must be obtained from Crestron).\n\n## Dependencies\n\nThe [PepperDash.Core](https://github.com/PepperDash/PepperDashCore) SIMPL# library is required. It is referenced via nuget. You must have nuget.exe installed and in the `PATH` environment variable to use the following command. Nuget.exe is available at [nuget.org](https://dist.nuget.org/win-x86-commandline/latest/nuget.exe).\n\n### Installing Dependencies\n\nTo install dependencies once nuget.exe is installed, run the following command:\n`nuget install .\\packages.config -OutputDirectory .\\packages -excludeVersion`.\nTo verify that the packages installed correctly, open Essentials and make sure that all references are found, then try and build it.\n\n### Installing Different versions of PepperDash Core\n\nIf you need a different version of PepperDash Core, use the command `nuget install .\\packages.config -OutputDirectory .\\packages -excludeVersion -Version {versionToGet}`. Omitting the `-Version` option will pull the version indicated in the packages.config file.\n\n## Utilization\n\nEssentials was originally conceptualized as a standalone application for running control system logic entirely in Simpl# Pro. It is primarily designed around accomplishing this goal, but during development, it became obvious that it could easily be leveraged to also serve as a partner application to one or more SIMPL Windows programs.\n\nUtilization of Essentials Framework falls into the following categories:\n\n1. Standalone Control System Application for controlling one or more rooms. See [Standalone Use](https://github.com/PepperDash/Essentials/wiki/Standalone-Use#standalone-application)\n\n2. Partner Application to a SIMPL Windows program. This allows for several useful advantages. See [SIMPL Windows Bridging](https://github.com/PepperDash/Essentials/wiki/SIMPL-Bridging#simpl-windows-bridging)\n\n- Dynamic device instantiation. Devices can be defined in configuration and instantiated at runtime and then bridged to a SIMPL Windows program via EISC.\n\n- Advanced logic. Some logic operations that cannot be affectively accomplished in SIMPL Windows (ex. JSON/XML serialization/deserialization, database operations, etc.) can be done in the Simpl# Pro environment and the necessary input and output bridged to a SIMPL Windows program via EISC.\n\n3. Hybrid Application that may contain elements of both standalone control and SIMPL partner application integration.\n\n- There may be a use case where a device can only be defined in a single application, but that device may need to be interacted with from multiple applications. The device can be defined in an Essentials application, interacted with in that application and also bridged to one or more SIMPL Windows applications.\n\n## Documentation\n\nFor detailed documentation, see the [Wiki](https://github.com/PepperDash/EssentialsFramework/wiki).\n\n## Support\n\n* Check out our [Discord Server](https://discord.gg/rWyeRH3K)\n\n## How-To (Getting Started)\n\nSee [Getting Started](https://github.com/PepperDash/Essentials/wiki/Get-started#how-to-get-started)"
  },
  {
    "path": "Sample Presets/SampleDirecTV.json",
    "content": "{\n  \"name\": \"Sample DirecTV List\",\n  \"channels\": [\n    {\n      \"name\": \"HBO\",\n      \"IconUrl\": \"HBO\",\n      \"Channel\": \"501\"\n    },\n    {\n      \"name\": \"HBO\",\n      \"IconUrl\": \"HBO\",\n      \"Channel\": \"501\"\n    },\n    {\n      \"name\": \"HBO\",\n      \"IconUrl\": \"HBO\",\n      \"Channel\": \"501\"\n    },\n    {\n      \"name\": \"HBO\",\n      \"IconUrl\": \"HBO\",\n      \"Channel\": \"501\"\n    },\n    {\n      \"name\": \"HBO\",\n      \"IconUrl\": \"HBO\",\n      \"Channel\": \"501\"\n    },\n    {\n      \"name\": \"HBO\",\n      \"IconUrl\": \"HBO\",\n      \"Channel\": \"501\"\n    }\n  ]\n}"
  },
  {
    "path": "docs/docfx.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json\",\n  \"metadata\": [\n    {\n      \"src\": [\n        {\n          \"src\": \"../\",\n          \"files\": [\n            \"src/**/*.csproj\"\n          ]\n        }\n      ],\n      \"properties\": {\n        \"TargetFramework\": \"net472\"\n      },\n      \"dest\": \"api\",\n      \"namespaceLayout\": \"nested\",\n      \"outputFormat\": \"apiPage\"\n    }\n  ],\n  \"build\": {\n    \"content\": [\n      {\n        \"files\": [\n          \"docs/**/*.{md,yml}\",\n          \"api/**/*.{md,yml}\",\n          \"index.md\",\n          \"toc.yml\"\n        ],\n        \"exclude\": [\n          \"_site/**\",\n          \".github/**\"\n        ]\n      }\n    ],\n    \"resource\": [\n      {\n        \"files\": [\n          \"docs/images/**\"\n        ]\n      }\n    ],\n    \"output\": \"_site\",\n    \"template\": [\n      \"default\",\n      \"modern\"\n    ],\n    \"globalMetadata\": {\n      \"_appName\": \"PepperDash Essentials\",\n      \"_appTitle\": \"PepperDash Essentials\",\n      \"_enableSearch\": true,\n      \"_appLogoPath\": \"docs/images/favicon-32x32.png\",\n      \"_appFaviconPath\": \"docs/images/favicon.ico\",\n      \"_disableToc\": false,\n      \"_enableNewTab\": true,\n      \"pdf\": false\n    }\n  }\n}"
  },
  {
    "path": "docs/docs/CardFrame.md",
    "content": "        {\n                \"key\": \"cardCage1\",\n                \"uid\": 1,\n                \"name\": \"Internal Card Cage\",\n                \"type\": \"internalcardcage\",\n                \"group\": \"cardCage\",\n                \"properties\": {\n                  \"cards\": {\n                    \"1\": \"c3com3\",\n                    \"2\": \"c3com3\",\n                    \"3\": \"\"\n                  }\n                }\n            },"
  },
  {
    "path": "docs/docs/Get-started.md",
    "content": "# Get started\n\n---\n[YouTube Video - Getting Started with PepperDash Essentials](https://youtu.be/FxEZtbpCwiQ)\n***\n\n## Get a CPZ\n\n### Prerequisites\n\n* [VS Code](https://code.visualstudio.com/)\n* [.NET 9 SDK](https://dotnet.microsoft.com/en-us/download)\n* [Git](https://git-scm.com/)\n\n> Note: Essentials 2.x.x uses .NET Framework 4.7.2 currently. The .NET 9 SDK will build the project with the appropriate references\n\n### Build From Source\n\n1. Clone the repo: `git clone https://github.com/PepperDash/Essentials.git`\n2. Open the folder in VS Code\n3. Build using the dotnet CLI: `dotnet build`\n\n### Download the latest release\n\nThe latest release can be found on [Github](https://github.com/PepperDash/Essentials/releases/latest)\n\n## How to Get Started\n\n2. Using an SFTP client or Crestron Toolbox, load the downloaded (or built) cpz to the processor in program slot 1\n   1. If using SFTP, connect via SSH and start the program by sending console command `progload -p:1`\n3. On first boot, the Essentials Application will build the necessary configuration folder structure in the user/program1/ path.\n4. The application has some example configuration files included. Copy `/Program01/Example Configuration/EssentialsSpaceHuddleRoom/configurationFile-HuddleSpace-2-Source.json` to the `/User/Program1/` folder.\n6. Reset the program via console `progreset -p:1`. The program will load the example configuration file.\n\nOnce Essentials is running with a valid configuration, the following console commands can be used to see what's going on:\n\n* ```devlist:1```\n  * Print the list of devices in [{key}] {name} format\n  * The key of a device can be used with the rest of the commands to get more information\n* `devprops:1 {deviceKey}`\n  * Print the real-time property values of the device with key \"display-1\".\n* `devmethods:1 display-1`\n  * Print the public methods available for the device with key \"display-1\".\n* `devjson:1 {\"deviceKey\":\"display-1\",\"methodName\":\"PowerOn\", \"params\": []}`\n  * Call the method `PowerOn()` on the device with key \"display-1\".\n\nNext: [Standalone use](~/docs/usage/Standalone-Use.md)\n"
  },
  {
    "path": "docs/docs/Home.md",
    "content": "# Welcome to PepperDash Essentials!\n\nPepperDash Essentials is an open-source framework for control systems, built on Crestron's Simpl# Pro framework. It can be configured as a standalone program capable of running a wide variety of system designs and can also be used to augment other Crestron programs.\n\nEssentials is a collection of C# libraries that can be used in many ways. It is a 100% configuration-driven framework that can be extended to add different workflows and behaviors, either through the addition of new device-types and classes, or via a plug-in mechanism. The framework is a collection of things that are all related and interconnected, but in general do not have strong dependencies on each other.\n\n---\n\n## Get started\n\n- [Download essentials build or clone repo](~/docs/Get-started.md)\n- [How to get started](~/docs/Get-started.md)\n- [YouTube Video Series Playlist](https://youtube.com/playlist?list=PLKOoNNwgPFZdV5wDEBDZxTHu1KROspaBu)\n- [Discord Server](https://discord.gg/6Vh3ssDdPs)\n\nOr use the links to the right to navigate our documentation.\n\n---\n\n## Benefits\n\n- Runs on Crestron 3-Series, **4-Series** and VC-4 Control System platforms\n- Reduced hardware overhead compared to S+ and Simpl solutions\n- Quick development cycle\n- Shared resources made easily available\n- More flexibility with less code\n- Configurable using simple JSON files\n- Is awesome\n\n---\n\n## Comment\n\nThe Essentials wiki is clearly in-progress right now. Take a look at the links to the right. We are actively working on this documentation, so please be patient with us. If you have any comments on or suggestions for the documentation, please file an issue here, with as much detail as you can provide: <https://github.com/PepperDash/Essentials/issues>\n\nThanks!\n\n---\n\n## Collaboration\n\nEssentials is an open-source project and we encourage collaboration on this community project. For features that may not be useful to the greater community, or for just-plain learning, we want to remind developers to try writing plugins for Essentials. More information can be found here: [Plugins](~/docs/technical-docs/Plugins.md)\n\n### Open-source-collaborative workflow\n\nThe `main` branch always contain the latest stable version. The `development` branch is used for most development efforts.\n\n[GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) will be used as the workflow for this collaborative project. To contribute, follow this process:\n\n1. Fork this repository ([More Info](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks))\n2. Create a branch using standard GitFlow branch prefixes (feature/hotfix) followed by a descriptive name.\n   - Example: `feature/add-awesomeness` or `hotfix/really-big-oops`\n   - When working on a new feature or bugfix, branch from the `development` branch. When working on a hotfix, branch from `main`.\n3. Make commits as necessary (often is better). And use concise, descriptive language, leveraging issue notation and/or [Closing Keywords](https://help.github.com/articles/closing-issues-using-keywords) to ensure any issues addressed by your work are referenced accordingly.\n4. When the scope of the work for your branch is complete, make sure to rebase your branch in case further progress has been made since the repo was forked\n5. Create a Pull Request to pull your branch into the appropriate branch in the main repository.\n6. Your Pull Request will be reviewed by our team and evaluated for inclusion into the main repository.\n\nNext: [Get started](~/docs/Get-started.md)\n"
  },
  {
    "path": "docs/docs/Plugins-Deprecated.md",
    "content": "# Deprecated\n\n**Note : this entry is out of date - please see [Plugins](~/docs/technical-docs/Plugins.md)**\n\n## What are Essentials Plugins?\n\nPlugins are SIMPL# Pro libraries that reference the Essentials Framework and can be loaded into an Essentials Application at runtime to extend functionality beyond what the Essentials Framework provides on its own.\n\n## Why Use Plugins?\n\nPlugins are a way to extend or add new functionality to the Essentials Application without having to modify the actual Framework. In most cases, a plugin can be written to support a new device or behavior. Using plugins also limits the scope of understanding needed to work within the Essentials Framework.\n\n## Should I use a Plugin?\n\nEssentials is meant to be a lightweight framework and an extensible basis for development. While some devices are included in the framework, mostly for the purposes of providing examples and developing and prototyping new device types, the bulk of new development is intended to take place in Plugins. Once a plugin adds new functionality that may be of benefit if shared across multiple plugins, it may make sense to port that common logic (base classes and/or interfaces) back into the framework to make it available to others. The thrust of future Essentials development is targeted towards building a library of plugins.\n\n## How do Plugins Work?\n\nOne or more plugins can be loaded to the /user/ProgramX/plugins as .dlls or .cplz packages. When the Essentials Application starts, it looks for any .cplz files, unzips them and then iterates any .dll assemblies in that folder and loads them. Once the plugin assemblies are loaded the Essentials Application will then attempt to load a configuration file and construct items as defined in the file. Those items can be defined in either the Essentials Framework or in any of the loaded plugin assemblies.\n\n![Architecture drawing](~/docs/images/Plugin%20Load%20Sequence.png)\n\n## What Must be Implemented in a Plugin for it to Work?\n\nAll plugin assemblies must contain a static method called LoadPlugin():\n\n```cs\npublic class SomeDevice : Device , IBridge  //IBridge only needs to be implemented if using a bridge\n{\n    // This string is used to define the minimum version of the\n    // Essentials Framework required for this plugin\n    public static string MinimumEssentialsFrameworkVersion = \"1.4.23\";\n\n    // This method gets called by the Essentials Framework when the application starts.\n    // It is intended to be used to load the new Device type(s) specified in the plugin\n    public static void LoadPlugin()\n    {\n        DeviceFactory.AddFactoryForType(\"typeName\", FactoryMethod);\n        // typeName should be the unique string that identifies the type of device to build,\n        // FactoryMethod represents the method that takes a DevicConfig object as and argument\n        // and returns an instance of the desired device type\n    }\n\n    // This is a factory method to construct a device and return it to be\n    // added to the DeviceManager\n    public static Device FactoryMethod(DeviceConfig dc)\n    {\n        return new SomeDevice(dc.key, dc.name, dc);\n    }\n\n#region IBridge\n    // This method is called by an EiscApi bridge instance and should call an extension method\n    // defined in your plugin.  Required for implementing IBridge\n    public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey)\n    {\n        this.LinkToApiExt(trilist, joinStart, joinMapKey);\n    }\n#endregion\n}\n```\n\n## SIMPL Bridging\n\nOptionally, if your plugin device needs to be able to bridge to a SIMPL program over EISC, and there isn't already an existing bridge class in the Essentials Framework, you can write a new bridge class in your plugin. However, in order for the Essentials Application to be able to us that new bridge, the bridge must implement the IBridge interface with the required LinkToApi() Extension method.\n\nOften though, you may find that a bridge class already exists in the Essentials Framework that you can leverage. For example, if you were writing a plugin to support a new display model that isn't already in the Essentials Framework, you would define a class in your plugin that inherits from PepperDash.Essentials.Core.DisplayBase. If you're only implementing the standard display control functions such as power/input/volume control, then the existing bridge class `DisplayControllerBridge` can be used. If you needed to add additional functions to the bridge, then you would need to write your own bridge in the plugin.\n\nFor additional info see the [SIMPL-Bridging article](~/docs/SIMPL-Bridging.md).\n\n## Template Essentials Plugin Repository\n\nFork this repository when starting a new plugin. The template repository uses the essentials-builds repository as a submodule. This allows the plugin to reference a specific build version of Essentials. You must make sure that you checkout the correct build of the Essentials-Builds repo that contains any dependencies that your plugin may rely on.\n\n[Essentials Plugin Template Repository](https://github.com/PepperDash/EssentialsPluginTemplate)\n"
  },
  {
    "path": "docs/docs/SIMPL-Bridging-Deprecated.md",
    "content": "# Deprecated\n\n**Note : this entry is out of date - please see [SIMPL Windows Bridging](~/docs/SIMPL-Bridging.md)**\n\n## SIMPL Windows Bridging - Deprecated\n\nEssentials allows for devices defined within the SIMPL# Pro application to be bridged to a SIMPL Windows application over Ethernet Intersystem Communication (EISC). This allows a SIMPL Windows program to take advantage of some of the features of the SIMPL# Pro environment, without requiring the entire application to be written in C#.\n\nSome of the main advantages are:\n\n1. The ability to instantiate devices from configuration.\n1. The ability to leverage C# concepts to handle data intensive tasks (Serialization/Deserialization of JSON/XML, cyrptography, etc.).\n1. The ability to reuse the same compiled SIMPL Windows program (regardless of target processor type) by offloading all the variables that may be room or hardware specific to Essentials.\n1. The ability to handle multiple communciation types generically without changing the SIMPL Program (TCP/UDP/SSH/HTTP/HTTPS/CEC, etc.)\n1. Much faster development cycle\n1. Reduced processor overhead\n1. Ability to easily share devices defined in Essentials between multiple other programs\n\n## Implementation\n\nBridges are devices that are defined within the devices array in the config file. They are unique devices with a specialized purpose; to act as a bridge between Essentials Devices and applications programmed traditionally in Simpl Windows. This is accomplished by instantiating a Three Series Intersystem Communication symbol within the bridge device, and linking its Boolean/Ushort/String inputs and outputs to actions on one or multiple Essentials device(s). The definition for which joins map to which actions is defined within the device to be bridged to in a class that derives from JoinMapBase.\n\nLet's consider the following Essentials Configuration:\n\n```JSON\n{\n    \"template\": {\n        \"roomInfo\": [\n            {}\n        ],\n        \"devices\": [\n            {\n                \"key\": \"processor\",\n                \"uid\": 1,\n                \"type\": \"pro3\",\n                \"name\": \"PRO3 w/o cards\",\n                \"group\": \"processor\",\n                \"supportedConfigModes\": [\n                    \"essentials\"\n                ],\n                \"supportedSystemTypes\": [\n                    \"hudType\",\n                    \"presType\",\n                    \"vtcType\",\n                    \"custom\"\n                ],\n                \"supportsCompliance\": true,\n                \"properties\": {\n                    \"numberOfComPorts\": 6,\n                    \"numberOfIrPorts\": 8,\n                    \"numberOfRelays\": 8,\n                    \"numberOfDIOPorts\": 8\n                }\n            },\n            {\n                \"key\": \"panasonicDisplay01\",\n                \"type\": \"PanasonicThefDisplay\",\n                \"name\": \"Main Display\",\n                \"group\": \"displays\",\n                \"uid\": 2,\n                \"properties\": {\n                    \"id\": \"01\",\n                    \"inputNumber\": 1,\n                    \"outputNumber\": 1,\n                    \"control\": {\n                        \"comParams\": {\n                            \"hardwareHandshake\": \"None\",\n                            \"parity\": \"None\",\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 9600,\n                            \"dataBits\": 8,\n                            \"softwareHandshake\": \"None\",\n                            \"stopBits\": 1\n                        },\n                        \"controlPortNumber\": 1,\n                        \"controlPortDevKey\": \"processor\",\n                        \"method\": \"com\"\n                    }\n                }\n            },\n            {\n                \"key\": \"vtcComPort\",\n                \"uid\": 3,\n                \"name\": \"VTC Coms\",\n                \"group\": \"comm\",\n                \"type\": \"genericComm\",\n                \"properties\": {\n                    \"control\": {\n                        \"comParams\": {\n                            \"hardwareHandshake\": \"None\",\n                            \"parity\": \"None\",\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 38400,\n                            \"dataBits\": 8,\n                            \"softwareHandshake\": \"None\",\n                            \"stopBits\": 1\n                        },\n                        \"controlPortNumber\": 2,\n                        \"controlPortDevKey\": \"processor\",\n                        \"method\": \"com\"\n                    }\n                }\n            },\n            {\n                \"key\": \"deviceBridge\",\n                \"uid\": 4,\n                \"name\": \"BridgeToDevices\",\n                \"group\": \"api\",\n                \"type\": \"eiscApi\",\n                \"properties\": {\n                    \"control\": {\n                        \"tcpSshProperties\": {\n                            \"address\": \"127.0.0.2\",\n                            \"port\": 0\n                        },\n                        \"ipid\": \"03\",\n                        \"method\": \"ipidTcp\"\n                    },\n                    \"devices\": [\n                        {\n                            \"deviceKey\": \"panasonicDisplay01\",\n                            \"joinStart\": 1\n                        },\n                        {\n                            \"deviceKey\": \"vtcComPort\",\n                            \"joinStart\": 51\n                        }\n                    ]\n                }\n            }\n        ]\n    }\n}\n```\n\nWe have four Essentials Devices configured:\n\n1. Pro3 with a Key of \"processor\"\n\n1. Panasonic Display with a Key of \"panasonicDisplay01\"\n\n1. Com port with a Key of \"vtcComPort\"\n\n1. Bridge with a Key of \"deviceBridge\"\n\nWe want to have access to the com port for VTC Control from Simpl Windows and we want to control the display from Simpl Windows. To accomplish this, we have created a bridge device and added the devices to be bridged to the \"devices\" array on the bridge. As you can see we define the device key and the join start, which will determine which joins we will use on the resulting EISC to interact with the devices. In the Bridge control properties we defined ipid 03, and we will need a corresponding Ethernet System Intercommunication in the Simpl Windows program at ipid 03.\n\nNow that our devices have been built, we can refer to the device join maps to see which joins correspond to which actions.\n\nSee below:\n\n```cs\nnamespace PepperDash.Essentials.Bridges\n{\n    public class DisplayControllerJoinMap : JoinMapBase\n    {\n        #region Digitals\n        /// <summary>\n        /// Turns the display off and reports power off feedback\n        /// </summary>\n        public uint PowerOff { get; set; }\n        /// <summary>\n        /// Turns the display on and repots power on feedback\n        /// </summary>\n        public uint PowerOn { get; set; }\n        /// <summary>\n        /// Indicates that the display device supports two way communication when high\n        /// </summary>\n        public uint IsTwoWayDisplay { get; set; }\n        /// <summary>\n        /// Increments the volume while high\n        /// </summary>\n        public uint VolumeUp { get; set; }\n        /// <summary>\n        /// Decrements teh volume while high\n        /// </summary>\n        public uint VolumeDown { get; set; }\n        /// <summary>\n        /// Toggles the mute state.  Feedback is high when volume is muted\n        /// </summary>\n        public uint VolumeMute { get; set; }\n        /// <summary>\n        /// Range of digital joins to select inputs and report current input as feedback\n        /// </summary>\n        public uint InputSelectOffset { get; set; }\n        /// <summary>\n        /// Range of digital joins to report visibility for input buttons\n        /// </summary>\n        public uint ButtonVisibilityOffset { get; set; }\n        /// <summary>\n        /// High if the device is online\n        /// </summary>\n        public uint IsOnline { get; set; }\n        #endregion\n\n        #region Analogs\n        /// <summary>\n        /// Analog join to set the input and report current input as feedback\n        /// </summary>\n        public uint InputSelect { get; set; }\n        /// <summary>\n        /// Sets the volume level and reports the current level as feedback\n        /// </summary>\n        public uint VolumeLevel { get; set; }\n        #endregion\n\n        #region Serials\n        /// <summary>\n        /// Reports the name of the display as defined in config as feedback\n        /// </summary>\n        public uint Name { get; set; }\n        /// <summary>\n        /// Range of serial joins that reports the names of the inputs as feedback\n        /// </summary>\n        public uint InputNamesOffset { get; set; }\n        #endregion\n\n        public DisplayControllerJoinMap()\n        {\n            // Digital\n            IsOnline = 50;\n            PowerOff = 1;\n            PowerOn = 2;\n            IsTwoWayDisplay = 3;\n            VolumeUp = 5;\n            VolumeDown = 6;\n            VolumeMute = 7;\n\n            ButtonVisibilityOffset = 40;\n            InputSelectOffset = 10;\n\n            // Analog\n            InputSelect = 11;\n            VolumeLevel = 5;\n\n            // Serial\n            Name = 1;\n            InputNamesOffset = 10;\n        }\n\n        public override void OffsetJoinNumbers(uint joinStart)\n        {\n            var joinOffset = joinStart - 1;\n\n            IsOnline = IsOnline + joinOffset;\n            PowerOff = PowerOff + joinOffset;\n            PowerOn = PowerOn + joinOffset;\n            IsTwoWayDisplay = IsTwoWayDisplay + joinOffset;\n            ButtonVisibilityOffset = ButtonVisibilityOffset + joinOffset;\n            Name = Name + joinOffset;\n            InputNamesOffset = InputNamesOffset + joinOffset;\n            InputSelectOffset = InputSelectOffset + joinOffset;\n\n            InputSelect = InputSelect + joinOffset;\n\n            VolumeUp = VolumeUp + joinOffset;\n            VolumeDown = VolumeDown + joinOffset;\n            VolumeMute = VolumeMute + joinOffset;\n            VolumeLevel = VolumeLevel + joinOffset;\n        }\n    }\n}\n```\n\nWe know that the Panasonic Display uses the DisplayControllerJoinMap class and can see the join numbers that will give us access to functionality in the Device.\n\nIsOnline = 50  \nPowerOff = 1  \nPowerOn = 2  \nIsTwoWayDisplay = 3  \nVolumeUp = 5  \nVolumeDown = 6  \nVolumeMute = 7\n\n```cs\nnamespace PepperDash.Essentials.Bridges\n{\n    public class IBasicCommunicationJoinMap : JoinMapBase\n    {\n        #region Digitals\n        /// <summary>\n        /// Set High to connect, Low to disconnect\n        /// </summary>\n        public uint Connect { get; set; }\n        /// <summary>\n        /// Reports Connected State (High = Connected)\n        /// </summary>\n        public uint Connected { get; set; }\n        #endregion\n\n        #region Analogs\n        /// <summary>\n        /// Reports the connections status value\n        /// </summary>\n        public uint Status { get; set; }\n        #endregion\n\n        #region Serials\n        /// <summary>\n        /// Data back from port\n        /// </summary>\n        public uint TextReceived { get; set; }\n        /// <summary>\n        /// Sends data to the port\n        /// </summary>\n        public uint SendText { get; set; }\n        /// <summary>\n        /// Takes a JSON serialized string that sets a COM port's parameters\n        /// </summary>\n        public uint SetPortConfig { get; set; }\n        #endregion\n\n        public IBasicCommunicationJoinMap()\n        {\n            TextReceived = 1;\n            SendText = 1;\n            SetPortConfig = 2;\n            Connect = 1;\n            Connected = 1;\n            Status = 1;\n        }\n\n        public override void OffsetJoinNumbers(uint joinStart)\n        {\n            var joinOffset = joinStart - 1;\n\n            TextReceived = TextReceived + joinOffset;\n            SendText = SendText + joinOffset;\n            SetPortConfig = SetPortConfig + joinOffset;\n            Connect = Connect + joinOffset;\n            Connected = Connected + joinOffset;\n            Status = Status + joinOffset;\n        }\n    }\n}\n```\n\nTextReceived = 1  \nSendText = 1  \nSetPortConfig = 2  \nConnect = 1  \nConnected = 1  \nStatus = 1\n\nConsidering our Bridge config, we can see that the display controls will start at join 1, and the VTC Com port will start at join 51. The result is a single EISC that allows us to interact with our Essentials devices.\n\nTo control diplay power from Simpl Windows, we would connect Digital Signals to joins 1 & 2 on the EISC to control Display Power On & Off.\nTo utilize the com port device, we would connect Serial Signals (VTC_TX$ and VTC_RX$) to join 51 on the EISC.\n\nYou can refer to our [Simpl Windows Bridging Example](https://github.com/PepperDash/EssentialsSIMPLWindowsBridgeExample) for a more complex example.  \nExample device config: <https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Example%20Configuration/SIMPLBridging/SIMPLBridgeExample_configurationFile.json>\n\n## Notes\n\n1. It is important to realize that there are no safety checks (yet) when assigning joinStarts in bridge configurations. If you were to put two devices on a bridge with overlapping joins, the most recently bridged join would overwrite previously bridged joins. For now it is on the programmer to ensure there are no conflicting join maps.\n\n1. There is _no_ limit to the amount of times a device may be bridged to. You may have the same device on multiple bridges across multiple applications without problem. That being said, we recommend using common sense. Accessing a single com port for VTC control via multiple bridges may not be wise...\n\n1. A bridge need not only bridge between applications on the same processor. A bridge may bridge to an application on a completely separate processor; simply define the ip address in the Bridge control properties accordingly.\n\n1. For devices included in Essentials, you will be able to find defined join maps below. If you are building your own plugins, you will need to build the join map yourself. It would be beneficial to review the wiki entry on the [Feedback Class](~/docs/technical-docs/Feedback-Classes.md) for this.\n\n1. When building plugins, we highly recommend reusing JoinMaps, as this will make code more easily interchangeable. For example; if you were to build a display plugin, we'd recommend you use/extend the existing DisplayControllerJoinMap. This way, you can swap plugins without needing any change on the Simpl Windows side. This is extremely powerful when maintaining Simpl Windows code bases for large deployments that may utilize differing equipment per room. If you can build a Simpl Windows program that interacts with established join maps, you can swap out the device via config without any change needed to Simpl Windows.\n\n1. Related to item 5, you can use the same paradigm with respect to physical device communication. If you were to have a DSP device in some rooms communicating over RS232 and some via SSH, it would be trival to swap the device from a Com port to an SSH client in the Essentials Devicee Config and update the Bridge Config to brigde to the desired communication method. Again this would require no change on the Simpl Windows side as long as you maintain the same join Start in the Bridge Device Configuration.\n\n## Common Use Cases\n\n1. There are 10 conference rooms that all operate the same, but have hardware differences that are impossible to account for in SIMPL Windows. For example, each room might have a DM-MD8X8 chassis, but the input and output cards aren't all in the same order, or they might be different models but function the same. You can use Essentials with a unique configuration file for each hardware configuration.\n\n1. You have a floor of conference rooms that all share some centralized hardware like DSP, AV Routing and a shared CEN-GWEXER gateway with multiple GLS-OIR-CSM-EX-BATT occupancy sensors. All the shared hardware can be defined in the Essentials configuration and bridged over an EISC to each program that needs access. The same device can even be exposed to multiple programs over different EISCs.\n\n1. You have a SIMPL program that works for many room types, but because some rooms have different models of processors than others (CP3/CP3N/AV3/PRO3/DMPS3 variants), you have to maintain several versions of the program, compiled for each processor model to maintain access to features like the System Monitor slot. You can use Essentials running in a slot on a processor to expose the System Monitor and many other features of the processor, regardless of model. Now you only need to maintain a single SIMPL program defined for your most complex processor application (ex. PRO3)\n\n## Device Type Join Maps\n\n### AirMediaController\n\n> supports: AM-200, AM-300\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/AirMediaControllerJoinMap.cs>\n\n### AppleTvController\n\n> supports: IR control of Apple TV\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/AppleTvJoinMap.cs>\n\n### CameraControlBase\n\n> supports: any camera that derives from CameraBase\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/CameraControllerJoinMap.cs>\n\n### DisplayController\n\n> supports: IR controlled displays, any two way display driver that derives from PepperDash.Essentials.Core.DisplayBase\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DisplayControllerJoinMap.cs>\n\n### DmChasisController\n\n> supports: All DM-MD-8x8/16x16/32x32 chassis, with or w/o DM-CPU3 Card\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs>\n\n### DmRmcController\n\n> supports: All DM-RMC devices\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmRmcControllerJoinMap.cs>\n\n### DmTxController\n\n> supports: All Dm-Tx devices\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmTxControllerJoinMap.cs>\n\n### DmpsAudioOutputController\n\n> supports: Program, Aux1, Aux2 outputs of all DMPS3 Control Systems\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs>\n\n### DmpsRoutingController\n\n> supports: Av routing for all DMPS3 Control Systems\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs>\n\n### GenericRelayController\n\n> supports: Any relay port on a Crestron Control System or Dm Endpoint\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/GenericRelayControllerJoinMap.cs>\n\n### GenericLightingJoinMap\n\n> supports: Devices derived from PepperDash.Essentials.Core.Lighting.LightingBase\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/GenericLightingJoinMap.cs>\n\n### GlsOccupancySensorBase\n\n> supports: Any Crestron GLS-Type Occupancy sensor - single/dual type\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/GlsOccupancySensorBaseJoinMap.cs>\n\n### HdMdxxxCEController\n\n> supports: HD-MD-400-C-E, HD-MD-300-C-E, HD-MD-200-C-E, HD-MD-200-C-1G-E-B/W\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/HdMdxxxCEControllerJoinMap.cs>\n\n### IBasicCommunication\n\n> supports: Any COM Port on a Control System or Dm Endpoint device, TCP Client, SSH Client, or UDP Server\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/IBasicCommunicationJoinMap.cs>\n\n### IDigitalInput\n\n> supports: Any Digital Input on a Control System, or DM Endpoint device\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/IDigitalInputJoinMap.cs>\n\n### SystemMonitorController\n\n> supports: Exposing the system monitor slot for any Control System\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/SystemMonitorJoinMap.cs>\n\n## Example SIMPL Windows Program\n\nWe've provided an [example program](https://github.com/PepperDash/EssentialsSIMPLWindowsBridgeExample) for SIMPL Windows that works with the provided example Essentials configuration file [SIMPLBridgeExample_configurationFile.json](https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Example%20Configuration/SIMPLBridging/SIMPLBridgeExample_configurationFile.json). Load Essentials and the example SIMPL program to two slots on the same processor and you can get a better idea of how to take advantage of SIMPL Windows bridging.\n\nNext: [Essentials architecture](~/docs/technical-docs/Arch-summary.md)\n"
  },
  {
    "path": "docs/docs/SIMPL-Bridging.md",
    "content": "# SIMPL Windows Bridging\n\n**Note : this entry is out of date - please see [SIMPL Windows Bridging - Updated](~/docs/usage/SIMPL-Bridging-Updated.md)**\n\nEssentials allows for devices defined within the SIMPL# Pro application to be bridged to a SIMPL Windows application over Ethernet Intersystem Communication (EISC). This allows a SIMPL Windows program to take advantage of some of the features of the SIMPL# Pro environment, without requiring the entire application to be written in C#.\n\nSome of the main advantages are:\n\n1. The ability to instantiate devices from configuration.\n1. The ability to leverage C# concepts to handle data intensive tasks (Serialization/Deserialization of JSON/XML, cyrptography, etc.).\n1. The ability to reuse the same compiled SIMPL Windows program (regardless of target processor type) by offloading all the variables that may be room or hardware specific to Essentials.\n1. The ability to handle multiple communciation types generically without changing the SIMPL Program (TCP/UDP/SSH/HTTP/HTTPS/CEC, etc.)\n1. Much faster development cycle\n1. Reduced processor overhead\n1. Ability to easily share devices defined in Essentials between multiple other programs\n\n## Implementation\n\nBridges are devices that are defined within the devices array in the config file. They are unique devices with a specialized purpose; to act as a bridge between Essentials Devices and applications programmed traditionally in Simpl Windows. This is accomplished by instantiating a Three Series Intersystem Communication symbol within the bridge device, and linking its Boolean/Ushort/String inputs and outputs to actions on one or multiple Essentials device(s). The definition for which joins map to which actions is defined within the device to be bridged to in a class that derives from JoinMapBase.\n\nLet's consider the following Essentials Configuration:\n\n```JSON\n{\n    \"template\": {\n        \"roomInfo\": [\n            {}\n        ],\n        \"devices\": [\n            {\n                \"key\": \"processor\",\n                \"uid\": 1,\n                \"type\": \"pro3\",\n                \"name\": \"PRO3 w/o cards\",\n                \"group\": \"processor\",\n                \"supportedConfigModes\": [\n                    \"essentials\"\n                ],\n                \"supportedSystemTypes\": [\n                    \"hudType\",\n                    \"presType\",\n                    \"vtcType\",\n                    \"custom\"\n                ],\n                \"supportsCompliance\": true,\n                \"properties\": {\n                    \"numberOfComPorts\": 6,\n                    \"numberOfIrPorts\": 8,\n                    \"numberOfRelays\": 8,\n                    \"numberOfDIOPorts\": 8\n                }\n            },\n            {\n                \"key\": \"panasonicDisplay01\",\n                \"type\": \"PanasonicThefDisplay\",\n                \"name\": \"Main Display\",\n                \"group\": \"displays\",\n                \"uid\": 2,\n                \"properties\": {\n                    \"id\": \"01\",\n                    \"inputNumber\": 1,\n                    \"outputNumber\": 1,\n                    \"control\": {\n                        \"comParams\": {\n                            \"hardwareHandshake\": \"None\",\n                            \"parity\": \"None\",\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 9600,\n                            \"dataBits\": 8,\n                            \"softwareHandshake\": \"None\",\n                            \"stopBits\": 1\n                        },\n                        \"controlPortNumber\": 1,\n                        \"controlPortDevKey\": \"processor\",\n                        \"method\": \"com\"\n                    }\n                }\n            },\n            {\n                \"key\": \"vtcComPort\",\n                \"uid\": 3,\n                \"name\": \"VTC Coms\",\n                \"group\": \"comm\",\n                \"type\": \"genericComm\",\n                \"properties\": {\n                    \"control\": {\n                        \"comParams\": {\n                            \"hardwareHandshake\": \"None\",\n                            \"parity\": \"None\",\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 38400,\n                            \"dataBits\": 8,\n                            \"softwareHandshake\": \"None\",\n                            \"stopBits\": 1\n                        },\n                        \"controlPortNumber\": 2,\n                        \"controlPortDevKey\": \"processor\",\n                        \"method\": \"com\"\n                    }\n                }\n            },\n            {\n                \"key\": \"deviceBridge\",\n                \"uid\": 4,\n                \"name\": \"BridgeToDevices\",\n                \"group\": \"api\",\n                \"type\": \"eiscApi\",\n                \"properties\": {\n                    \"control\": {\n                        \"tcpSshProperties\": {\n                            \"address\": \"127.0.0.2\",\n                            \"port\": 0\n                        },\n                        \"ipid\": \"03\",\n                        \"method\": \"ipidTcp\"\n                    },\n                    \"devices\": [\n                        {\n                            \"deviceKey\": \"panasonicDisplay01\",\n                            \"joinStart\": 1\n                        },\n                        {\n                            \"deviceKey\": \"vtcComPort\",\n                            \"joinStart\": 51\n                        }\n                    ]\n                }\n            }\n        ]\n    }\n}\n```\n\nWe have four Essentials Devices configured:\n\n1. Pro3 with a Key of \"processor\"\n\n1. Panasonic Display with a Key of \"panasonicDisplay01\"\n\n1. Com port with a Key of \"vtcComPort\"\n\n1. Bridge with a Key of \"deviceBridge\"\n\nWe want to have access to the com port for VTC Control from Simpl Windows and we want to control the display from Simpl Windows. To accomplish this, we have created a bridge device and added the devices to be bridged to the \"devices\" array on the bridge. As you can see we define the device key and the join start, which will determine which joins we will use on the resulting EISC to interact with the devices. In the Bridge control properties we defined ipid 03, and we will need a corresponding Ethernet System Intercommunication in the Simpl Windows program at ipid 03.\n\nNow that our devices have been built, we can refer to the device join maps to see which joins correspond to which actions.\n\nSee below:\n\n```cs\nnamespace PepperDash.Essentials.Bridges\n{\n    public class DisplayControllerJoinMap : JoinMapBase\n    {\n        #region Digitals\n        /// <summary>\n        /// Turns the display off and reports power off feedback\n        /// </summary>\n        public uint PowerOff { get; set; }\n        /// <summary>\n        /// Turns the display on and repots power on feedback\n        /// </summary>\n        public uint PowerOn { get; set; }\n        /// <summary>\n        /// Indicates that the display device supports two way communication when high\n        /// </summary>\n        public uint IsTwoWayDisplay { get; set; }\n        /// <summary>\n        /// Increments the volume while high\n        /// </summary>\n        public uint VolumeUp { get; set; }\n        /// <summary>\n        /// Decrements teh volume while high\n        /// </summary>\n        public uint VolumeDown { get; set; }\n        /// <summary>\n        /// Toggles the mute state.  Feedback is high when volume is muted\n        /// </summary>\n        public uint VolumeMute { get; set; }\n        /// <summary>\n        /// Range of digital joins to select inputs and report current input as feedback\n        /// </summary>\n        public uint InputSelectOffset { get; set; }\n        /// <summary>\n        /// Range of digital joins to report visibility for input buttons\n        /// </summary>\n        public uint ButtonVisibilityOffset { get; set; }\n        /// <summary>\n        /// High if the device is online\n        /// </summary>\n        public uint IsOnline { get; set; }\n        #endregion\n\n        #region Analogs\n        /// <summary>\n        /// Analog join to set the input and report current input as feedback\n        /// </summary>\n        public uint InputSelect { get; set; }\n        /// <summary>\n        /// Sets the volume level and reports the current level as feedback\n        /// </summary>\n        public uint VolumeLevel { get; set; }\n        #endregion\n\n        #region Serials\n        /// <summary>\n        /// Reports the name of the display as defined in config as feedback\n        /// </summary>\n        public uint Name { get; set; }\n        /// <summary>\n        /// Range of serial joins that reports the names of the inputs as feedback\n        /// </summary>\n        public uint InputNamesOffset { get; set; }\n        #endregion\n\n        public DisplayControllerJoinMap()\n        {\n            // Digital\n            IsOnline = 50;\n            PowerOff = 1;\n            PowerOn = 2;\n            IsTwoWayDisplay = 3;\n            VolumeUp = 5;\n            VolumeDown = 6;\n            VolumeMute = 7;\n\n            ButtonVisibilityOffset = 40;\n            InputSelectOffset = 10;\n\n            // Analog\n            InputSelect = 11;\n            VolumeLevel = 5;\n\n            // Serial\n            Name = 1;\n            InputNamesOffset = 10;\n        }\n\n        public override void OffsetJoinNumbers(uint joinStart)\n        {\n            var joinOffset = joinStart - 1;\n\n            IsOnline = IsOnline + joinOffset;\n            PowerOff = PowerOff + joinOffset;\n            PowerOn = PowerOn + joinOffset;\n            IsTwoWayDisplay = IsTwoWayDisplay + joinOffset;\n            ButtonVisibilityOffset = ButtonVisibilityOffset + joinOffset;\n            Name = Name + joinOffset;\n            InputNamesOffset = InputNamesOffset + joinOffset;\n            InputSelectOffset = InputSelectOffset + joinOffset;\n\n            InputSelect = InputSelect + joinOffset;\n\n            VolumeUp = VolumeUp + joinOffset;\n            VolumeDown = VolumeDown + joinOffset;\n            VolumeMute = VolumeMute + joinOffset;\n            VolumeLevel = VolumeLevel + joinOffset;\n        }\n    }\n}\n```\n\nWe know that the Panasonic Display uses the DisplayControllerJoinMap class and can see the join numbers that will give us access to functionality in the Device.\n\nIsOnline = 50  \nPowerOff = 1  \nPowerOn = 2  \nIsTwoWayDisplay = 3  \nVolumeUp = 5  \nVolumeDown = 6  \nVolumeMute = 7\n\n```cs\nnamespace PepperDash.Essentials.Bridges\n{\n    public class IBasicCommunicationJoinMap : JoinMapBase\n    {\n        #region Digitals\n        /// <summary>\n        /// Set High to connect, Low to disconnect\n        /// </summary>\n        public uint Connect { get; set; }\n        /// <summary>\n        /// Reports Connected State (High = Connected)\n        /// </summary>\n        public uint Connected { get; set; }\n        #endregion\n\n        #region Analogs\n        /// <summary>\n        /// Reports the connections status value\n        /// </summary>\n        public uint Status { get; set; }\n        #endregion\n\n        #region Serials\n        /// <summary>\n        /// Data back from port\n        /// </summary>\n        public uint TextReceived { get; set; }\n        /// <summary>\n        /// Sends data to the port\n        /// </summary>\n        public uint SendText { get; set; }\n        /// <summary>\n        /// Takes a JSON serialized string that sets a COM port's parameters\n        /// </summary>\n        public uint SetPortConfig { get; set; }\n        #endregion\n\n        public IBasicCommunicationJoinMap()\n        {\n            TextReceived = 1;\n            SendText = 1;\n            SetPortConfig = 2;\n            Connect = 1;\n            Connected = 1;\n            Status = 1;\n        }\n\n        public override void OffsetJoinNumbers(uint joinStart)\n        {\n            var joinOffset = joinStart - 1;\n\n            TextReceived = TextReceived + joinOffset;\n            SendText = SendText + joinOffset;\n            SetPortConfig = SetPortConfig + joinOffset;\n            Connect = Connect + joinOffset;\n            Connected = Connected + joinOffset;\n            Status = Status + joinOffset;\n        }\n    }\n}\n```\n\nTextReceived = 1  \nSendText = 1  \nSetPortConfig = 2  \nConnect = 1  \nConnected = 1  \nStatus = 1\n\nConsidering our Bridge config, we can see that the display controls will start at join 1, and the VTC Com port will start at join 51. The result is a single EISC that allows us to interact with our Essentials devices.\n\nTo control diplay power from Simpl Windows, we would connect Digital Signals to joins 1 & 2 on the EISC to control Display Power On & Off.\nTo utilize the com port device, we would connect Serial Signals (VTC_TX$ and VTC_RX$) to join 51 on the EISC.\n\nYou can refer to our [Simpl Windows Bridging Example](https://github.com/PepperDash/EssentialsSIMPLWindowsBridgeExample) for a more complex example.  \nExample device config: <https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Example%20Configuration/SIMPLBridging/SIMPLBridgeExample_configurationFile.json>\n\n## Notes\n\n1. It is important to realize that there are no safety checks (yet) when assigning joinStarts in bridge configurations. If you were to put two devices on a bridge with overlapping joins, the most recently bridged join would overwrite previously bridged joins. For now it is on the programmer to ensure there are no conflicting join maps.\n\n1. There is _no_ limit to the amount of times a device may be bridged to. You may have the same device on multiple bridges across multiple applications without problem. That being said, we recommend using common sense. Accessing a single com port for VTC control via multiple bridges may not be wise...\n\n1. A bridge need not only bridge between applications on the same processor. A bridge may bridge to an application on a completely separate processor; simply define the ip address in the Bridge control properties accordingly.\n\n1. For devices included in Essentials, you will be able to find defined join maps below. If you are building your own plugins, you will need to build the join map yourself. It would be beneficial to review the wiki entry on the [Feedback Class](~/docs/technical-docs/Feedback-Classes.md) for this.\n\n1. When building plugins, we highly recommend reusing JoinMaps, as this will make code more easily interchangeable. For example; if you were to build a display plugin, we'd recommend you use/extend the existing DisplayControllerJoinMap. This way, you can swap plugins without needing any change on the Simpl Windows side. This is extremely powerful when maintaining Simpl Windows code bases for large deployments that may utilize differing equipment per room. If you can build a Simpl Windows program that interacts with established join maps, you can swap out the device via config without any change needed to Simpl Windows.\n\n1. Related to item 5, you can use the same paradigm with respect to physical device communication. If you were to have a DSP device in some rooms communicating over RS232 and some via SSH, it would be trival to swap the device from a Com port to an SSH client in the Essentials Devicee Config and update the Bridge Config to brigde to the desired communication method. Again this would require no change on the Simpl Windows side as long as you maintain the same join Start in the Bridge Device Configuration.\n\n## Common Use Cases\n\n1. There are 10 conference rooms that all operate the same, but have hardware differences that are impossible to account for in SIMPL Windows. For example, each room might have a DM-MD8X8 chassis, but the input and output cards aren't all in the same order, or they might be different models but function the same. You can use Essentials with a unique configuration file for each hardware configuration.\n\n1. You have a floor of conference rooms that all share some centralized hardware like DSP, AV Routing and a shared CEN-GWEXER gateway with multiple GLS-OIR-CSM-EX-BATT occupancy sensors. All the shared hardware can be defined in the Essentials configuration and bridged over an EISC to each program that needs access. The same device can even be exposed to multiple programs over different EISCs.\n\n1. You have a SIMPL program that works for many room types, but because some rooms have different models of processors than others (CP3/CP3N/AV3/PRO3/DMPS3 variants), you have to maintain several versions of the program, compiled for each processor model to maintain access to features like the System Monitor slot. You can use Essentials running in a slot on a processor to expose the System Monitor and many other features of the processor, regardless of model. Now you only need to maintain a single SIMPL program defined for your most complex processor application (ex. PRO3)\n\n## Device Type Join Maps\n\n### AirMediaController\n\n> supports: AM-200, AM-300\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/AirMediaControllerJoinMap.cs>\n\n### AppleTvController\n\n> supports: IR control of Apple TV\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/AppleTvJoinMap.cs>\n\n### CameraControlBase\n\n> supports: any camera that derives from CameraBase\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/CameraControllerJoinMap.cs>\n\n### DisplayController\n\n> supports: IR controlled displays, any two way display driver that derives from PepperDash.Essentials.Core.DisplayBase\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DisplayControllerJoinMap.cs>\n\n### DmChasisController\n\n> supports: All DM-MD-8x8/16x16/32x32 chassis, with or w/o DM-CPU3 Card\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs>\n\n### DmRmcController\n\n> supports: All DM-RMC devices\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmRmcControllerJoinMap.cs>\n\n### DmTxController\n\n> supports: All Dm-Tx devices\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmTxControllerJoinMap.cs>\n\n### DmpsAudioOutputController\n\n> supports: Program, Aux1, Aux2 outputs of all DMPS3 Control Systems\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs>\n\n### DmpsRoutingController\n\n> supports: Av routing for all DMPS3 Control Systems\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs>\n\n### GenericRelayController\n\n> supports: Any relay port on a Crestron Control System or Dm Endpoint\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/GenericRelayControllerJoinMap.cs>\n\n### GenericLightingJoinMap\n\n> supports: Devices derived from PepperDash.Essentials.Core.Lighting.LightingBase\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/GenericLightingJoinMap.cs>\n\n### GlsOccupancySensorBase\n\n> supports: Any Crestron GLS-Type Occupancy sensor - single/dual type\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/GlsOccupancySensorBaseJoinMap.cs>\n\n### HdMdxxxCEController\n\n> supports: HD-MD-400-C-E, HD-MD-300-C-E, HD-MD-200-C-E, HD-MD-200-C-1G-E-B/W\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/HdMdxxxCEControllerJoinMap.cs>\n\n### IBasicCommunication\n\n> supports: Any COM Port on a Control System or Dm Endpoint device, TCP Client, SSH Client, or UDP Server\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/IBasicCommunicationJoinMap.cs>\n\n### IDigitalInput\n\n> supports: Any Digital Input on a Control System, or DM Endpoint device\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/IDigitalInputJoinMap.cs>\n\n### SystemMonitorController\n\n> supports: Exposing the system monitor slot for any Control System\n\n<https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Bridges/JoinMaps/SystemMonitorJoinMap.cs>\n\n## Example SIMPL Windows Program\n\nWe've provided an [example program](https://github.com/PepperDash/EssentialsSIMPLWindowsBridgeExample) for SIMPL Windows that works with the provided example Essentials configuration file [SIMPLBridgeExample_configurationFile.json](https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Example%20Configuration/SIMPLBridging/SIMPLBridgeExample_configurationFile.json). Load Essentials and the example SIMPL program to two slots on the same processor and you can get a better idea of how to take advantage of SIMPL Windows bridging.\n\nNext: [Essentials architecture](~/docs/technical-docs/Arch-summary.md)\n"
  },
  {
    "path": "docs/docs/getting-started.md",
    "content": "# Getting Started"
  },
  {
    "path": "docs/docs/how-to/how-to-add-docs.md",
    "content": "# How to Add Documentation to Essentials\n\nThis guide explains how to add new documentation articles to the Essentials docFx site.\n\n## Overview\n\nThe Essentials documentation uses [docFx](https://dotnet.github.io/docfx/) to generate a static documentation website. Documentation files are organized in a hierarchical structure with a table of contents (TOC) file that defines the site navigation. Documentation should be organized and written to fit into the [Diátaxis](https://diataxis.fr/start-here/) conceptual framework.\n\n## Documentation Structure\n\nDocumentation files are located in `/docs/docs/` and organized into the following subdirectories:\n\n- **how-to/** - Step-by-step guides and tutorials\n- **usage/** - Usage documentation for SIMPL bridging, standalone use, and hardware integration\n- **technical-docs/** - Technical documentation including architecture, plugins, and API references\n- **images/** - Image assets used in documentation\n\n## Adding a New Document\n\n### Step 1: Create Your Markdown File\n\n1. Determine which category your document belongs to (how-to, usage, or technical-docs)\n2. Create a new `.md` file in the appropriate subdirectory\n3. Use a descriptive filename with hyphens (e.g., `my-new-feature.md`)\n\n**Example:**\n```bash\n# For a how-to guide\ntouch /docs/docs/how-to/configure-audio-settings.md\n\n# For usage documentation\ntouch /docs/docs/usage/video-switcher-control.md\n\n# For technical documentation\ntouch /docs/docs/technical-docs/custom-device-plugin.md\n```\n\n### Step 2: Write Your Content\n\nStart your markdown file with a level 1 heading (`#`) that serves as the page title:\n\n```markdown\n# Your Document Title\n\nBrief introduction to the topic.\n\n## Section Heading\n\nContent goes here...\n\n### Subsection\n\nMore detailed content...\n```\n\n**Markdown Features:**\n- Use standard markdown syntax\n- Include code blocks with language specifiers (```csharp, ```json, etc.)\n- Add images: `![Alt text](../images/your-image.png)`\n- Link to other docs: `[Link text](../usage/related-doc.md)`\n\n### Step 3: Add to Table of Contents\n\nEdit `/docs/docs/toc.yml` to add your new document to the navigation:\n\n```yaml\n- name: How-to's\n  items:\n    - href: how-to/how-to-add-docs.md\n    - href: how-to/your-new-doc.md  # Add your document here\n```\n\n**TOC Structure:**\n- `name:` - Display name in the navigation menu\n- `href:` - Relative path to the markdown file\n- `items:` - Nested items for hierarchical navigation\n\n**Example with nested items:**\n```yaml\n- name: Usage\n  items:\n  - name: SIMPL Bridging\n    href: usage/SIMPL-Bridging-Updated.md\n    items:\n    - name: Your Sub-Topic\n      href: usage/your-sub-topic.md\n```\n\n### Step 4: Test Locally\n\nBuild and preview the docFx site locally to verify your changes:\n\n```bash\n# Navigate to the docs directory\ncd docs\n\n# Build the documentation\ndocfx build\n\n# Serve the site locally\ndocfx serve _site\n```\n\nThen open your browser to `http://localhost:8080` to view the site.\n\n## Best Practices\n\n### File Naming\n- Use lowercase with hyphens: `my-document-name.md`\n- Be descriptive but concise\n- Avoid special characters\n\n### Content Guidelines\n- Start with a clear introduction\n- Use hierarchical headings (H1 → H2 → H3)\n- Include code examples where appropriate\n- Add images to illustrate complex concepts\n- Link to related documentation\n\n### TOC Organization\n- Group related documents under the same parent\n- Order items logically (basic → advanced)\n- Keep the TOC hierarchy shallow (2-3 levels max)\n- Use clear, descriptive names for navigation items\n\n## Common Issues\n\n### Document Not Appearing\n- Verify the file path in `toc.yml` is correct and uses forward slashes\n- Ensure the markdown file exists in the specified location\n- Check for YAML syntax errors in `toc.yml`\n\n### Images Not Loading\n- Verify image path is relative to the markdown file location\n- Use `../images/` for files in the images directory\n- Ensure image files are committed to the repository\n\n### Broken Links\n- Use relative paths for internal links\n- Test all links after building the site\n- Use `.md` extension when linking to other documentation files\n\n## Additional Resources\n\n- [docFx Documentation](https://dotnet.github.io/docfx/)\n- [Markdown Guide](https://www.markdownguide.org/)\n- [YAML Syntax](https://yaml.org/spec/1.2/spec.html)\n- [Diátaxis](https://diataxis.fr/start-here/)\n"
  },
  {
    "path": "docs/docs/technical-docs/Arch-1.md",
    "content": "# Essentials architecture\n\n## Device and DeviceManager\n\n---\n[YouTube Video - The Device Model in PepperDash Essentials](https://youtu.be/QF4vCQfOYGw)\n***\n\nA `Device` (`PepperDash.Core.Device`) is a logical construct. It may represent a piece of hardware, a port, a socket, a collection of other devices/ports/constructs that define an operation, or any unit of logic that should be created at startup and exist independent of other devices.\n\n`DeviceManager` (`PepperDash.Essentials.Core.DeviceManager`) is the collection of all Devices. The collection of everything we control, and other business logic in a system. See the list below for what is typical in the device manager.\n\n## Flat system design\n\nIn Essentials, most everything we do is focused in one layer: The Devices layer. This layer interacts with the physical Crestron and other hardware and logical constructs underneath, and is designed so that we rarely act directly on the often-inconsistent hardware layer. The `DeviceManager` is responsible for containing all of the devices in this layer.\n\nTypes of things in `DeviceManager`:\n\n* Rooms\n* Sources\n* Codecs, DSPs, displays, routing hardware\n* IR Ports, Com ports, SSh Clients, ...\n* Occupancy sensors and relay-driven devices\n* Logical devices that manage multiple devices and other business, like shade or lighting scene controllers\n* Fusion connectors to rooms\n\nA Device doesn't always represent a physical piece of hardware, but rather a logical construct that \"does something\" and is used by one or more other devices in the running program.  For example, we create a room device, and its corresponding Fusion device, and that room has a Cisco codec device, with an attached SSh client device. All of these lie in a flat collection in the `DeviceManager`.\n\n> The `DeviceManager` is nothing more than a modified collection of things, and technically those things don't have to be Devices, but must at least implement the `IKeyed` (`PepperDash.Core.IKeyed`) interface (simply so items can be looked up by their key.) Items in the `DeviceManager` that are Devices are run through additional steps of [activation](~/docs/technical-docs/Arch-activate.md#2-pre-activation) at startup.  This collection of devices is all interrelated by their string keys.\n\nIn this flat design, we spin up devices, and then introduce them to their \"coworkers and bosses\" - the other devices and logical units that they will interact with - and get them all operating together to form a running unit. For example: A room configuration will contain a \"VideoCodecKey\" property and a \"DefaultDisplayKey\" property. The `DeviceManager` provides the room with the codec or displays having the appropriate keys. What the room does with those is dependent on its coding.\n\n> In the default Essentials routing scheme, the routing system gets the various devices involved in given route from `DeviceManager`, as they are discovered along the defined tie-lines. This is all done at route-time, on the fly, using only device and port keys. As soon as the routing operation is done, the whole process is released from memory. This is extremely-loose coupling between objects.\n\nThis flat structure ensures that every device in a system exists in one place and may be shared and reused with relative ease. There is no hierarchy.\n\n## Architecture drawing\n\n![Architecture overview](~/docs/images/arch-overview.png)\n\nNext: [Configurable lifecycle](~/docs/technical-docs/Arch-lifecycle.md)\n"
  },
  {
    "path": "docs/docs/technical-docs/Arch-activate.md",
    "content": "# Essentials architecture: DeviceManager activation\n\n## What is all this?\n\nThe Essentials system architecture is a loose collection of \"things\" - generally real or logical Devices - that all need to relate to each other. In the interest of keeping Essentials extensible and flexible, we use an non-ordered collection of objects that should only have references to each other in the least-binding way possible. Meaning: Devices should be designed to be able to function without related objects present, and when they are present they should only retain loose reference to those other objects for memory management and later deconstruction as Essentials grows into a real-time configurable environment.\n\nIn order to facilitate this loose coupling, Essentials devices go through five phases during the startup process: Construction; addition to `DeviceManager`; pre-activation; activation; post-activation. We will describe what is optimal behavior for each of the steps below:\n\n### Classes Referenced\n\n* `PepperDash.Core.Device`\n* `PepperDash.Essentials.Core.EssentialsDevice`\n* `PepperDash.Essentials.Core.DeviceManager`\n* `PepperDash.Essentials.Core.Privacy.MicrophonePrivacyController`\n\n## 1. Construction and addition to the DeviceManager\n\nIn general, a device's constructor should only be used to get the \"framework\" of the device in place. All devices are constructed in this stage.  Rooms and fusion bridges included. Simple devices like IR driver devices, and devices with no controls can be completely spun up in this phase. All devices are added to the `DeviceManager` after they are constructed, but may not be fully functional.\n\n## 2. Pre-activation\n\nThis stage is rarely used. It exists to allow an opportunity for any necessary logic to take place before the main activation phase.\n\n## 3. Activation\n\nThis stage is the main phase of startup, and where most devices will get up and running, if they need additional startup behavior defined.  The developer will code an optional overridden `CustomActivate()` method on the device class. This is where hardware ports may be set up; signals and feedbacks linked; UI drivers fired up; rooms linked to their displays and codec... With the exception of early-designed devices, most new Essentials classes do all of their startup here, rather than in their constructors.\n\nRemember that in your `CustomActivate()` method, you cannot assume that a device you depend on is alive and running yet.  It may be activating later.  You _can_ depend on that device's existence, and link yourself to it, but it may not be functional yet. In general, there should be no conditions in any Essentials code that depend on device startup sequence and ordering. All rooms, devices, classes should be able to function without linked devices being alive, and respond appropriately when they do come to life. Any post-activation steps can be done in step four below - and should be avoided in general.\n\nIf the `CustomActivate()` method is long, consider breaking it up into many smaller methods. This will enhance exception handling and debugging when things go wrong, with more-detailed stack traces, and makes for easier-to-read code.\n\nNote: It is best-practice in Essentials to not write arbitrarily-timed startup sequences to ensure that a \"system\" or room is functional. Rather, we encourage the developer to use various properties and conditions on devices to aggregate together \"room is ready\" statuses that can trigger further action. This ensures that all devices can be up and alive, allowing them to be debugged within a program that may otherwise be misbehaving - as well as not making users and expensive developers wait for code to start up!\n\n```cs\npublic override bool CustomActivate()\n{\n    Debug.Console(0, this, \"Final activation. Setting up actions and feedbacks\");\n    SetupFunctions();\n    SetupFeedbacks();\n\n    EISC.SigChange += EISC_SigChange;\n    ...\n}\n```\n\n## 4. Post-activation\n\nThis phase is used primarily to handle any logic in a device that might be dependent on another device, and we need to ensure that we have waited for the dependent device to be activated first.  For example, if we look at the `MicrophonePrivacyController` class, this is a \"virtual\" device whose purpose is to control the mute state of microphones from one or more contact closure inputs as well as provide feedback via different colored LEDs as to the current mute state.  This virtual-device doesn't actually represent any sort of physical hardware device, but rather relies on associating itself with other devices that represent digital inputs and relays as well as whatever device is responsible for preforming the actual muting of the microphones.\n\nWe can see in the example below that during the `CustomActivate()` phase, we define a post-activation action via a lambda in `AddPostActivationAction()` that will execute during the post-activation phase.  The purpose here is to check the state of the microphone mute and set the state of the relays that control the LEDs accordingly.  We need to do this as a post-activation action because we need to make sure that the devices PrivacyDevice, RedLedRelay and GreenLedRelay are fully activated before we can attempt to interact with them.\n\n### **Example**\n\n```cs\npublic override bool CustomActivate()\n{\n    foreach (var i in Config.Inputs)\n    {\n        var input = DeviceManager.GetDeviceForKey(i.DeviceKey) as IDigitalInput;\n        if(input != null)\n            AddInput(input);\n    }\n\n    var greenLed = DeviceManager.GetDeviceForKey(Config.GreenLedRelay.DeviceKey) as GenericRelayDevice;\n    if (greenLed != null)\n        GreenLedRelay = greenLed;\n    else\n        Debug.Console(0, this, \"Unable to add Green LED device\");\n\n    var redLed = DeviceManager.GetDeviceForKey(Config.RedLedRelay.DeviceKey) as GenericRelayDevice;\n    if (redLed != null)\n        RedLedRelay = redLed;\n    else\n        Debug.Console(0, this, \"Unable to add Red LED device\");\n\n    AddPostActivationAction(() => {\n        CheckPrivacyMode();\n        PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange -= PrivacyModeIsOnFeedback_OutputChange;\n        PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange;\n    });\n\n    initialized = true;\n\n    return base.CustomActivate();\n}\n\nvoid CheckPrivacyMode()\n{\n    if (PrivacyDevice != null)\n    {\n        var privacyState = PrivacyDevice.PrivacyModeIsOnFeedback.BoolValue;\n        if (privacyState)\n            TurnOnRedLeds();\n        else\n            TurnOnGreenLeds();\n    }\n}\n```\n\n## Activation exceptions\n\nEach of the three activation phases operates in a try/catch block for each device.  This way if one device has a fatal failure during activation, the failure will be logged and the system can continue to activate. This allows the developer to chase down multiple issues per load while testing, or to fix configuration omissions/errors as a group rather than one-at-a-time. A program can theoretically be fully-initialized and have many or all devices fail. We generally do not want to depend on exception handling to log device failures. Construction and activation code should have plenty of null checks, parameter validity checks, and debugging output to prevent exceptions from occurring. `String.IsEmptyOrNull(myString)` and `if(myObject == null)` are your friends. Invite them often.\n\n## Interdependence\n\nIn any real-world system, devices and business logic need to talk to each other, otherwise, what's the point of all this coding? When creating your classes and configuration, it is best practice to _try_ not to \"plug\" one device into another during construction or activation. For example your touchpanel controller class has a `Display1` property that holds the display-1 object. Rather, it may be better to refer to the device as it is stored in the `DeviceManager` when it's needed using the static `DeviceManager.GetDeviceForKey(key)` method to get a reference to the device, which can be cast using various interfaces/class types, and then interacted with.  This prevents objects from being referenced in places where the developer may later forget to dereference them, causing memory leak.  This will become more important as Essentials becomes more able to be reconfigured at runtime.\n\nAs an example, [connection-based routing](~/docs/technical-docs/Connection-based-routing.md#essentials-connection-based-routing) uses these methods.  When a route is requested, the collection of tielines and devices is searched for the devices and paths necessary to complete a route, but there are no devices or tie lines that are object-referenced in running code.  It can all be torn down and reconfigured without any memory-management dereferencing, setting things to null.\n\n## Device Initialization\n\nOnce the `DeviceManager` has completed the activation phase cycle for all devices, the devices themselves can be initialized.  The `EssentialsDevice` class subscribes to the `DeviceManager.AllDevicesActivated` event and invokes the virtual `Initialize()` method on `Device` in a separate thread.  This allows all devices to concurrently initialize in parallel threads. \n\nThe main task that should be undertaken in the `Initialize()` method for any 3rd party device class, it to begin communication with the device via its API.  Ideally, no class that communicates with a 3rd party device outside the program should attempt to start communicating before this point.\n\n### Example (from `PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoSparkCodec`)\n```cs\n        public override void Initialize()\n        {\n            var socket = Communication as ISocketStatus;\n            if (socket != null)\n            {\n                socket.ConnectionChange += new EventHandler<GenericSocketStatusChageEventArgs>(socket_ConnectionChange);\n            }\n\n            Communication.Connect();\n\n            CommunicationMonitor.Start();\n\n            const string prefix = \"xFeedback register \";\n\n            CliFeedbackRegistrationExpression =\n                prefix + \"/Configuration\" + Delimiter +\n                prefix + \"/Status/Audio\" + Delimiter +\n                prefix + \"/Status/Call\" + Delimiter +\n                prefix + \"/Status/Conference/Presentation\" + Delimiter +\n                prefix + \"/Status/Cameras/SpeakerTrack\" + Delimiter +\n                prefix + \"/Status/RoomAnalytics\" + Delimiter +\n                prefix + \"/Status/RoomPreset\" + Delimiter +\n                prefix + \"/Status/Standby\" + Delimiter +\n                prefix + \"/Status/Video/Selfview\" + Delimiter +\n                prefix + \"/Status/Video/Layout\" + Delimiter +\n                prefix + \"/Status/Video/Input/MainVideoMute\" + Delimiter +\n                prefix + \"/Bookings\" + Delimiter +\n                prefix + \"/Event/CallDisconnect\" + Delimiter +\n                prefix + \"/Event/Bookings\" + Delimiter +\n                prefix + \"/Event/CameraPresetListUpdated\" + Delimiter +\n                prefix + \"/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier\" + Delimiter;\n        }\n```\n\n## The goal\n\nRobust C#-based system code should not depend on \"order\" or \"time\" to get running.  We do not need to manage the order of our startup in this environment.  Our Room class may come alive before our DSP and or Codec, and the Room is responsible for handling things when those devices become available. The UI layer is responsible for blocking the UI or providing status when the Room's requirements are coming alive, or if something has gone away. We use events or `Feedbacks` to notify dependents that other devices/classes are ready or not, but we do not prevent continued construction/activation of the system when many of these events don't happen, or don't happen in a timely fashion.  This removes the need for startup management, which is often prolonged and consumes _tons_ of developer/installer time.  A fully-loaded Essentials system may go through activation in several seconds, with all devices concurrently getting themselves going, where legacy code may take 10 minutes.\n\nWhen designing new Device-based classes, be it rooms, devices, port controllers, bridges, make them as independent as possible.  They could exist alone in a program with no required partner objects, and just quietly exist without failing. We want the system to be fast and flexible, and keeping the interdependence between objects at a minimum improves this flexibility into the future.\n\nNext: [More architecture](~/docs/technical-docs/Arch-topics.md)\n"
  },
  {
    "path": "docs/docs/technical-docs/Arch-lifecycle.md",
    "content": "# Essentials Configurable System Lifecycle\n\nThe diagram below describes how Essentials gets a program up and running.\n\n(The various activation phases are covered in more detail on the [next page](~/docs/technical-docs/Arch-activate.md))\n\n![Lifecycle](~/docs/images/lifecycle.png)\n\nNext: [Activation phases](~/docs/technical-docs/Arch-activate.md)\n"
  },
  {
    "path": "docs/docs/technical-docs/Arch-summary.md",
    "content": "# Essentials architecture\n\n## Summary\n\nPepperDash Essentials is an open-source framework for control systems, built on Crestron's Simpl# Pro framework. It can be configured as a standalone program capable of running a wide variety of system designs and can also be used to augment other Crestron programs.\n\nEssentials is a collection of C# libraries that can be used in many ways. It is a 100% configuration-driven framework that can be extended to add different workflows and behaviors, either through the addition of new device-types and classes, or via a plug-in mechanism. The framework is a collection of things that are all related and interconnected, but in general do not have strong dependencies on each other.\n\n## Framework Libraries\n\nThe table below is a guide to understand the basic organization of code concepts within the various libraries that make up the architecture.\n\n![Table](~/docs/images/arch-table.PNG)\n\nThe diagram below shows the reference dependencies that exist between the different component libraries that make up the Essentials Framework.\n\n![Architecture drawing](~/docs/images/arch-high-level.png)\n\nNext: [Architecture](~/docs/technical-docs/Arch-1.md)\n"
  },
  {
    "path": "docs/docs/technical-docs/Arch-topics.md",
    "content": "# Configuration topics\n\nConfiguration is central to Essentials. On this page we will cover configuration-related topics, including the important concept of configure-first and some details about the config file process.\n\n## Classes Referenced\n\n- `PepperDash.Essentials.Core.Config.DeviceConfig`\n\n## Configure-first development\n\n## Framework Libraries\n\nThe table below is meant to serve as a guide to understand the basic organization of code concepts within the various libraries that make up the architecture.\n\n_Todo, try a text-based table:_\n\n![Table](~/docs/images/arch-table.PNG)\n\nThe diagram below shows the reference dependencies that exist between the different component libraries that make up the Essentials Framework.\n\n![Architecture drawing](~/docs/images/arch-high-level.png)\n\n### Architecture\n\n#### Device and DeviceManager\n\nA `Device` is a logical construct. It may represent a piece of hardware, a port, a socket, a collection of other devices/ports/constructs that define an operation, or any unit of logic that should be created at startup and exist independent of other devices.\n\n`DeviceManager` is the collection of all Devices. The collection of everything we control on a system. **ADD SOME MORE HERE**\n\n#### Flat system design\n\nIn Essentials, most everything we do is focused in one layer: The Devices layer. This layer interacts with the physical Crestron and other hardware and logical constructs underneath, and is designed so that we rarely act directly on the often-inconsistent hardware layer. The `DeviceManager` is responsible for containing all of the devices in this layer.\n\nTypes of devices:\n\n- Rooms\n- Sources\n- Codecs, DSPs, displays, routing hardware\n- IR Ports, Com ports, SSh Clients, ...\n- Occupancy sensors and relay-driven devices\n- Logical devices that manage multiple devices and other business, like shade or lighting scene controllers\n- Fusion connectors to rooms\n\nA Device doesn't always represent a physical piece of hardware, but rather a logical construct that \"does something\" and is used by one or more other devices in the running program. For example, we create a room device, and its corresponding Fusion device, and that room has a Cisco codec device, with an attached SSh client device. All of these lie in a flat collection in the `DeviceManager`.\n\n> The `DeviceManager` is a modified collection of objects, and those objects don't have to inherit from Devices or EssentialsDevices, but must at least implement the `IKeyed` interface (so items can be looked up by their key.) Items in the `DeviceManager` that are Devices are run through additional steps of activation at startup. This collection of devices is all interrelated by their string keys.\n\nIn this flat design, we spin up devices, and then introduce them to their \"coworkers and bosses\" - the other devices and logical units that they will interact with - and get them all operating together to form a running unit. For example: A room configuration will contain a \"VideoCodecKey\" property and a \"DefaultDisplayKey\" property. The `DeviceManager` provides the room with the codec or displays having the appropriate keys. What the room does with those is dependent on its coding.\n\n> In the default Essentials routing scheme, the routing system gets the various devices involved in given route from `DeviceManager`, as they are discovered along the defined tie-lines. This is all done at route-time, on the fly, using only device and port keys. As soon as the routing operation is done, the whole process is released from memory. This is extremely-loose coupling between objects.\n\nThis flat structure ensures that every device in a system exists in one place and may be shared and reused with relative ease. There is no hierarchy.\n\n#### Architecture drawing\n\n![Architecture overview](~/docs/images/arch-overview.png)\n\n#### Essentials Configurable System Lifecycle\n\n![Lifecycle](~/docs/images/lifecycle.png)\n\n### Activation phases additional topics and examples (OTHER DOCS)\n\nConcepts (link)\n\nRoom and touchpanel activation (link)\n\n#### Configure first development\n\nOne of the primary concepts that has been adopted and must be adhered to when writing for Essentials framework is the concept of \"configure first.\" The simple version is: Write what you need to do in the related configuration file (and configuration tool) first, then write the code that runs from that configuration. This ensures that the running code can actually be configured in the \"flat\" structure of devices and rooms that Essentials uses.\n\nOften, code is written and tested first without consideration for configurability. Then, when a developer tries to make it configurable, they discover that the code as written doesn’t support it without complicated configuration files. This creates spaghetti code in tools that are written to generate configurations and tends to create tighter coupling between objects than we desire. Later, a modified version of the original program is desired, but because the code was written in such a specific fashion, the code is hard to refactor and extend. This causes the configuration tool and configuration files to become even more convoluted. The modern versions of configuration tools that are starting to come out are modular and componentized. We want to ensure as much re-use of these modules as possible, with extensions and added features added on, rather than complete rewrites of existing code. In our running systems, we want to ensure as much flexibility in design as possible, eliminating multiple classes with similar code.\n\n### Configuration reader process\n\nAt the heart of the Essentials framework is the configuration system. While not technically necessary for a system written with the Essentials framework, it is the preferred and, currently, the only way to build an Essentials system. The configuration file is JSON, and well-defined (but not well documented, yet). It is comprised of blocks:\n\n- info (object) Contains metadata about the config file\n- devices (array) Contains, well, the devices we intend to build and load\n- rooms (array, typically only one) Contains the rooms we need\n- sourceLists (object) Used by one or more rooms to represent list(s) of sources for those rooms\n- tieLines (array) Used by the routing system to discover routing between sources and displays\n\nIn addition, a downloaded Portal config file will most likely be in a template/system form, meaning that the file contains two main objects, representing the template configuration and its system-level overrides. Other metadata, such as Portal UUIDs or URLs may be present.\n\nAt startup, the configuration file is read, and a ReadyEvent is fired. Upon being ready, that configuration is loaded by the ConfigReader.LoadConfig() method. The template and system are merged into a single configuration object, and that object is then deserialized into configuration wrapper classes that define most of the structure of the program to be built. (Custom configuration objects were built to allow for better type handling rather than using JToken methods to parse out error-prone property names.)\n\nFor example, a `DeviceConfig` object:\n\n```cs\nnamespace PepperDash.Essentials.Core.Config\n{\n    public class DeviceConfig\n    {\n        [JsonProperty(\"key\")]\n        public string Key { get; set; }\n\n        [JsonProperty(\"uid\")]\n        public int Uid { get; set; }\n\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        [JsonProperty(\"group\")]\n        public string Group { get; set; }\n\n        [JsonProperty(\"type\")]\n        public string Type { get; set; }\n\n        [JsonProperty(\"properties\")]\n        [JsonConverter(typeof(DevicePropertiesConverter))]\n        public JToken Properties { get; set; }\n    }\n}\n```\n\n_Every_ `Device` present must adhere to those five properties plus a properties object. The properties object will have its own deserialization helpers, depending on what its structure is.\n\nOnce the ConfigReader has successfully read and deserialized the config file, then `ControlSystem.Load()` is called. This does the following in order:\n\n1. Loads Devices\n2. Loads TieLines\n3. Loads Rooms\n4. Loads LogoServer\n5. Activation sequence\n\nThis ordering ensures that all devices are at least present before building tie lines and rooms. Rooms can be built without their required devices being present. In principle, this could break from the loosely-coupled goal we have described, but it is the clearest way to build the system in code. The goal is still to build a room class that doesn't have functional dependencies on devices that may not be ready for use.\n\nIn each device/room step, a device factory process is called. We call subsequent device factory methods in the various libraries that make up Essentials until one of them returns a functional device. This allows us to break up the factory process into individual libraries, and not have a huge list of types and build procedures. Here's part of the code:\n\n```cs\n// Try local factories first\nvar newDev = DeviceFactory.GetDevice(devConf);\n\nif (newDev == null)\n    newDev = BridgeFactory.GetDevice(devConf);\n\n// Then associated library factories\nif (newDev == null)\n    newDev = PepperDash.Essentials.Core.DeviceFactory.GetDevice(devConf);\nif (newDev == null)\n    newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf);\nif (newDev == null)\n    newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf);\nif (newDev == null)\n    newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf);\n```\n\nIn each respective factory, or device constructor, the configuration's properties object is either converted to a config object or read from using `JToken` methods. This builds the device which may be ready to go, or may require activation as described above.\n\nA similar process is carried out for rooms, but as of now, the room types are so few that they are all handled in the `ControlSystem.LoadRooms()` method.\n\n_This process will soon be enhanced by a plug-in mechanism that will drill into dynamically-loaded DLLs and load types from factories in those libraries. This is where custom essentials systems will grow from._\n\nAfter those five steps, the system will be running and ready to use.\n"
  },
  {
    "path": "docs/docs/technical-docs/Communication-Basics.md",
    "content": "# Unifying communication methods\n\nIn networked A/V systems, devices can use many different methods of communication: COM ports, TCP/IP sockets, Telnet, SSH. Generally, the data protocol and commands that are sent and received using any of these methods are the same, and it is not necessary for a device to know the details of the communication method it is using. A Samsung MDC protocol display in room 1 using RS232 speaks the same language as another Samsung MDC does in the next room using TCP/IP. For these, and most cases where the device doesn't need to know its communication method, we introduce the `IBasicCommunication` interface.\n## Classes Referenced\n\n* `PepperDash.Core.IBasicCommunication`\n* `PepperDash.Core.ISocketStatus`\n* `PepperDash.Core.GenericTcpIpClient`\n* `PepperDash.Core.GenericSshClient`\n* `PepperDash.Core.GenericSecureTcpIpClient`\n* `PepperDash.Essentials.Core.ComPortController`\n* `PepperDash.Essentials.Core.StatusMonitorBase`\n## IBasicCommunication and ISocketStatus\n\nAll common communication controllers will implement the `IBasicCommunication` interface, which is an extension of `ICommunicationReceiver`. This defines receive events, connection state properties, and send methods. Devices that need to use COM port, TCP, SSh or other similar communication will require an `IBasicCommunication` type object to be injected at construction time.\n\n```cs\n/// <summary>\n/// An incoming communication stream\n/// </summary>\npublic interface ICommunicationReceiver : IKeyed\n{\n    event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n    event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n    bool IsConnected { get; }\n    void Connect();\n    void Disconnect();\n}\n\n/// <summary>\n/// Represents a device that uses basic connection\n/// </summary>\npublic interface IBasicCommunication : ICommunicationReceiver\n{\n    void SendText(string text);\n    void SendBytes(byte[] bytes);\n}\n\n/// <summary>\n/// For IBasicCommunication classes that have SocketStatus. GenericSshClient,\n/// GenericTcpIpClient\n/// </summary>\npublic interface ISocketStatus : IBasicCommunication\n{\n    event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;\n    SocketStatus ClientStatus { get; }\n}\n```\n\n### Developing devices with communication\n\nEssentials uses dependency injection concepts in its start up phase. Simply, most devices use the same methods of communication, and are often communication-agnostic. During the build-from-configuration phase, the communication method device is instantiated, and then injected into the device that will use it. Since the communication device is of `IBasicCommunication`, the device controller receiving it knows that it can do things like listen for events, send text, or be notified when sockets change.\n\n### Device Factory, Codec example\n\n![Communication Device factory](~/docs/images/comm-device-factory.png)\n\nThe DeviceManager will contain two new devices after this: The Cisco codec, and the codec's `GenericSshClient`. This enables easier debugging of the client using console methods. Some devices like this codec will also have a `StatusMonitorBase` device, for Fusion and other reporting.\n\n> `ComPortController` is `IBasicCommunication` as well, but methods like `Connect()` and `Disconnect()` do nothing on these types.\n\n#### ISocketStatus\n\n`PepperDash.Core.GenericTcpIpClient`, `GenericSshClient` and some other socket controllers implement `ISocketStatus`, which is an extension of `IBasicCommunication`. This interface reveals connection status properties and events.\n\n```cs\npublic interface ISocketStatus : IBasicCommunication\n{\n    event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;\n    SocketStatus ClientStatus { get; }\n}\n```\n\nClasses that are using socket-based comms will need to check if the communication is `ISocketStatus` and link up to the `ConnectionChange` event for connection handling.\n"
  },
  {
    "path": "docs/docs/technical-docs/ConfigurationStructure.md",
    "content": "# Configuration Structure\n\n---\n\n[YouTube Video - Configuring PepperDash Essentials](https://youtu.be/EK8Ti9a1o7s)\n\n***\n\nThe Essentials configuration structure is designed to allow minimum duplication of data across systems that share many similarities, which makes it ideally suited for applications where large numbers of duplicate room types must be deployed.\n\nAt a high level, the idea is to define a template of all of the common configuration shared by a group of systems of the same type.  Then individual differences per system instance can be defined in a system block that either add data missing in the template, or override the default values set in the template.\n\n## Top Level Object Structure (Double Config)\n\n```cs\n{\n    // This object is deserialized to type PepperDash.Essentials.Core.Config.EssentialsConfig\n\n    \"system_url\":\"\", // For Portal use only\n    \"template_url\":\"\", // For Portal use only\n    \"template\":{\n        // This object is deserialized to type PepperDash.Essentials.Core.EssentialsConfig\n        // For most manually generated configuration, only define data here.  Leave system empty\n    },\n    \"system\":{\n        // This object is deserialized to type PepperDash.Essentials.Core.EssentialsConfig\n        // Any data here will be overlayed on top of the data in template.  In the case of duplicate values\n        // the value in system will be overwrite any value in template\n    }\n}\n```\n\n## Object Structure for `template` and `system` (`PepperDash.Essentials.Core.EssentialsConfig`)\n\n``` js\n{\n     \"info\": {\n        // This object is deserialized to type PepperDash.Essentials.Core.Config.InfoConfig\n        // Contains information about the system/configuration\n     },\n     \"devices\": [\n        // This object is deserialized to type List<PepperDash.Essentials.Core.Config.DeviceConfig>\n        // An array of devices\n     ],\n     \"rooms\": [\n        // This object is deserialized to type List<PepperDash.Essentials.Core.Config.DeviceConfig>\n        // An array of rooms.  These are not automatically deserialized\n     ],\n     \"tielines\":[\n        // An array of tie lines that describe the connections between routing ports on devices\n     ],\n     \"sourceLists\":{\n        // This object is deserialized to type Dictionary<string, Dictionary<string, PepperDash.Essentials.Core.SourceListItem>>\n        // An object that contains a collection\n     },\n     \"joinMaps\":{\n        // This object is deserialized to type Dictionary<string, string> where the value is a serialized class that inherits from JoinMapBase to be deserialized later\n        // Used to define custom join maps for bridging devices to SIMPL\n     }\n}\n```\n\n## The Template and System Concept (Merging Configurations)\n\nIn order to understand how and why we use a double configuration concept, it's important to understand the relationship between a Template and a System in Portal.  A System represents a physical installed group of hardware(either currently or in the future), acting together usually as part of a single control system program.  A system MUST inherit from a Template.  A Template represents the common elements of one or more systems.\n\nThe idea being that configuration values that are common to all systems can be stored in the configuration for the template.  Then, any configuration values that are unique to a particular system cane be stored in the configuration of the System.  By \"merging\" the System configuration values over top of the Template configuration values, the resulting data contains all of the values that should be shared by each system that inherits from a common template, as well as the unique values for each individual system.\n\nBelow is an example of a double configuration containing both template and system properties.\n\n```JSON\n{\n    \"template\": {\n        \"info\": {\n            \"name\": \"Template Name\",\n            \"description\": \"A 12 person conference room\"\n        },\n        \"devices\": [\n\n        ],\n        \"rooms\": [\n\n        ]\n    },\n    \"system\": {\n        \"info\": {\n            \"name\": \"System Name\",\n            \"myNewSystemProperty\": \"Some Value\"\n        },\n        \"devices\": [\n\n        ],\n        \"rooms\": [\n\n        ]\n    }\n}\n```\n\nBelow is an example of the result of merging the above double configuration example into a single configuration.\n\n```JSON\n{\n    \"info\": {\n        \"name\": \"System Name\",                          // Since this property existed in both the template and system, the system value replaces the template value after the merge\n        \"description\": \"A 12 person conference room\",   // This property existed only in the template and is unchanged after the merge\n        \"myNewSystemProperty\": \"Some Value\"             // This property existed only in the system and is unchanged after the merge\n    },\n    \"devices\": [\n\n    ],\n    \"rooms\": [\n\n    ]\n}\n```\n\n---\n\n## Device Object Structure\n\nThe devices array is meant to hold a series of device objects.  The basic device object structure is defined below.\n\n```JSON\n{\n    \"key\": \"someUniqueString\",  // *required* a unique string\n    \"name\": \"A friendly Name\",  // *required* a friendly name meant for display to users\n    \"type\": \"exampleType\",      // *required* the type identifier for this object.  \n    \"group\": \"exampleGroup\",    // *required* the group identifier for this object.  This really equates to a category for the device,\n                                // such as \"lighting\" or \"displays\" and may be deprecated in future in favor of \"category\"\n    \"uid\":0,                    // *required* a unique numeric identifier for each device\n    \"properties\": {             // *required* an object where the configurable properties of the device are contained\n        \"control\": {            // an object to contain all of the properties to connect to and control the device\n            \"method\": \"ssh\",    // the control method used by this device\n            \"tcpSshProperties\": {   // contains the necessary properties for the specified method\n                \"address\": \"1.2.3.4\",\n                \"port\": 22,\n                \"username\": \"admin\",\n                \"password\": \"uncrackablepassword\"\n            }\n        },\n        \"someCustomProperty\": \"I Love Tacos!\"\n    }\n    // Do NOT add any custom data at the top level of the device object.  All custom data must be in the properties object.\n}\n```\n\nSome additional details about specific properties that are important to note:\n\n* \"key\": This value needs to be unique in the array of devices objects\n* \"uid\": This value also needs to be unique for reasons related to configuration tools and template/system merging\n* \"type\": Think of this as a way to identify what specific module you might associate with this device.  In Essentials, this value is used to determine what class will be instantiated for the device (ex. \"necmpsx\" or \"samsungMdc\" for two types of displays)\n* \"properties\":  This object is used to store both specific and miscellaneous data about the device.\n  * Specific data, like that shown above in the \"control\" object has a pre-defined structure.\n  * Other data must be stored as objects or new properties inside the \"properties\" object such as \"someCustomProperty\" in the example above.\n* Do NOT add any additional properties at the top level of the device object.  All custom data must be in the \"properties\" object.\n\n## The Device Properties.Control Object\n\nThe control object inside properties has some reserved properties that are used by configuration tools and Essentials that require some caution.\n\n```JSON\n{\n    \"properties\": {             // *required* an object where the configurable properties of the device are contained\n        \"control\": {            // an object to contain all of the properties to connect to and control the device\n            // Example of the reserved properties for a socket based port (ssh, tcpIp, udp)\n            \"method\": \"ssh\",    // the control method used by this device\n            \"tcpSshProperties\": {   // contains the necessary properties for the specified method\n                \"address\": \"1.2.3.4\",               // IP Address or hostname\n                \"port\": 22,\n                \"username\": \"admin\",\n                \"password\": \"uncrackablepassword\",\n                \"autoReconnect\": true,              // If true, the client will attempt to re-connect if the connection is broken externally\n                \"AutoReconnectIntervalMs\": 2000     // The time between re-connection attempts\n            },\n\n            // Example of the reserved properties for a Com port\n            \"method\": \"com\",\n            \"controlPortNumber\": 1,                 // The number of the com port on the device specified by controlPortDevKey\n            \"controlPortDevKey\": \"processor\",       // The key of the device where the com port is located\n            \"comParams\": {                          // This object contains all of the com spec properties for the com port\n                \"hardwareHandshake\": \"None\",\n                \"parity\": \"None\",\n                \"protocol\": \"RS232\",\n                \"baudRate\": 9600,\n                \"dataBits\": 8,\n                \"softwareHandshake\": \"None\",\n                \"stopBits\": 1\n            }\n        }\n    }\n}\n```\n\n---\n\n## Device Merging\n\nThe following examples illustrate how the device key and uid properties affect how devices are merged together in a double configuration scenario.  In order for a template device and a system device to merge, they must have the same key and uid values\n\n```JSON\n{\n    \"template\": {\n        \"info\": {\n            \"name\": \"Template Name\",\n            \"description\": \"A 12 person conference room\"\n        },\n        \"devices\": [\n            {                                           // This is the template device\n                \"key\": \"display-1\",\n                \"name\": \"Display\",\n                \"type\": \"samsungMdc\",\n                \"group\": \"displays\",\n                \"uid\":0,\n                \"properties\": {\n                    \"control\": {\n                        \"method\": \"ssh\",\n                        \"tcpSshProperties\": {\n                            \"address\": \"\",              // Note that at the template level we won't know the actual IP address so this value is left empty\n                            \"port\": 22,\n                            \"username\": \"admin\",\n                            \"password\": \"uncrackablepassword\"\n                        }\n                    }\n                }\n            }\n        ],\n        \"rooms\": [\n\n        ]\n    },\n    \"system\": {\n        \"info\": {\n            \"name\": \"System Name\",\n            \"myNewSystemProperty\": \"Some Value\"\n        },\n        \"devices\": [\n            {                                           // This is the system device\n                \"key\": \"display-1\",\n                \"uid\":0,\n                \"properties\": {\n                    \"control\": {\n                            \"tcpSshProperties\": {\n                            \"address\": \"10.10.10.10\"    // Note that the actual IP address is specified at the system level\n                        }\n                    }\n                }\n            }\n        ],\n        \"rooms\": [\n\n        ]\n    }\n}\n```\n\nBelow is an example of the result of merging the above double configuration example into a single configuration.  \n\n```JSON\n{\n    \"info\": {\n        \"name\": \"System Name\",\n        \"description\": \"A 12 person conference room\",\n        \"myNewSystemProperty\": \"Some Value\"\n    },\n     \"devices\": [\n        {\n            \"key\": \"display-1\",\n            \"name\": \"Display\",\n            \"type\": \"samsungMdc\",\n            \"group\": \"displays\",\n            \"uid\":0,\n            \"properties\": {\n                \"control\": {\n                    \"method\": \"ssh\",\n                    \"tcpSshProperties\": {\n                        \"address\": \"10.10.10.10\",   // Note that the merged device object inherits all of the template\n                                                    // properties and overwrites the template address property with the system value\n                        \"port\": 22,\n                        \"username\": \"admin\",\n                        \"password\": \"uncrackablepassword\"\n                    }\n                }\n            }\n        }\n     ],\n     \"rooms\": [\n\n     ]\n}\n```\n"
  },
  {
    "path": "docs/docs/technical-docs/Connection-Based-Routing.md",
    "content": "# Essentials connection-based routing\n\n## TL;DR\n\nRouting is defined by a connection graph or a wiring diagram. Routable devices are sources, midpoints, or destinations. Devices are connected by tie lines. Tie lines represent the cables connecting devices, and have specific signal types (audio, video, audioVideo, secondaryAudio, usbInput, usbOutput). Routes are made by telling a destination to get a route from a source for a specific signal type. Combined signal types (e.g., audioVideo) are automatically split into separate routing operations.\n\n## Summary\n\nEssentials routing is described by defining a graph of connections between devices in a system, typically in configuration. The audio, video and combination connections are like a wiring diagram. This graph is a collection of devices and tie lines, each tie line connecting a source device, source output port, destination device and destination input port. Tie lines are logically represented as a collection.\n\nWhen routes are to be executed, Essentials will use this connection graph to decide on routes from source to destination. A method call is made on a destination, which says \"destination, find a way for source xyz to get to you.\" An algorithm analyzes the tie lines, instantly walking backwards from the destination, down every connection until it finds a complete path from the source. If a connected path is found, the algorithm then walks forward through all midpoints to the destination, executing switches as required until the full route is complete. The developer or configurer only needs to say \"destination, get source xyz\" and Essentials figures out how, regardless of what devices lie in between.\n\n### Signal Type Handling\n\nWhen a combined signal type like `audioVideo` is requested, Essentials automatically splits it into two separate routing operations—one for audio and one for video. Each signal type is routed independently through the system, ensuring that:\n- Audio-only tie lines can be used for the audio portion\n- Video-only tie lines can be used for the video portion\n- AudioVideo tie lines can be used for both portions\n\nDuring path discovery, **only tie lines that support the requested signal type are considered**. For example, if a video route is requested, only tie lines with the video flag will be evaluated. This ensures signal compatibility throughout the entire routing chain.\n\n### Port-Specific Routing\n\nThe routing system supports routing to and from specific ports on devices. You can specify:\n- A specific input port on the destination device\n- A specific output port on the source device\n- Both specific ports for precise routing control\n\nWhen no specific ports are specified, the algorithm will automatically discover the appropriate ports based on available tie lines.\n\n### Request Queuing\n\nAll routing requests are processed sequentially through a queue. For devices that implement warming/cooling behavior (e.g., projectors), route requests are automatically held when a device is cooling down and executed once the device is ready. This prevents routing errors and ensures proper device state management.\n\n### Classes Referenced\n\n* `PepperDash.Essentials.Core.Routing.IRoutingSource`\n* `PepperDash.Essentials.Core.Routing.IRoutingOutputs`\n* `PepperDash.Essentials.Core.Routing.IRoutingInputs`\n* `PepperDash.Essentials.Core.Routing.IRoutingInputsOutputs`\n* `PepperDash.Essentials.Core.Routing.IRoutingSinkNoSwitching`\n* `PepperDash.Essentials.Core.Routing.IRoutingSinkWithSwitching`\n\n## Example system, a simple presentation system\n\nThe diagram below shows the connections in a simple presentation system, with a few variations in connection paths. Example routes will be described following the diagram.\n\nEach visible line between ports on devices represents a tie line. A tie line connects an output port on one device to an input port on another device, for example: an HDMI port on a document camera to an HDMI input on a matrix switcher. A tie line has a signal type (audio, video, audioVideo, secondaryAudio, usbInput, or usbOutput) that determines what signals can travel through it. It is essentially a logical representation of a physical cable in a system. This diagram has 12 tie lines, and those tie lines are defined in the tieLines array in configuration.\n\n![Routing system diagram](~/docs/images/routing-system-diagram.png)\n\nLet’s go through some examples of routing, using pseudo-code:\n\n1. Method call: “Projector 1, show Doc cam.” Routing will walk backwards through DM-RMC-3 and DM-8x8 iterating through all “wired up” ports until it finds a path back to the Doc cam. Routing will then step back through all devices in the discovered chain, switching routes on those that are switchable: Doc cam: no switching; DM 8x8: route input 3 to output 3; DM-RMC-3: no switching; Projector 1: Select input HDMI In. Route is complete.\n2. Method call: \"Projector 2, show Laptop, video-only.\" Routing will walk backwards through DM-RMC-4, DM 8x8, DM-TX-1, iterating through all connected ports until it finds a connection to the laptop. During this search, only tie lines that support video signals are considered. Routing then steps back through all devices, switching video where it can: Laptop: No switching; DM-TX-1: Select HDMI in; DM 8x8: Route input 5 to output 4; DM-RMC-4: No switching; Projector 2: Select HDMI input. Route is complete.\n3. Method call: \"Amplifier, connect Laptop audio.\" Again walking backwards to Laptop, as in #2 above, but this time only tie lines supporting audio signals are evaluated. Switching will take place on DM-TX-1, DM 8x8, audio-only.\n4. Very simple call: “Lobby display, show signage controller.” Routing will walk back on HDMI input 1 and immediately find the signage controller. It then does a switch to HDMI 1 on the display.\n\nAll four of the above could be logically combined in a series of calls to define a possible “scene” in a room: Put Document camera on Projector 1, put Laptop on Projector 2 and the audio, put Signage on the Lobby display. They key takeaway is that the developer doesn’t need to define what is involved in making a certain route. The person configuring the system defines how it’s wired up, and the code only needs to tell a given destination to get a source, likely through configuration as well.\n\nAll of the above routes can be defined in source list routing tables, covered elsewhere (**make link)**.\n\n---\n\n## Routing Algorithm Details\n\n### Combined Signal Type Splitting\n\nWhen an `audioVideo` route is requested, the routing system automatically splits it into two independent routing operations:\n\n1. **Audio Route**: Finds the best path for audio signals from source to destination\n2. **Video Route**: Finds the best path for video signals from source to destination\n\nEach route can take a different physical path through the system. For example:\n- Video might travel: Laptop → DM-TX-1 → DM Matrix → Display\n- Audio might travel: Laptop → DM-TX-1 → DM Matrix → Audio Processor → Amplifier\n\nBoth routes are discovered, stored, and executed independently. This allows for flexible system designs where audio and video follow different paths.\n\nThe same splitting behavior occurs for `Video + SecondaryAudio` requests, where video and secondary audio are routed as separate operations.\n\n### Signal Type Filtering\n\nAt each step of the route discovery process, the algorithm filters tie lines based on the requested signal type:\n\n- **Video request**: Only considers tie lines with the `video` flag set\n- **Audio request**: Only considers tie lines with the `audio` flag set\n- **AudioVideo request**: Routes audio and video separately, each following their respective filtering rules\n\nIf no tie line exists with the required signal type at any point in the chain, that path is rejected and the algorithm continues searching for an alternative route. If no valid path is found, the route request fails and no switching occurs.\n\nThis filtering ensures that incompatible signal types never interfere with routing decisions. For example, an audio-only cable will never be selected when routing video, preventing misconfiguration errors.\n\n---\n\n### Definitions\n\n#### Ports\n\nPorts are logical representations of the input and output ports on a device.\n\n#### Source\n\nA source is a device at the beginning of a signal chain. For example, a set-top box, or a camera. Source devices typically have only output ports.\n\nSource devices in Essentials must implement `IRoutingOutputs` or `IRoutingSource`\n\n#### Midpoint\n\nA midpoint is a device in the middle of the signal chain. Typically a switcher, matrix or otherwise. Examples: DM chassis; DM-TX; DM-RMC; A video codec. These devices will have input and output ports.\n\nMidpoint devices must implement `IRoutingInputsOutputs`. Midpoints with switching must implement `IRouting`.\n\n#### Sink\n\nA sink is a device at the end of a full signal path. For example, a display, amplifier, encoder, etc. Sinks typically contain only input ports. They may or may not have switching, like a display with several inputs. Classes defining sink devices must implement `IRoutingSinkNoSwitching` or `IRoutingSinkWithSwitching`.\n\n#### Tie-line\n\nA tie-line is a logical representation of a physical cable connection between two devices. It has five properties that define how the tie-line connects two devices.\n\n##### How Tie Line Types Are Determined\n\nThe effective type of a tie line is determined by one of two methods:\n\n1. **Automatic (Recommended)**: When no `type` property is specified in configuration, the tie line's type is automatically calculated as the **intersection** of signal types supported by both the source and destination ports. This ensures only compatible signals are considered for routing.\n\n   Example: If a source port supports `AudioVideo` and the destination port supports `Audio`, the tie line will have type `Audio` (the only common type).\n\n2. **Manual Override**: When the `type` property is explicitly set, it overrides the automatic calculation. This is useful when the physical cable supports fewer signal types than both ports are capable of.\n\n   Example: Both ports support `AudioVideo`, but the cable only carries audio, so you set `\"type\": \"audio\"`.\n\n##### Validation\n\nAt startup, tie line configurations are validated to ensure:\n- Both ports exist on their respective devices\n- The source and destination ports have at least one common signal type\n- If a `type` override is specified, both ports must support that signal type\n\nInvalid tie lines will fail to build with descriptive error messages, preventing runtime routing issues.\n\n##### Signal Types\n\nTie lines support the following signal types:\n\n- `audio` - Audio-only signals\n- `video` - Video-only signals\n- `audioVideo` - Combined audio and video (automatically split during routing)\n- `secondaryAudio` - Secondary audio channel (e.g., program audio separate from microphone audio)\n- `usbInput` - USB input signals\n- `usbOutput` - USB output signals\n\nThe `type` property determines which signals can travel through the tie line. During route discovery, only tie lines matching the requested signal type will be considered as valid paths.\n\n**Note**: In most cases, you should omit the `type` property and let the system automatically calculate it from the port capabilities. Only use it when you need to restrict the tie line to fewer signal types than the ports support or when needed for clarity.\n\n##### Configuration Examples\n\n**Example 1: Automatic type calculation (recommended)**\n\nConnecting an HDMI cable between devices that both support audio and video. The `type` property is omitted, so the tie line will automatically support `AudioVideo`:\n\n```json\n{\n  \"sourceKey\": \"ciscoSparkPlusCodec-1\",\n  \"sourcePort\": \"HdmiOut1\",\n  \"destinationKey\": \"display-1\",\n  \"destinationPort\": \"HdmiIn1\"\n}\n```\n\n**Example 2: Type override for cable limitations**\n\nBoth devices support `AudioVideo`, but the physical cable only carries audio. The `type` property restricts routing to audio only:\n\n```json\n{\n  \"sourceKey\": \"dmSwitcher-1\",\n  \"sourcePort\": \"audioVideoOut1\",\n  \"destinationKey\": \"amplifier-1\",\n  \"destinationPort\": \"audioVideoIn1\",\n  \"type\": \"audio\"\n}\n```\n\n**Example 3: Mismatched port types (automatically handled)**\n\nSource only supports audio, destination supports both. No `type` needed—the tie line will automatically be `Audio`:\n\n```json\n{\n  \"sourceKey\": \"audioProcessor-1\",\n  \"sourcePort\": \"audioOut1\",\n  \"destinationKey\": \"dmSwitcher-1\",\n  \"destinationPort\": \"audioVideoIn1\"\n}\n```\n\n**Invalid Example: Incompatible types**\n\nThis configuration will **fail validation** at startup because the ports have no common signal types:\n\n```json\n{\n  \"sourceKey\": \"audioProcessor-1\",\n  \"sourcePort\": \"audioOut1\",\n  \"destinationKey\": \"display-1\",\n  \"destinationPort\": \"hdmiIn1\",\n  \"type\": \"video\"\n}\n```\nError: `\"Override type 'Video' is not supported by source port 'audioOut1' (type: Audio)\"`\n\n### Interfaces\n\nTodo: Define Interfaces IRouting, IRoutingOutputs, IRoutingInputs\n"
  },
  {
    "path": "docs/docs/technical-docs/Debugging.md",
    "content": "# Methods of Debugging\n\n1. You can use Visual Studio step debugging\n   - Pros:\n     - Detailed real time debugging into with breakpoints and object inspection\n   - Cons:\n     - Doesn't really work remotely\n     - On processors with Control Subnet, you must be connected to the CS interface to use step debugging. Often not practical or possible.\n     - No logging\n     - Using breakpoints stops the program and can interrupt system usage\n     - Requires the running application to be built in debug mode, not release mode\n2. You can use the Debug class features build into the PepperDash.Core library.\n   - Pros:\n     - Can be easily enabled from console\n     - Allows for setting the level of verbosity\n     - Works when troubleshooting remotely and doesn't require a connection to the CS interface of the processor.\n     - Allows for logging to the Crestron error log or a custom log stored on removable media\n     - Works regardless of build type setting (debug/release)\n     - Can easily identify which class instance is generating console messages\n     - Can use console commands to view the state of public properties on devices\n     - Can use console commands to call methods on devices\n     - Doesn't stop the program\n   - Cons:\n     - No detailed object inspection in real time\n     - Only prints console statements already in code\n     - When enabled at the highest level of verbosity, it can produce a significant amount of data in console. Can be hard to find messages easily.\n     - No current mechanism to filter messages by device. (can be filtered by 3rd party tools easily, though)\n     - Not very effective in debugging applications running on the VC-4 platform as only log messages get printed to the Syslog\n\n## How to use the PepperDash.Core Debug Class\n\nThe majority of interaction is done via console, preferably via an SSH session through Crestron Toolbox, PuTTy or any other suitable application.\n\nIn code, the most useful method is `Debug.Console()` which has several overloads. All variations take an integer value for the level (0-2) as the first argument. Level 0 will ALWAYS print. Level 1 is for typical debug messages and level 2 is for verbose debugging. In cases where the overloads that accept a `Debug.ErrorLogLevel` parameter are used, the message will ALWAYS be logged, but will only print to console if the current debug level is the same or higher than the level set in the `Debug.Console()` statement.\n\nAll statements printed to console are prefixed by a timestamp which can be greatly helpful in debugging order of operations.\n\n```cs\n// The most basic use, sets the level (0) and the message to print.\nDebug.Console(0, \"Hello World\");\n// prints: [timestamp]App 1:Hello World\n\n// The string parameter has a built in string.Format() that takes params object[] items\nstring world = \"World\";\nDebug.Console(0, \"Hello {0}\", world);\n// prints: [timestamp]App 1:Hello World\n\n// This overload takes an IKeyed as the second parameter and the resulting statement will\n// print the Key of the device in console to help identify the class instance the message\n// originated from\nDebug.Console(0, this, \"Hello World\");\n// prints: [timestamp]App 1:[deviceKey]Hello World\n\n// Each of the above overloads has a corresponding variant that takes an argument to indicate\n// the level of error to log the message at as well as printing to console\nDebug.Console(0, Debug.ErrorLogLevel.Notice, \"Hello World\");\n// prints: [timestamp]App 1:Hello World\n```\n\n## Console Commands\n\n### General Console Commands\n\nBelow are is a non-exhaustive list of some of the Essentials specific console commands that allow interaction with the application at runtime.\n\n### `help user`\n\nWill print the available console commands for each program slot.  Console commands can be added and removed dynamically by Essentials and may vary by the version of Essentials that is running.  This is the best place to start to determine the available commands registered for each instance of Essentials running on a processor.\n\n### `reportversions:[slot]`\n\nWill print the running versions of all .dll libraries.  Useful for determining the exact build version of the Essentials application and all plugins\n\n### `gettypes:[slot] [searchString(optional)]`\n\nThe `searchString` value is an optional parameter to filter the results.\n\nWill print all of the valid `type` values registered in the `DeviceFactory` for the running Essentials application.  This helps when generating config structure and defining devices.  Device types added by plugins will also be shown.\n\n### `showconfig:[slot]`\n\nWill print out the merged config object\n\n### `donotloadonnextboot:[slot] [true/false]`\n\nWhen the value is set to true, Essentials will pause when starting up, to allow for a developer to attach to the running process from an IDE for purposes of step debugging.  Once attached, issuing the command `go:[slot]` will cause the configuration file to be read and the program to initialize.  This value gets set to false when the `go` command is issues.\n\n### DeviceManager Console Commands\n\nThe following console commands all perform actions on devices that have been registered with the `PepperDash.Essentials.Core.DeviceManager` static class\n\n### `Appdebug:[slot][0-2]`\n\nGets or sets the current debug level where 0 is the lowest setting and 2 is the most verbose\n\n### `getjoinmap:[slot] [bridgeKey][deviceKey (optional)]\n\nFor use with SIMPL Bridging.  Prints the join map for the specified bridge.  If a device key is specified, only the joins for that device will be printed.\n\nExample:\n\n```sh\nRMC3>appdebug:1 // Gets current level\nRMC3>AppDebug level = 0\n\nRMC3>appdebug:1 1 // Sets level to 1 (all messages level 1 or lower will print)\nRMC3>[Application 1], Debug level set to 1\n```\n\n### `Devlist:[slot]`\n\nGets the current list of devices from `DeviceManager`\n\nPrints in the form [deviceKey] deviceName\n\nExample:\n\n```sh\n// Get the list of devices for program 1\nRMC3>devlist:1\n\nRMC3>[16:34:05.819]App 1:28 Devices registered with Device Mangager:\n[16:34:05.834]App 1:  [cec-1] Tx 5 cec 1\n[16:34:05.835]App 1:  [cec-1-cec]\n[16:34:05.835]App 1:  [cec-5] Rmc 1 cec 1\n[16:34:05.836]App 1:  [cec-5-cec]\n[16:34:05.836]App 1:  [cec-6] Dm Chassis In 1 cec 1\n[16:34:05.837]App 1:  [cec-6-cec]\n[16:34:05.837]App 1:  [cec-7] Dm Chassis Out 1 cec 1\n[16:34:05.838]App 1:  [cec-7-cec]\n[16:34:05.838]App 1:  [comm-1] Generic comm 1\n[16:34:05.838]App 1:  [comm-1-com]\n[16:34:05.839]App 1:  [comm-2] Rmc comm 1\n[16:34:05.839]App 1:  [comm-2-com]\n[16:34:05.840]App 1:  [comm-3] Rmc comm 2\n[16:34:05.840]App 1:  [comm-3-com]\n[16:34:05.841]App 1:  [dmMd8x8-1] DM-MD8x8 Chassis 1\n[16:34:05.842]App 1:  [dmRmc100C-1] DM-RMC-100-C Out 3\n[16:34:05.843]App 1:  [dmRmc200C-1] DM-RMC-200-C Out 2\n[16:34:05.843]App 1:  [dmRmc4kScalerC-1] DM-RMC-4K-SCALER-C Out 1\n[16:34:05.844]App 1:  [dmTx201C-1] DM-TX-201C 1\n[16:34:05.845]App 1:  [eisc-1A]\n[16:34:05.845]App 1:  [gls-odt-1] GLS-ODT-CN 1\n[16:34:05.846]App 1:  [gls-oir-1] GLS-OIR-CN 1\n[16:34:05.846]App 1:  [processor]\n[16:34:05.847]App 1:  [ssh-1] Generic SSH 1\n[16:34:05.847]App 1:  [ssh-1-ssh]\n[16:34:05.848]App 1:  [systemMonitor]\n[16:34:05.848]App 1:  [tcp-1] Generic TCP 1\n[16:34:05.849]App 1:  [tcp-1-tcp]\n```\n### `Setdevicestreamdebug:[slot][devicekey][both/rx/tx/off]`\n\nEnables debug for communication on a single device\n\nExample:\n\n```sh\nPRO3>setdevicestreamdebug:1 lights-1-com both\n\n[13:13:57.000]App 1:[lights-1-com] Sending 4 characters of text: 'test'\n\nPRO3>setdevicestreamdebug:1 lights-1-com off\n```\n\n### `Devprops:[slot][devicekey]`\n\nGets the list of public properties on the device with the corresponding `deviceKey`\n\nExample:\n\n```sh\n// Get the properties on the device with Key 'cec-1-cec'\n// This device happens to be a CEC port on a DM-TX-201-C's HDMI input\nRMC3>devprops:1 cec-1-cec\n[\n  {\n    \"Name\": \"IsConnected\",\n    \"Type\": \"Boolean\",\n    \"Value\": \"True\",\n    \"CanRead\": true,\n    \"CanWrite\": false\n  },\n  {\n    \"Name\": \"Key\",\n    \"Type\": \"String\",\n    \"Value\": \"cec-1-cec\",\n    \"CanRead\": true,\n    \"CanWrite\": true\n  },\n  {\n    \"Name\": \"Name\",\n    \"Type\": \"String\",\n    \"Value\": \"\",\n    \"CanRead\": true,\n    \"CanWrite\": true\n  },\n  {\n    \"Name\": \"Enabled\",\n    \"Type\": \"Boolean\",\n    \"Value\": \"False\",\n    \"CanRead\": true,\n    \"CanWrite\": true\n  }\n]\n\nRMC3>\n\n```\n\n### `Devmethods:[slot][devicekey]`\n\nGets the list of public methods available on the device\n\nExample:\n\n```sh\n// Get the methods on the device with Key 'cec-1-cec'\nRMC3>devmethods:1 cec-1-cec\n[\n  {\n    \"Name\": \"SendText\",\n    \"Params\": [\n      {\n        \"Name\": \"text\",\n        \"Type\": \"String\"\n      }\n    ]\n  },\n  {\n    \"Name\": \"SendBytes\",\n    \"Params\": [\n      {\n        \"Name\": \"bytes\",\n        \"Type\": \"Byte[]\"\n      }\n    ]\n  },\n  {\n    \"Name\": \"SimulateReceive\",\n    \"Params\": [\n      {\n        \"Name\": \"s\",\n        \"Type\": \"String\"\n      }\n    ]\n  },\n  //... Response abbreviated for clarity ...\n]\n\nRMC3>\n```\n\n### `Devjson:[slot][json formatted object {\"devicekey\", \"methodname\", \"params\"}]`\n\nUsed in conjunction with devmethods, this command allows any of the public methods to be called from console and the appropriate arguments can be passed in to the method via a JSON object.\n\nThis command is most useful for testing without access to hardware as it allows both simulated input and output for a device.\n\nExample:\n\n```sh\n// This command will call the SendText(string text) method on the\n// device with the Key 'cec-1-cec' and pass in \"hello world\" as the\n// argument parameter.  On this particular device, it would cause\n// the string to be sent via the CEC Transmit\nRMC3>devjson:1 {\"deviceKey\":\"cec-1-cec\", \"methodName\":\"SendText\", \"params\": [\"hello world\\r\"]}\n\n// This command will call SimulateReceive(string text) on the device with Key 'cec-1-cec'\n// This would simulate receiving data on the CEC port of the DM-TX-201-C's HDMI input\nRMC3>devjson:1 {\"deviceKey\":\"cec-1-cec\", \"methodName\":\"SimulateReceive\", \"params\": [\"hello citizen of Earth\\r\"]}\n```\n\nFor additional examples, see this [file](https://github.com/PepperDash/Essentials/blob/main/devjson%20commands.json).\n"
  },
  {
    "path": "docs/docs/technical-docs/Feedback-Classes.md",
    "content": "# Feedback classes\n\n***\n* [YouTube Video - Using Feedbacks in PepperDash Essentials](https://youtu.be/5GQVRKbD9Rk)\n***\n\nThe various Feedback classes are like \"signals\". They can enable various events, and are designed to be used where we need small data events to be sent without requiring custom handlers.\n\n## Why Feedbacks?\n\nWe have been writing \"code\" in an environment, Simpl, for years and have taken for granted the power that signals in that environment give us. With the release of the ability to develop in C#, we have been handed a massive set of tools to potentially make our lives better, but because of the age and limited scope of the .NET 3.5 Compact Framework, many of the things that have been very easy to do in the past have become challenging or bulky to write. Crestron classes have things called \"Sigs\", which are a less-functional version of the signal that we used in Simpl, but we have no ability to use our own Sigs around our own classes. This forces us to break out of the constraints and mindset of Simpl programming, but simultaneously keeps us partially bound to the \"old way\" of doing things.\n\nSignals as we have known them since Simpl came around are great. They allow a certain type of functional programming to be built, where things operate in solutions, and we are given a whole set of behaviors that we don't really have to think about: Something goes high, the next thing responds, something else happens, etc. With our older C# framework, it is most straightforward (and least-flexible) to take Sig transitions and handle them using very-flat and bulky coding techniques: Switch/case blocks, if/else blocks, slow dictionaries... In the Essentials environment (and in many other frameworks) these methods quickly reveal their flaws.\n\nEnter the Feedback. We want to define simple events that can be attached to various things - TP Sigs, EISC, event handlers - and maintain their own state. This simplifies the interface to various device classes, and allows us to define functional, simple classes with well-defined means of connecting them together.\n\n### Feedbacks are similar to signals\n\nFeedbacks can:\n\n- Fire an event (OutputChange)\n- Be linked to one or more matching Crestron Sigs and update those Sigs\n- May contain complex computations to define the output value\n- Be put into test mode and have their value function overridden\n\nA Feedback is defined on a class using a C# construct called a `Func`. A `Func` is a small operation that returns a single value and is typically written in a lambda. The operation/expression in the `Func` is calculated when FireUpdate() is called on the Feedback. The result is then available for all objects listening to this Feedback.\n\n[Func documentation (MSDN)](<https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx>)\n\n#### Creating Feedbacks\n\nThe following `IntFeedback` returns the value of the `_VolumeLevel` field in this display class:\n\n```cs\npublic class MyDisplay\n{\n    public IntFeedback VolumeLevelFeedback { get; private set; }\n\n...\n\n    public MyDisplay(...)\n    {\n        VolumeLevelFeedback = new IntFeedback(() => { return _VolumeLevel; });\n\n        ...\n```\n\nThis BoolFeedback, adapted from the DmTx201Controller class, defines the `Func` first, and then creates the BoolFeedback using that `Func`. The value returned is true if the input is the digital-HDMI connection, and the TX hardware's VideoAttributes.HdcpActiveFeedback is true as well.\n\n```cs\npublic class MyTx\n{\n    public BoolFeedback HdcpActiveFeedback { get; private set; }\n\n    Func HdcpActiveFeedbackFunc = () =>\n        ActualVideoInput == DmTx200Base.eSourceSelection.Digital\n        && tx.HdmiInput.VideoAttributes.HdcpActiveFeedback.BoolValue,\n\n...\n\n    public MyTx(...)\n    {\n        HdcpActiveFeedback = new BoolFeedback(HdcpActiveFeedbackFunc);\n\n        ...\n```\n\n#### Triggering Feedback\n\nIn your classes, when you need to update the objects listening to a Feedback, you will call MyFeedback.FireUpdate() inside your class. This will trigger the evaluation of the Func value, update any linked Sigs, and fire the OutputChange event.\n\n```cs\nint _VolumeLevel;\n\nvoid ComDataChanged(string data) // volume=77\n{\n    if(data.StartsWith(\"volume=\"))\n    {\n        _VolumeLevel = MyParseVolumeMethod(data); // get the level, 77\n        VolumeLevelFeedback.FireUpdate(); // all listeners updated\n\n```\n\n#### Using Feedbacks\n\nFeedbacks of the various types have BoolValue, IntValue, UShortValue, and StringValue properties that return the current value of the Feedback.\n\n```cs\nif (MyTxDevice.HdcpActiveFeedback.BoolValue)\n{\n    ... do something that needs to happen when HDCP is active ...\n```\n\nFeedbacks all share an OutputChange event, that fires an event with an empty EventArgs object. The event handler can go get the appropriate \\*Value property when the event fires. The example below is a bit contrived, but explains the idea.\n\n```cs\n    ...\n    MyDisplayDevice.VolumeLevelFeedback.OutputChange += MyDisplayVolumeHandler;\n\n    ...\n}\n\nvoid MyDisplayVolumeHandler(object o, EventArgs a)\n{\n    MobileControlServer.VolumeLevel = MyDisplayDevice.VolumeLevelFeedback.IntValue;\n```\n\nFeedbacks also have a LinkInputSig(\\*InputSig sig) method that can directly trigger one or more Sigs on a Crestron device, without requiring an event handler. This is very useful for attaching states of our devices to Crestron touchpanels or EISCs, for example. The BoolFeedback class also has a LinkComplementInputSig(BoolInputSig sig) method that will invert the BoolFeedback's value to one or more attached Sigs.\n\nAs well as updating upon change, the Feedback will set the Sig's value to the Feedback value upon calling the LinkInputSig method. This eliminates the need to walk through an object, property-by-property, and update several Sig values - as well as setting up to watch those values for changes. It is all handled in one step.\n\n```cs\npublic class MyClass\n{\n    Tsw760 MyTp;\n\n    MyDisplay Display;\n\n    HookUpSigsMethod()\n    {\n        ...\n\n        // changes to VolumeLevelFeedback will automatically propagate to UShortInputSig 123\n        // changes to HdcpActiveFeedback will propagate to BoolInputSig 456\n        // and these two panel Sigs are updated immediately as well.\n        Display.VolumeLevelFeedback.LinkInputSig(MyTp.UshortInput[123]);\n        MyHdcpDevice.HdcpActiveFeedback.LinkInputSig(MyTp.BoolInput[456]);\n```\n"
  },
  {
    "path": "docs/docs/technical-docs/Glossary-of-Terms.md",
    "content": "# Glossary of Terms\n\n**Assembly**\n An assembly is a file that is automatically generated by the compiler upon successful compilation of every . NET application. It can be either a Dynamic Link Library or an executable file. It is generated only once for an application and upon each subsequent compilation the assembly gets updated.\n\n**Device**\nA base class, defined in the PepperDash.Core library (`PepperDash.Core.Device`).  It can represent a physical device, or a virtual device or behaviour.  Generally, most new classes defined in the Essentials ecosystem should derive from Device.\n\n**DeviceManager**\nA static class (`PepperDash.Core.Essentials.DeviceManager`) that contains an unordered collection of Devices.  Devices are added/registered to the DeviceManager and later can be retrieved as references by Key.\n\n**Essentials Application**\nA Crestron SIMPL# Pro application that is made up of the Essentials Framework and any optionally any number of Essentials Plugins\n\n**Essentials Framework**\nThe collection of core libraries that make up the framework\n\n**Essentials Plugins**\nSIMPL# Pro libraries that reference the Essentials Framework and are loaded at runtime to add or extend functionality\n\n**IKeyed**\nAn important interface defined in PepperDash.Core that requires a string property named Key, whose value must be unique.\n\n**PepperDash.Core**\nA SIMPL# utility library referenced by Essentials Framework.\n"
  },
  {
    "path": "docs/docs/technical-docs/Plugins.md",
    "content": "# What are Essentials Plugins?\n\n**Note : this entry updates a deprecated method - for information related to that deprecated method, see [Plugins - Deprecated](~/docs/Plugins-Deprecated.md)**\n\n***\n* [YouTube Video - Loading Plugins in PepperDash Essentials](https://youtu.be/NA64iyNNAgE)\n* [YouTube Video - Build Your Own Plugin, Part 1](https://youtu.be/m2phC8g3Kfk)\n* [YouTube Video - Build Your Own Plugin, Part 2](https://youtu.be/2_PrWRk6Gy0)\n***\n\nPlugins are SIMPL# Pro libraries that reference the Essentials Framework and can be loaded into an Essentials Application at runtime to extend functionality beyond what the Essentials Framework provides on its own.\n\n## Why Use Plugins?\n\nPlugins are a way to extend or add new functionality to the Essentials Application without having to modify the actual Framework. In most cases, a plugin can be written to support a new device or behavior. Using plugins also limits the scope of understanding needed to work within the Essentials Framework.\n\n## Should I use a Plugin?\n\nEssentials is meant to be a lightweight framework and an extensible basis for development. While some devices are included in the framework, mostly for the purposes of providing examples and developing and prototyping new device types, the bulk of new development is intended to take place in Plugins. Once a plugin adds new functionality that may be of benefit if shared across multiple plugins, it may make sense to port that common logic (base classes and/or interfaces) back into the framework to make it available to others. The thrust of future Essentials development is targeted towards building a library of plugins.\n\n## How do Plugins Work?\n\nOne or more plugins can be loaded to the /user/ProgramX/plugins as .dlls or .cplz packages. When the Essentials Application starts, it looks for any .cplz files, unzips them and then iterates any .dll assemblies in that folder and loads them. Once the plugin assemblies are loaded the Essentials Application will then attempt to load a configuration file and construct items as defined in the file. Those items can be defined in either the Essentials Framework or in any of the loaded plugin assemblies.\n\n![Architecture drawing](~/docs/images/Plugin%20Load%20Sequence.png)\n\n## What Must be Implemented in a Plugin for it to Work?\n\nAll plugin assemblies must contain a class which inherits from ```EssentialsPluginDeviceFactory<T>```, where ```<T>``` is a class which inherits from ```PepperDash.Essentials.Core.EssentialsDevice```\n\nWithin this class, we will define some metadata for the plugin and define which constructor to use\nfor instantiating each class as defined by type.\n\nNote that multiple types can be loaded from the same plugin.\n\n```cs\nusing System;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Core;\nusing System.Collections.Generic;\n\nnamespace MyPlugin\n{\n    public class SomeDeviceFactory : EssentialsPluginDeviceFactory<SomeDevice>\n    {\n        // This method defines metadata for the devices in the plugin\n        public SomeDeviceFactory()\n        {\n            // This string is used to define the minimum version of the\n            // Essentials Framework required for this plugin\n            MinimumEssentialsFrameworkVersion = \"1.5.0\";\n\n            // This string is used to define all valid type names for\n            // this plugin.  This string is case insensitive\n            TypeNames = new List<string> { \"SomeDevice\" , \"ThisNewDevice\" };\n        }\n\n        // This method instantiates new devices for the defined type\n        // within the plugin\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            // Deserialize the json properties for the loaded type to a new object\n            var props = dc.Properties.ToObject<SomeDeviceProperties>();\n\n            // Return a newly instantiated device using the desired constructor\n            // If no valid property data exists, return null with console print\n            // describing the failure.\n            if (props == null)\n            {\n                Debug.Console(0, \"No valid property data for 'SomeDevice' - Verify Configuration.\");\n                Debug.Console(0, dc.Properties.ToString());\n                return null;\n            }\n            return new SomeDevice(dc.Key, dc.Name, props);\n        }\n    }\n\n    public class OtherDeviceFactory : EssentialsPluginDeviceFactory<OtherDevice>\n    {\n        // This method defines metadata for the devices in the plugin\n        public OtherDeviceFactory()\n        {\n            // This string is used to define the minimum version of the\n            // Essentials Framework required for this plugin\n            MinimumEssentialsFrameworkVersion = \"1.5.0\";\n\n            // This string is used to define all valid type names for\n            // this plugin.  This string is case insensitive\n            TypeNames = new List<string> { \"OtherDevice\", \"ThisOtherDevice\" };\n        }\n\n        // This method instantiates new devices for the defined type\n        // within the plugin\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            // Deserialize the json properties for the loaded type to a new object\n            var props = dc.Properties.ToObject<OtherDeviceProperties>();\n\n            // Return a newly instantiated device using the desired constructor\n            // If no valid property data exists, return null with console print\n            // describing the failure.\n            if (props == null)\n            {\n                Debug.Console(0, \"No valid property data for 'OtherDevice' - Verify Configuration.\");\n                Debug.Console(0, dc.Properties.ToString());\n                return null;\n            }\n            return new OtherDevice(dc.Key, dc.Name, props);\n        }\n    }\n}\n```\n\n## SIMPL Bridging\n\nOptionally, if your plugin device needs to be able to bridge to a SIMPL program over EISC, and there isn't already an existing bridge class in the Essentials Framework, you can write a new bridge class in your plugin. However, in order for the Essentials Application to be able to us that new bridge, the bridge must implement the ```IBridgeAdvanced``` interface with the required ```LinkToApi()``` Extension method.\n\nIf you are writing code for a bridgeable device, you should be inheriting from ```EssentialsBridgeableDevice```, which inherits from the already-required ```EssentialsDevice``` and implements ```IBridgeAdvanced```.\n\nOften though, you may find that a bridge class already exists in the Essentials Framework that you can leverage. For example, if you were writing a plugin to support a new display model that isn't already in the Essentials Framework, you would define a class in your plugin that inherits from PepperDash.Essentials.Core.DisplayBase. If you're only implementing the standard display control functions such as power/input/volume control, then the existing bridge class `DisplayControllerBridge` can be used. If you needed to add additional functions to the bridge, then you would need to write your own bridge in the plugin.\n\nFor additional info see the [SIMPL-Bridging article](~/docs/SIMPL-Bridging.md).\n\n## Template Essentials Plugin Repository\n\nFork this repository when starting a new plugin. The template repository uses the essentials-builds repository as a submodule. This allows the plugin to reference a specific build version of Essentials. You must make sure that you checkout the correct build of the Essentials-Builds repo that contains any dependencies that your plugin may rely on.\n\n[Essentials Plugin Template Repository](https://github.com/PepperDash/EssentialsPluginTemplate)\n"
  },
  {
    "path": "docs/docs/technical-docs/Supported-Devices.md",
    "content": "# Essentials Framework Devices by Type\n\n## Cameras\n\n* VISCA protocol\n* Cisco (via codec)\n* Zoom (via Zoom Room)\n\n## Disc Player\n\n* Any IR disc player that implements standard RAD commands\n\n## Displays\n\n* Any IR display that implements standard RAD commands\n* Samsung MDC protocol (commercial)\n* NEC Professional series flat panel\n* Avocor VTF\n* Panasonic TH series flat panels\n* Panasonic Projectors [(via plugin)](https://github.com/PepperDash/epi-display-panasonic-projectors)\n* LG Commercial series [(via plugin)](https://github.com/PepperDash/epi-display-lg)\n* Generic CEC control via HDMI [(via plugin)](https://github.com/PepperDash/epi-generic-cec-display)\n* Crestron Certified Driver Display [(via plugin)](https://github.com/batourin/epi-display-ccd)\n\n## Lighting/Shading\n\n* Lutron Quantum\n* Somfy Shades (relay control)\n\n## Power Controllers\n\n* Digital Logger\n\n## Set Top Boxes\n\n* Any IR set top box that implements standard RAD commands\n\n## Streaming Players\n\n* AppleTV (IR)\n* Roku (IR)\n\n## Video Codecs\n\n* Cisco CE series (C/SX/RoomKit)\n* Zoom Room\n\n## DSPs / Audio Codecs\n\n* BiAmp Tesira [(via plugin](https://github.com/PepperDash/epi-dsp-tesira)\n\n## Crestron Devices\n\n* AM-200/300 Airmedia\n* All DM Chassis (8x8 * 128x128)\n* All DM input/output cards\n* All DMPS Processors\n* All DM Transmitter models (with COM/IR/Relay/CEC port access)\n* All DM Receiver models (with COM/IR/Relay/CEC port access)\n* DGE-100\n* DM-DGE-200-C\n* DIN-8SW8\n* CEN-IO-DIGIN-104\n* CEN-RFGWEX/GWEXER\n* GLS-ODT/OIR-C-CN Occupancy Sensors\n* TSW-XXX series touchpanels\n* XPanel for SmartGraphics\n* Fusion Room and Assets\n"
  },
  {
    "path": "docs/docs/toc.yml",
    "content": "- name: Get Started With Essentials\n- href: ../index.md\n- href: Get-started.md \n- name: How-to's\n  items:\n  - name: How to add an article or doc page\n    href: how-to/how-to-add-docs.md\n- name: Usage\n  items:\n  - href: usage/Standalone-Use.md    \n  - href: usage/SIMPL-Bridging-Updated.md\n    items:\n    - name: Join Maps\n      href: usage/JoinMaps.md\n    - name: Bridging to Hardware Resources\n      href: usage/Bridging-To-Hardware-Resources.md\n      items:\n      - name: GenericComm Bridging\n        href: usage/GenericComm.md\n      - name: RelayOutput Bridging\n        href: usage/RelayOutput.md\n      - name: Digital Input Bridging\n        href: usage/DigitalInput.md\n      - name: IR Driver Bridging\n        href: usage/IR-Driver-Bridging.md\n- name: Technical documentation\n  items:    \n    - href: technical-docs/Arch-summary.md\n    - name: Devices and DeviceManager\n      href: technical-docs/Arch-1.md\n    - name: Configurable lifecycle\n      href: technical-docs/Arch-lifecycle.md\n    - name: Activation phases\n      href: technical-docs/Arch-activate.md\n    - name: More\n      href: technical-docs/Arch-topics.md\n    - name: Plugins\n      href: technical-docs/Plugins.md\n    - name: Communication Basics\n      href: technical-docs/Communication-Basics.md\n    - name: Debugging\n      href: technical-docs/Debugging.md\n    - name: Feedback Classes\n      href: technical-docs/Feedback-Classes.md\n    - name: Connection Based Routing\n      href: technical-docs/Connection-Based-Routing.md\n    - name: Configuration Structure\n      href: technical-docs/ConfigurationStructure.md\n    - name: Supported Devices\n      href: technical-docs/Supported-Devices.md\n    - name: Glossary of Terms\n      href: technical-docs/Glossary-of-Terms.md"
  },
  {
    "path": "docs/docs/usage/Bridging-To-Hardware-Resources.md",
    "content": "# Bridging to Hardware and Network Resources\n\nOne of the most powerful features of Essentials is the ability to bridge SIMPL to and hardware resource to any piece of equipment instantiated inside of essentials.  You can bridge directly to a comport on the processor just as easily as you can to the comport of an instantiated DM device.  A simple change in the connection location of a display can be made with just a few keystrokes.  This isn't restricted to comports either.  Devices and direct connections can be linked to Digital Inputs, IR Ports, Relays, Comports, SSH, TCP/IP, UDP, and Cresnet resources.\n\n## Examples\n\nFollow the links below for examples of bridging to hardware and network resources.\n\n**[GenericComm Bridging](~/docs/usage/GenericComm.md)**\n\n**[RelayOutput Bridging](~/docs/usage/RelayOutput.md)**\n\n**[Digital Input Bridging](~/docs/usage/DigitalInput.md)**\n\n**[Card Frame Bridging](~/docs/CardFrame.md)**\n"
  },
  {
    "path": "docs/docs/usage/DigitalInput.md",
    "content": "# DigitalInput\n\nDigital Inputs can be bridged directly to SIMPL from any device that is both inlcuded within essentials and has a relay.\n\nConsider the following example.\n\n```JSON\n{\n    \"template\": {\n        \"roomInfo\": [\n            {}\n        ],\n        \"devices\": [\n            {\n                \"key\": \"processor\",\n                \"uid\": 0,\n                \"type\": \"pro3\",\n                \"name\": \"pro3\",\n                \"group\": \"processor\",\n                \"supportedConfigModes\": [\n                    \"compliance\",\n                    \"essentials\"\n                ],\n                \"supportedSystemTypes\": [\n                    \"hudType\",\n                    \"presType\",\n                    \"vtcType\",\n                    \"custom\"\n                ],\n                \"supportsCompliance\": true,\n                \"properties\": {}\n            },\n            {\n                \"key\": \"DigitalInput-1\",\n                \"uid\": 3,\n                \"name\": \"Digital Input 1\",\n                \"group\": \"api\",\n                \"type\": \"digitalInput\",\n                \"properties\": {\n                    \"portDeviceKey\" : \"processor\",\n                    \"portNumber\" : 1,\n                    \"disablePullUpResistor\" : true\n                }\n            },\n            {\n                \"key\": \"DigitalInput-2\",\n                \"uid\": 3,\n                \"name\": \"Digital Input 2\",\n                \"group\": \"api\",\n                \"type\": \"digitalInput\",\n                \"properties\": {\n                    \"portDeviceKey\" : \"processor\",\n                    \"portNumber\" : 2,\n                    \"disablePullUpResistor\" : true\n                }\n            },\n            {\n                \"key\": \"deviceBridge\",\n                \"uid\": 4,\n                \"name\": \"BridgeToDevices\",\n                \"group\": \"api\",\n                \"type\": \"eiscapiadv\",\n                \"properties\": {\n                    \"control\": {\n                        \"tcpSshProperties\": {\n                            \"address\": \"127.0.0.2\",\n                            \"port\": 0\n                        },\n                        \"ipid\": \"03\",\n                        \"method\": \"ipidTcp\"\n                    },\n                    \"devices\": [\n                        {\n                            \"deviceKey\": \"DigitalInput-1\",\n                            \"joinStart\": 1\n                        },\n                        {\n                            \"deviceKey\": \"DigitalInput-2\",\n                            \"joinStart\": 2\n                        }\n                    ]\n                }\n            }\n        ]\n    }\n}\n```\n\n## RelayOutput Configuration Explanation\n\nThis configuration is meant for a Pro3 device, and instantiates two relay ports and links them to an eisc bridge to another processor slot on ipid 3.  Let's break down the ```DigitalInput-1``` device.\n\n```JSON\n{\n    \"key\": \"DigitalInput-1\",\n    \"uid\": 3,\n    \"name\": \"Digital Input 1\",\n    \"group\": \"api\",\n    \"type\": \"digitalInput\",\n    \"properties\": {\n        \"portDeviceKey\" : \"processor\",\n        \"portNumber\" : 1,\n        \"disablePullUpResistor\" : true\n    }\n}\n```\n\n**```Key```**\n\nThe Key is a unique identifier for essentials.  The key allows the device to be linked to other devices also defined by key.  All Keys MUST be unique, as every device is added to a globally-accessible dictionary.  If you have accidentally utilized the same key twice, Essentials will notify you during startup that there is an issue with the device.\n\n**```Uid```**\n\nThe Uid is reserved for use with an PepperDash internal config generation tool, and is not useful to Essentials in any way.\n\n**```Name```**\n\nThe Name a friendly name assigned to the device.  Many devices pass this data to the bridge for utilization in SIMPL.\n\n**```Group```**\n\nUtilized in certain Essentials devices.  In this case, the value is unimportant.\n\n**```Type```**\n\nThe Type is the identifier for a specific type of device in Essentials.  A list of all valid types can be reported by using the consolecommand ```gettypes``` in Essentials.  In this case, the type is ```digitalInput```.  This type is valid for any instance of a Relay Output.\n\n**```Properties```**\n\nThese are the properties essential to the instantiation of the identified type.\n\n### Properties\n\nThere are two properties relevant to the instantiation of a relay device.\n\n**```portDeviceKey```**\n\nThis property maps to the ```key``` of the device upon which the relay resides.\n\n**```portNumber```**\n\nThis property maps to the number of the relay on the device you have mapped the relay device to.  Even if the device has only a single relay, ```portNumber``` must be defined.\n\n**```disablePullUpResistor```**\n\nThis is a boolean value, therefore it is a case-sensitive ```true``` or ```false``` utilized to determine if the pullup resistor on the digital input will be disabled or not.\n\n### The JoinMap\n\nThe joinmap for a ```digitalInput``` device is comprised of a single digital join.\n\n```cs\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    public class IDigitalInputJoinMap : JoinMapBaseAdvanced\n    {\n\n        [JoinName(\"InputState\")]\n        public JoinDataComplete InputState = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Room Email Url\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n\n        public IDigitalInputJoinMap(uint joinStart)\n            : base(joinStart, typeof(IDigitalInputJoinMap))\n        {\n        }\n    }\n}\n```\n\n```InputState``` is a digital join that represents the feedback for the associated Digital Input Device.  Its join is set to 1.\n"
  },
  {
    "path": "docs/docs/usage/GenericComm.md",
    "content": "# GenericComm\n\nOne of the most common scenarios in control system development is utilizing RS232 to connect to a device.  Essentials doesn't restrict you to connecting a native essentials device or plugin to the comport.  You can directly access the comport, and even set baudrates on the fly if you so desire.\n\nSimilarly you can instantiate one of several socket types in this manner and bridge them directly to SIMPL.\n\nConsider the following example.\n\n```JSON\n{\n    \"template\": {\n        \"roomInfo\": [\n            {}\n        ],\n        \"devices\": [\n            {\n                \"key\": \"processor\",\n                \"uid\": 0,\n                \"type\": \"pro3\",\n                \"name\": \"pro3\",\n                \"group\": \"processor\",\n                \"supportedConfigModes\": [\n                    \"compliance\",\n                    \"essentials\"\n                ],\n                \"supportedSystemTypes\": [\n                    \"hudType\",\n                    \"presType\",\n                    \"vtcType\",\n                    \"custom\"\n                ],\n                \"supportsCompliance\": true,\n                \"properties\": {}\n            },\n            {\n                \"key\": \"Comport-1\",\n                \"uid\": 3,\n                \"name\": \"Comport 1\",\n                \"group\": \"api\",\n                \"type\": \"genericComm\",\n                \"properties\": {\n                    \"control\": {\n                        \"method\": \"com\",\n                        \"comParams\": {\n                            \"hardwareHandshake\": \"None\",\n                            \"parity\": \"None\",\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 115200,\n                            \"dataBits\": 8,\n                            \"softwareHandshake\": \"None\",\n                            \"stopBits\": 1\n                        },\n                        \"controlPortNumber\": 1,\n                        \"controlPortDevKey\": \"processor\",\n                    }\n                }\n            },\n            {\n                \"key\": \"Comport-2\",\n                \"uid\": 3,\n                \"name\": \"Comport 2\",\n                \"group\": \"api\",\n                \"type\": \"genericComm\",\n                \"properties\": {\n                    \"control\": {\n                        \"method\": \"ssh\",\n                        \"tcpSshProperties\": {\n                            \"address\": \"192.168.1.57\",\n                            \"port\": 22,\n                            \"username\": \"\",\n                            \"password\": \"\",\n                            \"autoReconnect\": true,\n                            \"autoReconnectIntervalMs\": 10000\n                        }\n                    }\n                }\n            },\n            {\n                \"key\": \"deviceBridge\",\n                \"uid\": 4,\n                \"name\": \"BridgeToDevices\",\n                \"group\": \"api\",\n                \"type\": \"eiscapiadv\",\n                \"properties\": {\n                    \"control\": {\n                        \"tcpSshProperties\": {\n                            \"address\": \"127.0.0.2\",\n                            \"port\": 0\n                        },\n                        \"ipid\": \"03\",\n                        \"method\": \"ipidTcp\"\n                    },\n                    \"devices\": [\n                        {\n                            \"deviceKey\": \"Comport-1\",\n                            \"joinStart\": 1\n                        },\n                        {\n                            \"deviceKey\": \"Comport-2\",\n                            \"joinStart\": 3\n                        }\n                    ]\n                }\n            }\n        ]\n    }\n}\n```\n\n## GenericComm Configuration Explanation\n\nThis configuration is meant for a Pro3 device, and instantiates one comport and one SSH session and links them to an eisc bridge to another processor slot on ipid 3.  Let's break down the ```Comport-1``` device.\n\n```JSON\n{\n    \"key\": \"Comport-1\",\n    \"uid\": 3,\n    \"name\": \"Comport 1\",\n    \"group\": \"comm\",\n    \"type\": \"genericComm\",\n    \"properties\": {\n        \"control\": {\n            \"comParams\": {\n                \"hardwareHandshake\": \"None\",\n                \"parity\": \"None\",\n                \"protocol\": \"RS232\",\n                \"baudRate\": 115200,\n                \"dataBits\": 8,\n                \"softwareHandshake\": \"None\",\n                \"stopBits\": 1\n            },\n            \"controlPortNumber\": 1,\n            \"controlPortDevKey\": \"processor\",\n            \"method\": \"com\"\n        }\n    }\n}\n```\n\n**```Key```**\n\nThe Key is a unique identifier for essentials.  The key allows the device to be linked to other devices also defined by key.  All Keys MUST be unique, as every device is added to a globally-accessible dictionary.  If you have accidentally utilized the same key twice, Essentials will notify you during startup that there is an issue with the device.\n\n**```Uid```**\n\nThe Uid is reserved for use with an PepperDash internal config generation tool, and is not useful to Essentials in any way.\n\n**```Name```**\n\nThe Name a friendly name assigned to the device.  Many devices pass this data to the bridge for utilization in SIMPL.\n\n**```Group```**\n\nUtilized in certain Essentials devices.  In this case, the value is unimportant.\n\n**```Type```**\n\nThe Type is the identifier for a specific type of device in Essentials.  A list of all valid types can be reported by using the consolecommand ```gettypes``` in Essentials.  In this case, the type is ```genericComm```.  This type is valid for any instance of a serial-based communications channel such as a Serial Port, SSH, UDP, or standard TCP/IP Socket.\n\n**```Properties```**\n\nThese are the properties essential to the instantiation of the identified type.\n\n#### Control\n\nThe properties within this property are dependant on the type of genericComm you wish to instantiate.  There is one common property for control of any type, and that is ```method```.  The ```method``` property requires a string that maps to the following enumerations in Essentials :\n\n```cs\nnamespace PepperDash.Core\n{\n    // Summary:\n    //     Crestron Control Methods for a comm object\n    public enum eControlMethod\n    {\n        None = 0,\n        Com = 1,\n        IpId = 2,\n        IpidTcp = 3,\n        IR = 4,\n        Ssh = 5,\n        Tcpip = 6,\n        Telnet = 7,\n        Cresnet = 8,\n        Cec = 9,\n        Udp = 10,\n        UdpClient = 11,\n    }\n}\n```\n\nThese enumerations are not case sensitive.  Not all methods are valid for a ```genericComm``` device.  For a comport, the only valid type would be ```Com```.  For a direct network socket, valid options are ```Ssh```, ```Tcpip```, ```Telnet```, ```UdpClient```, and ```Udp```.\n\n##### ComParams\n\nA ```Com``` device requires a ```comParams``` object to set the properties of the comport.  The values of all properties are case-insensitive.\n\n```JSON\n{\n\"comParams\": {\n    \"hardwareHandshake\": \"None\",\n    \"parity\": \"None\",\n    \"protocol\": \"RS232\",\n    \"baudRate\": 115200,\n    \"dataBits\": 8,\n    \"softwareHandshake\": \"None\",\n    \"stopBits\": 1\n}\n```\n\n**Valid ```hardwareHandshake``` values are as follows**\n\n```sh\n\"None\"\n\"Rts\"\n\"Cts\"\n\"RtsCts\"\n```\n\n**Valid ```parity``` values are as follows**\n\n```sh\n\"None\"\n\"Even\"\n\"Odd\"\n\"Mark\"\n```\n\n**Valid ```protocol``` values are as follows**\n\n```sh\n\"RS232\"\n\"RS422\"\n\"RS485\"\n```\n\n**Valid ```baudRate``` values are as follows**\n\n```sh\n300\n600\n1200\n1800\n2400\n3600\n4800\n7200\n9600\n14400\n19200\n28800\n38400\n57600\n115200\n```\n\n**Valid ```dataBits``` values are as follows**\n\n```sh\n7\n8\n```\n\n**Valid ```softwareHandshake``` values are as follows**\n\n```sh\n\"None\"\n\"XON\"\n\"XONT\"\n\"XONR\"\n```\n\n**Valid ```stopBits``` values are as follows**\n\n```sh\n1\n2\n```\n\nAdditionally, a ```control``` object for a physical hardware port needs to map to that physical port.  This is accomplished by utilizing the ```controlPortDevKey``` and ```port``` properties.\n\n**```controlPortDevKey```**\n\nThis property maps to the ```key``` of the device upon which the port resides.\n\n**```port```**\n\nThis property maps to the number of the port on the device you have mapped the relay device to.  Even if the device has only a single port, ```port``` must be defined.\n\n##### TcpSshParams\n\nA ```Ssh```, ```TcpIp```, ```UdpClient```, or ```Udp``` device requires a ```tcpSshProperties``` object to set the propeties of the socket.\n\n```Json\n{\n    \"tcpSshProperties\": {\n        \"address\": \"192.168.1.57\",\n        \"port\": 22,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"autoReconnect\": true,\n        \"autoReconnectIntervalMs\": 10000\n    }\n}\n```\n\n**```address```**\n\nThis is the IP address, hostname, or FQDN of the resource you wish to open a socket to.  Use ```UdpClient``` for outbound UDP to a remote endpoint.  Use ```Udp``` when you need Essentials to bind a local UDP listener.\n\n**```port```**\n\nThis is the port you wish to utilize for the socket connection.  Certain protocols require certain ports - ```Ssh``` being ```22``` and ```Telnet``` being ```23```.\n\n**```username```**\n\nThis is the username (if required) for authentication to the device you are connecting to.  Typcally only required for ```Ssh``` connections.\n\n**```password```**\n\nThis is the password (if required) for authentication to the device you are connecting to.  Typcally only required for ```Ssh``` connections.\n\n**```autoreconnect```**\n\nThis is a boolean value, therefore it is a case-sensitive ```true``` or ```false``` utilized to determine if the socket will attempt to reconnect upon loss of connection.\n\n**```autoReconnectIntervalMs```**\n\nThis is the duration of time, in Miliseconds, that the socket will wait before discrete connection attempts if ```autoreconnect``` is set to true.\n\n##### The JoinMap\n\nThe join map for a generic comms device is fairly simple.  \n\n```cs\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    public class IBasicCommunicationJoinMap : JoinMapBaseAdvanced\n    {\n        [JoinName(\"TextReceived\")]\n        public JoinDataComplete TextReceived = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Text Received From Remote Device\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"SendText\")]\n        public JoinDataComplete SendText = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Text Sent To Remote Device\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"SetPortConfig\")]\n        public JoinDataComplete SetPortConfig = new JoinDataComplete(new JoinData() { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Set Port Config\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"Connect\")]\n        public JoinDataComplete Connect = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Connect\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"Connected\")]\n        public JoinDataComplete Connected = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Connected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"Status\")]\n        public JoinDataComplete Status = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n\n        public IBasicCommunicationJoinMap(uint joinStart)\n            : base(joinStart, typeof(IBasicCommunicationJoinMap))\n        {\n        }\n    }\n}\n```\n\n```TextReceived``` is a stream of strings received **FROM** the connected device.\n\n```SendText``` is for any strings you wish to send **TO** the connected device.\n\n```Connect``` connects to a remote socket device on the rising edge of the signal.\n\n```Connected``` represents the current connection state.  High for Connected, low for Disconnected.\n\n```Status``` is an analog value that is representative of the connection states as reported by the SIMPL TCP/IP socket symbol.\n\nAll of the preceeding joins are set to join ```1```.  The second serial input join is reserved for ```2```.  It allows you to send a ```comparams``` json object as a string, utilizing the same format mentioned previously in this document.  Doing so will override the configured comport specifications.\n"
  },
  {
    "path": "docs/docs/usage/IR-Driver-Bridging.md",
    "content": "## Legacy IR Driver Bridging\n\n```json\n{\n\t\"id\": \"1\",\n\t\"name\": \"Apple TV\",\n\t\"key\": \"appleTv-1\",\n\t\"type\": \"genericIrController\",\n\t\"uid\": 3,\n\t\"group\": \"devices\",\n\t\"properties\": {\n\t\t\"control\": {\n\t\t\t\"method\": \"ir\",\n\t\t\t\"irFile\": \"Apple_AppleTV_4th_Gen_Essentials.ir\",\n\t\t\t\"controlPortDevKey\": \"processor\",\n\t\t\t\"controlPortNumber\": \"1\"\n\t\t}\n\t}\n}\n```\n\n## Bridge Join Map IR Driver Bridging\n\n```json\n{\n\t\"id\": \"1\",\n\t\"name\": \"Apple TV\",\n\t\"key\": \"appleTv-1\",\n\t\"type\": \"genericIrController\",\n\t\"uid\": 3,\n\t\"group\": \"devices\",\n\t\"properties\": {\n\t\t\"control\": {\n\t\t\t\"method\": \"ir\",\n\t\t\t\"irFile\": \"Apple_AppleTV_4th_Gen_Essentials.ir\",\n\t\t\t\"controlPortDevKey\": \"processor\",\n\t\t\t\"controlPortNumber\": \"1\",\n\t\t\t\"useBridgeJoinMap\": true\n\t\t}\n\t}\n}\n```\n\nBoth methods will bridge the IR signals with `Standard Command` defined in the IR file.  \n\nThe `useBridgeJoinMap` property implements `GenericIrControllerJoinMap.cs` to standardized IR driver `Standard Command` signal joins.  This allows users to swap IR drivers that implement `Standard Command` while bridging IR signals consistently between drivers.  For example, when `useBridgeJoinMap` is present, channel up will be mapped to join-22 + device `joinstart` for any IR driver that has the signal marked as `Standard Command`.\n\n\n## GenericIrControllerJoinMap (Example)\n\n### Digitals\n\n| Join Number | Join Span | Description | Type                | Capabilities |\n| ----------- | --------- | ----------- | ------------------- | ------------ |\n| 1           | 1         | PLAY        | Digital             | FromSIMPL    |\n| 2           | 1         | STOP        | Digital             | FromSIMPL    |\n| 3           | 1         | PAUSE       | Digital             | FromSIMPL    |\n| 4           | 1         | FSCAN       | Digital             | FromSIMPL    |\n| 5           | 1         | RSCAN       | Digital             | FromSIMPL    |\n| 9           | 1         | POWER       | Digital             | FromSIMPL    |\n| 10          | 1         | 0           | Digital             | FromSIMPL    |\n| 11          | 1         | 1           | Digital             | FromSIMPL    |\n| 12          | 1         | 2           | Digital             | FromSIMPL    |\n| 13          | 1         | 3           | Digital             | FromSIMPL    |\n| 14          | 1         | 4           | Digital             | FromSIMPL    |\n| 15          | 1         | 5           | Digital             | FromSIMPL    |\n| 16          | 1         | 6           | Digital             | FromSIMPL    |\n| 17          | 1         | 7           | Digital             | FromSIMPL    |\n| 18          | 1         | 8           | Digital             | FromSIMPL    |\n| 19          | 1         | 9           | Digital             | FromSIMPL    |\n| 21          | 1         | ENTER       | Digital             | FromSIMPL    |\n| 22          | 1         | CH+         | Digital             | FromSIMPL    |\n| 23          | 1         | CH-         | Digital             | FromSIMPL    |\n| 27          | 1         | POWER_ON    | Digital             | FromSIMPL    |\n| 28          | 1         | POWER_OFF   | Digital             | FromSIMPL    |\n| 30          | 1         | LAST        | Digital             | FromSIMPL    |\n| 41          | 1         | BACK        | Digital             | FromSIMPL    |\n| 42          | 1         | GUIDE       | Digital             | FromSIMPL    |\n| 43          | 1         | INFO        | Digital             | FromSIMPL    |\n| 44          | 1         | MENU        | Digital             | FromSIMPL    |\n| 45          | 1         | UP_ARROW    | Digital             | FromSIMPL    |\n| 46          | 1         | DN_ARROW    | Digital             | FromSIMPL    |\n| 47          | 1         | LEFT_ARROW  | Digital             | FromSIMPL    |\n| 48          | 1         | RIGHT_ARROW | Digital             | FromSIMPL    |\n| 49          | 1         | SELECT      | Digital             | FromSIMPL    |\n| 54          | 1         | PAGE_UP     | Digital             | FromSIMPL    |\n| 55          | 1         | PAGE_DOWN   | Digital             | FromSIMPL    |\n| 61          | 1         | A           | Digital             | FromSIMPL    |\n| 62          | 1         | B           | Digital             | FromSIMPL    |\n| 63          | 1         | C           | Digital             | FromSIMPL    |\n| 64          | 1         | D           | Digital             | FromSIMPL    |\n\n### Analogs\n\n| Join Number | Join Span | Description | Type                | Capabilities |\n| ----------- | --------- | ----------- | ------------------- | ------------ |\n\n### Serials\n\n| Join Number | Join Span | Description | Type                | Capabilities |\n| ----------- | --------- | ----------- | ------------------- | ------------ |\n\n\n"
  },
  {
    "path": "docs/docs/usage/JoinMaps.md",
    "content": "# What is a Join Map?\n\nA join map is a class that defines the list of joins accessible to an `EssentialsBridgeableDevice` across an EISC Bridge.\n\n## Why use a Join Map?\n\nA join map is necessary to bridge joins across an EISC bridge from Essentials to a SIMPL program. Join maps can be overriden in a configuration as necessary, but by default each device has a standard join map that publishes joins as a function of the joinOffset property of the device within a given Essentials Bridge. Join maps are reusable and extensible. Several join maps for standard device types already exist within Essentials, and those can be utilized for plugin creation without creation of a new join map. Utilizing standard join maps allows you to create a consistent API between device types that allows switching of devices via config without any new SIMPL or SIMPL#Pro code being written.\n\n## How Join maps Work\n\nWhenever you instantiate a device and link that device to an EISC bridge utilizing your configuration in Essentials, the method `LinkToApi()` is called. This method matches various methods, feedbacks, and properties to joins on the EISC bridge in order to create a consistent API for communication to SIMPL.\n\nWhenever `LinkToApi()` is called, it creates a new instance of the device's joinMap class on demand. The constructor of that joinMap creates an object containing the join metadata, adds any configured join offsets to the standard join map, and adds all associated joins to a global join list that can be easily referenced from the command line.\n\nThere are several components for each join within a join map.\n\n```cs\n[JoinName(\"Online\")]\n        public JoinDataComplete Online = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Reports Online Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n```\n\n### Attribute\n\n```cs\n[JoinName(\"Online\")]\n```\n\nIf the attribute is present, the join data is added to the publically available list `Joins`. This can be used to \"prebuild\" functionality within a join map that you may not yet need. If you do not add this attribute (or simply comment it out), the join data will not be displayed whenever join data is printed using the `getjoinmap` command.\n\n### JoinData\n\n```cs\nJoinData() { JoinNumber = 1, JoinSpan = 1 };\n```\n\n`JoinData` contains the pertinent information for the bridge. JoinData contains the information that the bridge utilizes to create each associated connection from the EISC to the methods, properties, and feedbacks associated with a device.\n\n`JoinNumber` is the 1-based index of the join you wish to tie to a given method, property, or feedback. This join, combined with the offset defined in the brdige's configuration for a device, will give you the SIMPL EISC join linked to the given data.\n\n`JoinSpan` determines a number of associated joins. Perhaps you have a list of Camera Presets, or a list of inputs. You can create one single join map entry and define the span of all associated join types.\n\nA `JoinData` object with a `JoinNumber` of 11 and a `JoinSpan` of 10 and a `joinOffset` of 0 is associated with joins 11-20 on the EISC.\n\n### JoinMetadata\n\n```cs\nJoinMetadata() { Label = \"Reports Online Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }\n```\n\n`JoinMetadata` provides the data reported when the `getjoinmap` command is used.\n\n`Label` is the description of the what this join does.\n\n`JoinCapabilities` is represented by an enum defining the direction that the data is flowing for this join. Appropriate values are:\n\n```cs\npublic enum eJoinCapabilities\n    {\n        None = 0,\n        ToSIMPL = 1,\n        FromSIMPL = 2,\n        ToFromSIMPL = ToSIMPL | FromSIMPL\n    }\n```\n\n`JoinType` is represented by an enum defining the data type in SIMPL. Appropriate values are:\n\n```cs\npublic enum eJoinType\n    {\n        None = 0,\n        Digital = 1,\n        Analog = 2,\n        Serial = 4,\n        DigitalAnalog = Digital | Analog,\n        DigitalSerial = Digital | Serial,\n        AnalogSerial = Analog | Serial,\n        DigitalAnalogSerial = Digital | Analog | Serial\n    }\n```\n\n### JoinDataComplete\n\n```chsarp\nJoinDataComplete(JoinData data, JoinMetadata metadata);\n```\n\n`JoinDataComplete` represents the `JoinData` and the `JoinMetadata` in a single object. You can call an instance of `JoinDataComplete` to report any information about a specific join. In a device bridge, you would utilize the `JoinNumber` property to link a feature from the plugin to the EISC API.\n\n### Example Join Map\n\nThis is the join map for `IBasicCommunication` Devices\n\n```cs\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    public class IBasicCommunicationJoinMap : JoinMapBaseAdvanced\n    {\n        [JoinName(\"TextReceived\")]\n        public JoinDataComplete TextReceived = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Text Received From Remote Device\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"SendText\")]\n        public JoinDataComplete SendText = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Text Sent To Remote Device\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"SetPortConfig\")]\n        public JoinDataComplete SetPortConfig = new JoinDataComplete(new JoinData() { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Set Port Config\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"Connect\")]\n        public JoinDataComplete Connect = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Connect\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"Connected\")]\n        public JoinDataComplete Connected = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Connected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"Status\")]\n        public JoinDataComplete Status = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n\n        public IBasicCommunicationJoinMap(uint joinStart)\n            : base(joinStart, typeof(IBasicCommunicationJoinMap))\n        {\n        }\n    }\n}\n```\n\n### Returning Data on Join Maps\n\nA mechanism for printing join maps to console from a running Essentials program is built in to Essentials.\n\nGiven a single Generic Communication device with a `joinStart` of 11 and a key of \"Com-1\", defined on a bridge with a key of \"Bridge-1\" and IPID of A0, the command `getjoinmap Bridge-1 Com-1` would return:\n\n```sh\nJoin Map for device 'Com-1' on EISC 'Bridge-1':\nGenericComm:\n\nDigitals:\nFound 2 Digital Joins\nJoin Number: '11' | JoinSpan: '1' | Label : 'Connect' | Type: 'Digital' | Capabilities : 'FromSimpl'\nJoin Number: '11' | JoinSpan: '1' | Label : 'Connected' | Type: 'Digital' | Capabilities : 'ToSimpl'\nAnalogs:\nFound 1 Analog Joins\nJoin Number: '11' | JoinSpan: '1' | Label : 'Status' | Type: 'Analog' | Capabilities : 'ToSimpl'\nSerials:\nFound 3 Serial Joins\nJoin Number: '11' | JoinSpan: '1' | Label : 'Text Received From Remote Device' | Type: 'Serial' | Capabilities : 'FromSimpl'\nJoin Number: '11' | JoinSpan: '1' | Label : 'Text Sent To Remote Device' | Type: 'Serial' | Capabilities : 'ToSimpl'\nJoin Number: '12' | JoinSpan: '1' | Label : 'Set Port Config' | Type: 'Serial' | Capabilities : 'FromSimpl'\n```\n"
  },
  {
    "path": "docs/docs/usage/RelayOutput.md",
    "content": "# RelayOutput\n\nRelays can be bridged directly to SIMPL from any device that is both inlcuded within essentials and has a relay.\n\nConsider the following example.\n\n```JSON\n{\n    \"template\": {\n        \"roomInfo\": [\n            {}\n        ],\n        \"devices\": [\n            {\n                \"key\": \"processor\",\n                \"uid\": 0,\n                \"type\": \"pro3\",\n                \"name\": \"pro3\",\n                \"group\": \"processor\",\n                \"supportedConfigModes\": [\n                    \"compliance\",\n                    \"essentials\"\n                ],\n                \"supportedSystemTypes\": [\n                    \"hudType\",\n                    \"presType\",\n                    \"vtcType\",\n                    \"custom\"\n                ],\n                \"supportsCompliance\": true,\n                \"properties\": {}\n            },\n            {\n                \"key\": \"Relay-1\",\n                \"uid\": 3,\n                \"name\": \"Relay 1\",\n                \"group\": \"api\",\n                \"type\": \"relayOutput\",\n                \"properties\": {\n                    \"portDeviceKey\" : \"processor\",\n                    \"portNumber\" : 1\n                }\n            },\n            {\n                \"key\": \"Relay-2\",\n                \"uid\": 3,\n                \"name\": \"Relay 2\",\n                \"group\": \"api\",\n                \"type\": \"relayOutput\",\n                \"properties\": {\n                    \"portDeviceKey\" : \"processor\",\n                    \"portNumber\" : 2\n                }\n            },\n            {\n                \"key\": \"deviceBridge\",\n                \"uid\": 4,\n                \"name\": \"BridgeToDevices\",\n                \"group\": \"api\",\n                \"type\": \"eiscapiadv\",\n                \"properties\": {\n                    \"control\": {\n                        \"tcpSshProperties\": {\n                            \"address\": \"127.0.0.2\",\n                            \"port\": 0\n                        },\n                        \"ipid\": \"03\",\n                        \"method\": \"ipidTcp\"\n                    },\n                    \"devices\": [\n                        {\n                            \"deviceKey\": \"Relay-1\",\n                            \"joinStart\": 1\n                        },\n                        {\n                            \"deviceKey\": \"Relay-2\",\n                            \"joinStart\": 2\n                        }\n                    ]\n                }\n            }\n        ]\n    }\n}\n```\n\n## RelayOutput Configuration Explanation\n\nThis configuration is meant for a Pro3 device, and instantiates two relay ports and links them to an eisc bridge to another processor slot on ipid 3.  Let's break down the ```Relay-1``` device.\n\n```JSON\n{\n    \"key\": \"Relay-1\",\n    \"uid\": 3,\n    \"name\": \"Relay 1\",\n    \"group\": \"api\",\n    \"type\": \"relayOutput\",\n    \"properties\": {\n        \"portDeviceKey\" : \"processor\",\n        \"portNumber\" : 1\n    }\n}\n```\n\n**```Key```**\n\nThe Key is a unique identifier for essentials.  The key allows the device to be linked to other devices also defined by key.  All Keys MUST be unique, as every device is added to a globally-accessible dictionary.  If you have accidentally utilized the same key twice, Essentials will notify you during startup that there is an issue with the device.\n\n**```Uid```**\n\nThe Uid is reserved for use with an PepperDash internal config generation tool, and is not useful to Essentials in any way.\n\n**```Name```**\n\nThe Name a friendly name assigned to the device.  Many devices pass this data to the bridge for utilization in SIMPL.\n\n**```Group```**\n\nUtilized in certain Essentials devices.  In this case, the value is unimportant.\n\n**```Type```**\n\nThe Type is the identifier for a specific type of device in Essentials.  A list of all valid types can be reported by using the consolecommand ```gettypes``` in Essentials.  In this case, the type is ```relayOutput```.  This type is valid for any instance of a Relay Output.\n\n**```Properties```**\n\nThese are the properties essential to the instantiation of the identified type.\n\n### Properties\n\nThere are two properties relevant to the instantiation of a relay device.\n\n**```portDeviceKey```**\n\nThis property maps to the ```key``` of the device upon which the relay resides.\n\n**```portNumber```**\n\nThis property maps to the number of the relay on the device you have mapped the relay device to.  Even if the device has only a single relay, ```portNumber``` must be defined.\n\n### The JoinMap\n\nThe joinmap for a ```relayOutput``` device is comprised of a single digital join.\n\n```cs\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    public class GenericRelayControllerJoinMap : JoinMapBaseAdvanced\n    {\n\n        [JoinName(\"Relay\")]\n        public JoinDataComplete Relay = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Device Relay State Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n\n        public GenericRelayControllerJoinMap(uint joinStart)\n            : base(joinStart, typeof(GenericRelayControllerJoinMap))\n        {\n        }\n    }\n}\n```\n\n```Relay``` is a digital join that represents both the trigger and the feedback for the associated relay device.  Its join is set to 1.\n"
  },
  {
    "path": "docs/docs/usage/SIMPL-Bridging-Updated.md",
    "content": "# Use with SIMPL Windows\n\n***\n* [YouTube Video - SIMPL Windows in PepperDash Essentials](https://youtu.be/P2jNzsfpgJE)\n***\n\nEssentials allows for devices defined within the SIMPL# Pro application to be bridged to a SIMPL Windows application over Ethernet Intersystem Communication (EISC). This allows a SIMPL Windows program to take advantage of some of the features of the SIMPL# Pro environment, without requiring the entire application to be written in C#.\n\nSome of the main advantages are:\n\n1. The ability to instantiate devices from configuration.\n2. The ability to leverage C# concepts to handle data intensive tasks (Serialization/Deserialization of JSON/XML, cyrptography, etc.).\n3. The ability to reuse the same compiled SIMPL Windows program (regardless of target processor type) by offloading all the variables that may be room or hardware specific to Essentials.\n4. The ability to handle multiple communciation types generically without changing the SIMPL Program (TCP/UDP/SSH/HTTP/HTTPS/CEC, etc.)\n5. Much faster development cycle\n6. Reduced processor overhead\n7. Ability to easily share devices defined in Essentials between multiple other programs\n\n## Implementation\n\nBridges are devices that are defined within the devices array in the config file. They are unique devices with a specialized purpose: to act as a bridge between Essentials Devices and applications programmed traditionally in SIMPL Windows. This is accomplished by instantiating a Three Series Intersystem Communication symbol within the bridge device, and linking its Boolean/Ushort/String inputs and outputs to actions on one or multiple Essentials device(s). The definition for which joins map to which actions is defined within the device to be bridged to in a class that derives from JoinMapBase.\n\nLet's consider the following Essentials Configuration:\n\n```JSON\n{\n    \"template\": {\n        \"roomInfo\": [\n            {}\n        ],\n        \"devices\": [\n            {\n                \"key\": \"processor\",\n                \"uid\": 1,\n                \"type\": \"pro3\",\n                \"name\": \"PRO3 w/o cards\",\n                \"group\": \"processor\",\n                \"supportedConfigModes\": [\n                    \"essentials\"\n                ],\n                \"supportedSystemTypes\": [\n                    \"hudType\",\n                    \"presType\",\n                    \"vtcType\",\n                    \"custom\"\n                ],\n                \"supportsCompliance\": true,\n                \"properties\": {\n                    \"numberOfComPorts\": 6,\n                    \"numberOfIrPorts\": 8,\n                    \"numberOfRelays\": 8,\n                    \"numberOfDIOPorts\": 8\n                }\n            },\n            {\n                \"key\": \"panasonicDisplay01\",\n                \"type\": \"PanasonicThefDisplay\",\n                \"name\": \"Main Display\",\n                \"group\": \"displays\",\n                \"uid\": 2,\n                \"properties\": {\n                    \"id\": \"01\",\n                    \"inputNumber\": 1,\n                    \"outputNumber\": 1,\n                    \"control\": {\n                        \"comParams\": {\n                            \"hardwareHandshake\": \"None\",\n                            \"parity\": \"None\",\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 9600,\n                            \"dataBits\": 8,\n                            \"softwareHandshake\": \"None\",\n                            \"stopBits\": 1\n                        },\n                        \"controlPortNumber\": 1,\n                        \"controlPortDevKey\": \"processor\",\n                        \"method\": \"com\"\n                    }\n                }\n            },\n            {\n                \"key\": \"vtcComPort\",\n                \"uid\": 3,\n                \"name\": \"VTC Coms\",\n                \"group\": \"comm\",\n                \"type\": \"genericComm\",\n                \"properties\": {\n                    \"control\": {\n                        \"comParams\": {\n                            \"hardwareHandshake\": \"None\",\n                            \"parity\": \"None\",\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 38400,\n                            \"dataBits\": 8,\n                            \"softwareHandshake\": \"None\",\n                            \"stopBits\": 1\n                        },\n                        \"controlPortNumber\": 2,\n                        \"controlPortDevKey\": \"processor\",\n                        \"method\": \"com\"\n                    }\n                }\n            },\n            {\n                \"key\": \"deviceBridge\",\n                \"uid\": 4,\n                \"name\": \"BridgeToDevices\",\n                \"group\": \"api\",\n                \"type\": \"eiscApi\",\n                \"properties\": {\n                    \"control\": {\n                        \"tcpSshProperties\": {\n                            \"address\": \"127.0.0.2\",\n                            \"port\": 0\n                        },\n                        \"ipid\": \"03\",\n                        \"method\": \"ipidTcp\"\n                    },\n                    \"devices\": [\n                        {\n                            \"deviceKey\": \"panasonicDisplay01\",\n                            \"joinStart\": 1\n                        },\n                        {\n                            \"deviceKey\": \"vtcComPort\",\n                            \"joinStart\": 51\n                        }\n                    ]\n                }\n            }\n        ]\n    }\n}\n```\n\nWe have four Essentials Devices configured:\n\n1. Pro3 with a Key of \"processor\"\n\n1. Panasonic Display with a Key of \"panasonicDisplay01\"\n\n1. Com port with a Key of \"vtcComPort\"\n\n1. Bridge with a Key of \"deviceBridge\"\n\nWe want to have access to the com port for VTC Control from SIMPL Windows and we want to control the display from SIMPL Windows. To accomplish this, we have created a bridge device and added the devices to be bridged to the \"devices\" array on the bridge. As you can see we define the device key and the join start, which will determine which joins we will use on the resulting EISC to interact with the devices. In the Bridge control properties we defined ipid 03, and we will need a corresponding Ethernet System Intercommunication in the SIMPL Windows program at ipid 03.\n\nNow that our devices have been built, we can refer to the device join maps to see which joins correspond to which actions.\n\nSee below:\n\n```cs\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    public class DisplayControllerJoinMap : JoinMapBaseAdvanced\n    {\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"PowerOff\")]\n        public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Power Off\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"PowerOn\")]\n        public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData() { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Power On\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"IsTwoWayDisplay\")]\n        public JoinDataComplete IsTwoWayDisplay = new JoinDataComplete(new JoinData() { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Is Two Way Display\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"VolumeUp\")]\n        public JoinDataComplete VolumeUp = new JoinDataComplete(new JoinData() { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Volume Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"VolumeLevel\")]\n        public JoinDataComplete VolumeLevel = new JoinDataComplete(new JoinData() { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Volume Level\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        [JoinName(\"VolumeDown\")]\n        public JoinDataComplete VolumeDown = new JoinDataComplete(new JoinData() { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Volume Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"VolumeMute\")]\n        public JoinDataComplete VolumeMute = new JoinDataComplete(new JoinData() { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Volume Mute\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"VolumeMuteOn\")]\n        public JoinDataComplete VolumeMuteOn = new JoinDataComplete(new JoinData() { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Volume Mute On\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"VolumeMuteOff\")]\n        public JoinDataComplete VolumeMuteOff = new JoinDataComplete(new JoinData() { JoinNumber = 9, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Volume Mute Off\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"InputSelectOffset\")]\n        public JoinDataComplete InputSelectOffset = new JoinDataComplete(new JoinData() { JoinNumber = 11, JoinSpan = 10 },\n            new JoinMetadata() { Label = \"Input Select\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"InputNamesOffset\")]\n        public JoinDataComplete InputNamesOffset = new JoinDataComplete(new JoinData() { JoinNumber = 11, JoinSpan = 10 },\n            new JoinMetadata() { Label = \"Input Names Offset\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"InputSelect\")]\n        public JoinDataComplete InputSelect = new JoinDataComplete(new JoinData() { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Input Select\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        [JoinName(\"ButtonVisibilityOffset\")]\n        public JoinDataComplete ButtonVisibilityOffset = new JoinDataComplete(new JoinData() { JoinNumber = 41, JoinSpan = 10 },\n            new JoinMetadata() { Label = \"Button Visibility Offset\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalSerial });\n\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData() { JoinNumber = 50, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Is Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        public DisplayControllerJoinMap(uint joinStart)\n            : base(joinStart, typeof(CameraControllerJoinMap))\n        {\n        }\n    }\n}\n```\n\nWe know that the Panasonic Display uses the DisplayControllerJoinMap class and can see the join numbers that will give us access to functionality in the Device.\n\nIsOnline = 50  \nPowerOff = 1  \nPowerOn = 2  \nIsTwoWayDisplay = 3  \nVolumeUp = 5  \nVolumeDown = 6  \nVolumeMute = 7\n\n```cs\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    public class IBasicCommunicationJoinMap : JoinMapBaseAdvanced\n    {\n        [JoinName(\"TextReceived\")]\n        public JoinDataComplete TextReceived = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Text Received From Remote Device\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"SendText\")]\n        public JoinDataComplete SendText = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Text Sent To Remote Device\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"SetPortConfig\")]\n        public JoinDataComplete SetPortConfig = new JoinDataComplete(new JoinData() { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Set Port Config\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n        [JoinName(\"Connect\")]\n        public JoinDataComplete Connect = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Connect\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"Connected\")]\n        public JoinDataComplete Connected = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Connected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        [JoinName(\"Status\")]\n        public JoinDataComplete Status = new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata() { Label = \"Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n\n        public IBasicCommunicationJoinMap(uint joinStart)\n            : base(joinStart, typeof(IBasicCommunicationJoinMap))\n        {\n        }\n    }\n}\n```\n\nConsidering our Bridge config, we can see that the display controls will start at join 1, and the VTC Com port will start at join 51. The result is a single EISC that allows us to interact with our Essentials devices.\n\nTo control diplay power from SIMPL Windows, we would connect Digital Signals to joins 1 & 2 on the EISC to control Display Power On & Off.\nTo utilize the com port device, we would connect Serial Signals (VTC_TX$ and VTC_RX$) to join 51 on the EISC.\n\nYou can refer to our [SIMPL Windows Bridging Example](https://github.com/PepperDash/EssentialsSIMPLWindowsBridgeExample) for a more complex example.  \nExample device config: <https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Example%20Configuration/SIMPLBridging/SIMPLBridgeExample_configurationFile.json>\n\n## Notes\n\n1. It is important to realize that there are no safety checks (yet) when assigning joinStarts in bridge configurations. If you were to put two devices on a bridge with overlapping joins, the most recently bridged join would overwrite previously bridged joins. For now it is on the programmer to ensure there are no conflicting join maps.\n\n2. There is _no_ limit to the amount of times a device may be bridged to. You may have the same device on multiple bridges across multiple applications without problem. That being said, we recommend using common sense. Accessing a single com port for VTC control via multiple bridges may not be wise...\n\n3. A bridge need not only bridge between applications on the same processor. A bridge may bridge to an application on a completely separate processor; simply define the ip address in the Bridge control properties accordingly.\n\n4. For devices included in Essentials, you will be able to find defined join maps below. If you are building your own plugins, you will need to build the join map yourself. It would be beneficial to review the wiki entry on the [Feedback Class](~/docs/technical-docs/Feedback-Classes.md) for this.\n\n5. When building plugins, we highly recommend reusing JoinMaps, as this will make code more easily interchangeable. For example; if you were to build a display plugin, we'd recommend you use/extend the existing `DisplayControllerJoinMap`. This way, you can swap plugins without needing any change on the SIMPL Windows side. This is extremely powerful when maintaining SIMPL Windows code bases for large deployments that may utilize differing equipment per room. If you can build a SIMPL Windows program that interacts with established join maps, you can swap out the device via config without any change needed to SIMPL Windows.\n\n6. Related to item 5, you can use the same paradigm with respect to physical device communication. If you were to have a DSP device in some rooms communicating over RS232 and some via SSH, it would be trival to swap the device from a Com port to an SSH client in the Essentials Devicee Config and update the Bridge Config to brigde to the desired communication method. Again this would require no change on the SIMPL Windows side as long as you maintain the same join Start in the Bridge Device Configuration.\n\n## Common Use Cases\n\n1. There are 10 conference rooms that all operate the same, but have hardware differences that are impossible to account for in SIMPL Windows. For example, each room might have a DM-MD8X8 chassis, but the input and output cards aren't all in the same order, or they might be different models but function the same. You can use Essentials with a unique configuration file for each hardware configuration.\n\n2. You have a floor of conference rooms that all share some centralized hardware like DSP, AV Routing and a shared CEN-GWEXER gateway with multiple GLS-OIR-CSM-EX-BATT occupancy sensors. All the shared hardware can be defined in the Essentials configuration and bridged over an EISC to each program that needs access. The same device can even be exposed to multiple programs over different EISCs.\n\n3. You have a SIMPL program that works for many room types, but because some rooms have different models of processors than others (CP3/CP3N/AV3/PRO3/DMPS3 variants), you have to maintain several versions of the program, compiled for each processor model to maintain access to features like the System Monitor slot. You can use Essentials running in a slot on a processor to expose the System Monitor and many other features of the processor, regardless of model. Now you only need to maintain a single SIMPL program defined for your most complex processor application (ex. PRO3)\n\n## Join Map Documentation\n\n[Join Map Documentation](~/docs/usage/JoinMaps.md)\n\n## Device Type Join Maps\n\nPlease note that these joinmaps _may_ be using a deprecated implementation. The implementation is valid but nonetheless frowned upon for new features and plugins.\n\n### AirMediaController\n\n> supports: AM-200, AM-300\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/AirMediaControllerJoinMap.cs>\n\n### AppleTvController\n\n> supports: IR control of Apple TV\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs>\n\n### CameraControlBase\n\n> supports: any camera that derives from CameraBase\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/CameraControllerJoinMap.cs>\n\n### DisplayController\n\n> supports: IR controlled displays, any two way display driver that derives from PepperDash.Essentials.Core.DisplayBase\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/DisplayControllerJoinMap.cs>\n\n### DmChasisController\n\n> supports: All DM-MD-8x8/16x16/32x32 chassis, with or w/o DM-CPU3 Card\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs>\n\n### DmRmcController\n\n> supports: All DM-RMC devices\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs>\n\n### DmTxController\n\n> supports: All Dm-Tx devices\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmTxControllerJoinMap.cs>\n\n### DmpsAudioOutputController\n\n> supports: Program, Aux1, Aux2 outputs of all DMPS3 Control Systems\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs>\n\n### DmpsRoutingController\n\n> supports: Av routing for all DMPS3 Control Systems\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs>\n\n### GenericRelayController\n\n> supports: Any relay port on a Crestron Control System or Dm Endpoint\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/GenericRelayControllerJoinMap.cs>\n\n### GenericLightingJoinMap\n\n> supports: Devices derived from PepperDash.Essentials.Core.Lighting.LightingBase\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/GenericLightingJoinMap.cs>\n\n### GlsOccupancySensorBase\n\n> supports: Any Crestron GLS-Type Occupancy sensor - single/dual type\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsOccupancySensorBaseJoinMap.cs>\n\n### HdMdxxxCEController\n\n> supports: HD-MD-400-C-E, HD-MD-300-C-E, HD-MD-200-C-E, HD-MD-200-C-1G-E-B/W\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/HdMdxxxCEControllerJoinMap.cs>\n\n### IBasicCommunication\n\n> supports: Any COM Port on a Control System or Dm Endpoint device, TCP Client, SSH Client, or UDP Server\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/IBasicCommunicationJoinMap.cs>\n\n### IDigitalInput\n\n> supports: Any Digital Input on a Control System, or DM Endpoint device\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/IDigitalInputJoinMap.cs>\n\n### SystemMonitorController\n\n> supports: Exposing the system monitor slot for any Control System\n\n<https://github.com/PepperDash/Essentials/blob/main/essentials-framework/Essentials%20Core/PepperDashEssentialsBase/Bridges/JoinMaps/SystemMonitorJoinMap.cs>\n\n## Example SIMPL Windows Program\n\nWe've provided an [example program](https://github.com/PepperDash/EssentialsSIMPLWindowsBridgeExample) for SIMPL Windows that works with the provided example Essentials configuration file [SIMPLBridgeExample_configurationFile.json](https://github.com/PepperDash/Essentials/blob/main/PepperDashEssentials/Example%20Configuration/SIMPLBridging/SIMPLBridgeExample_configurationFile.json). Load Essentials and the example SIMPL program to two slots on the same processor and you can get a better idea of how to take advantage of SIMPL Windows bridging.\n\nNext: [Essentials architecture](~/docs/technical-docs/Arch-summary.md)\n"
  },
  {
    "path": "docs/docs/usage/Standalone-Use.md",
    "content": "# Stand-alone Application\n\nEssentials was originally designed as a standalone SIMPL# Pro control system application and has developed into a versatile, pluggable application. This page describes how to use our built-in room types for a completely self-contained \"one-slot\" control program.\n\nBy defining devices and a room in a JSON configuration file, Essentials can control an entire AV control system for a room. A file can be manually created in an IDE such as Visual Studio Code, or it can be generated by a friendly web-based configuration tool on [PepperDash Portal](http://pepperdash.com/products/), or some other configuration tool application, both requiring no knowledge of JSON.  These tools step a user through building the necessary devices and setting to achieve a full working room.\n\n## Plugins\n\n### Devices\n\nEssentials supports device plugins for communicating with various devices using both standard Crestron CIP communications, Cresnet, SSH, or other TCP/IP-based communication methods. See [the Plugins section](~/docs/technical-docs/Plugins.md) for more details\n\n### Rooms\n\nIn order to tie together equipment into a unit that comprises what devices are used in a room, Essentials supports Room plugins. These plugins are similar to device plugins, in that they're loaded at runtime and allow for customization of business logic and behavior. They're loaded into a different section of the Device Manager, and can reference devices created by device plugins using the device's key.\n\nSee Also: [[Supported Devices|Supported-Devices]]\n\nNext: [Simpl Windows bridging](~/docs/usage/SIMPL-Bridging-Updated.md)\n"
  },
  {
    "path": "docs/index.md",
    "content": "# Welcome to PepperDash Essentials!\n\nPepperDash Essentials is an open-source framework for control systems, built on Crestron's Simpl# Pro framework. It can be configured as a standalone program capable of running a wide variety of system designs and can also be used to augment other Crestron programs.\n\nEssentials is a collection of C# libraries that can be used in many ways. It is a 100% configuration-driven framework that can be extended to add different workflows and behaviors, either through the addition of new device-types and classes, or via a plug-in mechanism. The framework is a collection of things that are all related and interconnected, but in general do not have strong dependencies on each other.\n\n---\n\n## Get started\n\n- [Download an Essentials build or clone the repo](~/docs/Get-started.md)\n- [Get started](~/docs/Get-started.md)\n- [YouTube Video Series Playlist](https://youtube.com/playlist?list=PLKOoNNwgPFZdV5wDEBDZxTHu1KROspaBu)\n- [Discord Server](https://discord.gg/6Vh3ssDdPs)\n\nOr use the links to the left to navigate our documentation.\n\n---\n\n## Benefits\n\n- Runs on Crestron 3-Series, **4-Series** and VC-4 Control System platforms\n- Reduced hardware overhead compared to S+ and Simpl solutions\n- Quick development cycle\n- Shared resources made easily available\n- More flexibility with less code\n- Configurable using simple JSON files\n\n---\n\n## Collaboration\n\nEssentials is an open-source project and we encourage collaboration on this community project. For features that may not be useful to the greater community, or for just-plain learning, we want to remind developers to try writing plugins for Essentials. More information can be found here: [Plugins](~/docs/technical-docs/Plugins.md)\n\n### Open-source-collaborative workflow\n\nThe `main` branch always contain the latest stable version. The `development` branch is used for most development efforts.\n\n[GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) will be used as the workflow for this collaborative project. To contribute, follow this process:\n\n1. Fork this repository ([More Info](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks))\n2. Create a branch using standard GitFlow branch prefixes (feature/hotfix) followed by a descriptive name.\n   - Example: `feature/add-awesomeness` or `hotfix/really-big-oops`\n   - When working on a new feature or bugfix, branch from the `development` branch. When working on a hotfix, branch from `main`.\n3. Make commits as necessary (often is better). And use concise, descriptive language, leveraging issue notation and/or [Closing Keywords](https://help.github.com/articles/closing-issues-using-keywords) to ensure any issues addressed by your work are referenced accordingly.\n4. When the scope of the work for your branch is complete, make sure to update your branch in case further progress has been made since the repo was forked\n5. Create a Pull Request to pull your branch into the appropriate branch in the main repository.\n6. Your Pull Request will be reviewed by our team and evaluated for inclusion into the main repository.\n\nNext: [Get started](~/docs/Get-started.md)\n"
  },
  {
    "path": "docs/toc.yml",
    "content": "- name: Docs\n  href: docs/\n- name: API\n  href: api/"
  },
  {
    "path": "src/Directory.Build.props",
    "content": "<Project>\n  <PropertyGroup>\n    <Version>2.29.0-local</Version>\n    <InformationalVersion>$(Version)</InformationalVersion>\n    <Authors>PepperDash Technology</Authors>\n    <Company>PepperDash Technology</Company>\n    <Product>PepperDash Essentials</Product>\n    <Copyright>Copyright ©  2025</Copyright>\n    <RepositoryUrl>https://github.com/PepperDash/Essentials</RepositoryUrl>\n    <RepositoryType>git</RepositoryType>\n    <PackageTags>Crestron; 4series</PackageTags>\n    <PackageOutputPath>../../output</PackageOutputPath>\n    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>\n    <PackageLicenseFile>LICENSE.md</PackageLicenseFile>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n    <GenerateDocumentationFile>True</GenerateDocumentationFile>\n    <ProduceReferenceAssembly>true</ProduceReferenceAssembly>\n  </PropertyGroup>\n  <ItemGroup>\n    <None Include=\"..\\..\\LICENSE.md\" Pack=\"true\" PackagePath=\"\"/>\n    <None Include=\"..\\..\\README.md\" Pack=\"true\" PackagePath=\"\"/>\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "src/Directory.Build.targets",
    "content": "<Project>\n  <ItemGroup>    \n    <None Include=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).clz\" Condition=\"$(ProjectType) == 'Library'\">\n      <Pack>true</Pack>\n      <PackagePath>build;</PackagePath>\n    </None>\n    <None Include=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz\" Condition=\"$(ProjectType) == 'Program'\">\n      <Pack>true</Pack>\n      <PackagePath>build;</PackagePath>\n    </None>\n    <None Include=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz\" Condition=\"$(ProjectType) == 'ProgramLibrary'\">\n      <Pack>true</Pack>\n      <PackagePath>build;</PackagePath>\n    </None>\n  </ItemGroup>\n  <PropertyGroup Condition=\"$(ProjectType) == 'Library'\">\n    <FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).clz</FileName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"$(ProjectType) == 'ProgramLibrary'\">\n    <FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz</FileName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"$(ProjectType) == 'Program'\">\n    <FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName>\n  </PropertyGroup>\n\n  <Target Name=\"DeleteCLZ\" BeforeTargets=\"CoreBuild\" Condition=\"$(ProjectType) == 'Library' And $(TargetDir) != ''\">\n    <ItemGroup>\n      <OldCLZFiles Include=\"$(TargetDir)$(TargetName).*.$(TargetFramework).clz\" />\n    </ItemGroup>\n    <Delete Files=\"@(OldCLZFiles)\" Condition=\"@(OldCLZFiles) != ''\">\n      <Output TaskParameter=\"DeletedFiles\" ItemName=\"DeletedList\"/>\n    </Delete>\n    <Message Text=\"Deleted old CLZ files: '@(DeletedList)'\" Condition=\"@(DeletedList) != ''\" />\n  </Target>\n  <Target Name=\"DeleteCPZ\" BeforeTargets=\"CoreBuild\" Condition=\"$(ProjectType) == 'Program' And $(TargetDir) != ''\">\n    <ItemGroup>\n      <OldCPZFiles Include=\"$(TargetDir)$(TargetName).*.$(TargetFramework).cpz\" />\n    </ItemGroup>\n    <Delete Files=\"@(OldCPZFiles)\" Condition=\"@(OldCPZFiles) != ''\">\n      <Output TaskParameter=\"DeletedFiles\" ItemName=\"DeletedList\"/>\n    </Delete>\n    <Message Text=\"Deleted old CPZ files: '@(DeletedList)'\" Condition=\"@(DeletedList) != ''\" />\n  </Target>\n  <Target Name=\"DeleteCPLZ\" BeforeTargets=\"CoreBuild\" Condition=\"$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''\">\n    <ItemGroup>\n      <OldCPLZFiles Include=\"$(TargetDir)$(TargetName).*.$(TargetFramework).cplz\" />\n    </ItemGroup>\n    <Delete Files=\"@(OldCPLZFiles)\" Condition=\"@(OldCPLZFiles) != ''\">\n      <Output TaskParameter=\"DeletedFiles\" ItemName=\"DeletedList\"/>\n    </Delete>\n    <Message Text=\"Deleted old CPLZ files: '@(DeletedList)'\" Condition=\"@(DeletedList) != ''\" />\n  </Target>\n  \n  <Target Name=\"CreateCPLZ\" AfterTargets=\"Build\" Condition=\"$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''\" DependsOnTargets=\"DeleteCPLZ\">\n    <Message Text=\"Creating CPLZ $(TargetDir)\"></Message>\n    <MakeDir Directories=\"$(PackageOutputPath)\" Condition=\"!Exists($(PackageOutputPath))\" />    \n    <ZipDirectory SourceDirectory=\"$(TargetDir)\" DestinationFile=\"$(PackageOutputPath)\\$(TargetName).$(Version).$(TargetFramework).cplz\" Overwrite=\"true\"/>\n    <Copy SourceFiles=\"$(PackageOutputPath)\\$(TargetName).$(Version).$(TargetFramework).cplz\" DestinationFiles=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz\" />\n  </Target>  \n  <Target Name=\"Copy CLZ\" AfterTargets=\"SimplSharpPostProcess\" Condition=\"($(ProjectType) == 'Library')\">\n    <Message Text=\"Copying CLZ\"></Message>\n    <Move SourceFiles=\"$(TargetDir)\\$(TargetName).clz\" DestinationFiles=\"$(TargetDir)\\$(TargetName).$(Version).$(TargetFramework).clz\"/>\n    <Copy SourceFiles=\"$(TargetDir)\\$(TargetName).$(Version).$(TargetFramework).clz\" DestinationFiles=\"$(PackageOutputPath)\\$(TargetName).$(Version).$(TargetFramework).clz\"/>\n  </Target>\n  <Target Name=\"Copy CPZ\" AfterTargets=\"SimplSharpPostProcess\" Condition=\"($(ProjectType) == 'Program' And ( '$(TargetFramework)' != 'net6.0' ) And ( '$(TargetFramework)' != 'net8.0' ))\">\n    <Message Text=\"Copying CPZ\"></Message>\n    <Move SourceFiles=\"$(TargetDir)$(TargetName).cpz\" DestinationFiles=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz\" />\n    <Copy SourceFiles=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz\" DestinationFiles=\"$(PackageOutputPath)\\$(TargetName).$(Version).$(TargetFramework).cpz\" />\n  </Target>\n</Project>\n"
  },
  {
    "path": "src/PepperDash.Core/ComTextHelper.cs",
    "content": "using System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\n\nnamespace PepperDash.Core\n{\n  /// <summary>\n  /// Helper class for formatting communication text and byte data for debugging purposes.\n  /// </summary>\n  public class ComTextHelper\n  {\n    /// <summary>\n    /// Gets escaped text for a byte array\n    /// </summary>\n    /// <param name=\"bytes\"></param>\n    /// <returns>string with all bytes escaped</returns>\n    public static string GetEscapedText(byte[] bytes)\n    {\n      return string.Concat(bytes.Select(b => string.Format(@\"[{0:X2}]\", (int)b)).ToArray());\n    }\n\n    /// <summary>\n    /// Gets escaped text for a string\n    /// </summary>\n    /// <param name=\"text\"></param>\n    /// <returns>string with all bytes escaped</returns>\n    public static string GetEscapedText(string text)\n    {\n      var bytes = Encoding.GetEncoding(28591).GetBytes(text);\n      return string.Concat(bytes.Select(b => string.Format(@\"[{0:X2}]\", (int)b)).ToArray());\n    }\n\n    /// <summary>\n    /// Gets debug text for a string\n    /// </summary>\n    /// <param name=\"text\"></param>\n    /// <returns>string with all non-printable characters escaped</returns>\n    public static string GetDebugText(string text)\n    {\n      return Regex.Replace(text, @\"[^\\u0020-\\u007E]\", a => GetEscapedText(a.Value));\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/CommunicationGather.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Core\n{\n\t/// <summary>\n\t/// Defines the string event handler for line events on the gather\n\t/// </summary>\n\t/// <param name=\"text\"></param>\n\tpublic delegate void LineReceivedHandler(string text);\n\n\t/// <summary>\n\t/// Attaches to IBasicCommunication as a text gather\n\t/// </summary>\n\tpublic class CommunicationGather\n\t{\n\t\t/// <summary>\n\t\t/// Event that fires when a line is received from the IBasicCommunication source.\n\t\t/// The event merely contains the text, not an EventArgs type class.\n\t\t/// </summary>\n\t\tpublic event EventHandler<GenericCommMethodReceiveTextArgs> LineReceived;\n\n\t\t/// <summary>\n\t\t/// The communication port that this gathers on\n\t\t/// </summary>\n        public ICommunicationReceiver Port { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Default false. If true, the delimiter will be included in the line output\n\t\t/// events\n\t\t/// </summary>\n\t\tpublic bool IncludeDelimiter { get; set; }\n\n\t\t/// <summary>\n\t\t///\tFor receive buffer\n\t\t/// </summary>\n\t\tStringBuilder ReceiveBuffer = new StringBuilder();\n\n\t\t/// <summary>\n\t\t/// Delimiter, like it says!\n\t\t/// </summary>\n\t\tchar Delimiter;\n\n\t\tstring[] StringDelimiters;\n\n\t\t/// <summary>\n\t\t/// Constructor for using a char delimiter\n\t\t/// </summary>\n\t\t/// <param name=\"port\"></param>\n\t\t/// <param name=\"delimiter\"></param>\n\t\tpublic CommunicationGather(ICommunicationReceiver port, char delimiter)\n\t\t{\n\t\t\tPort = port;\n\t\t\tDelimiter = delimiter;\n\t\t\tport.TextReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(Port_TextReceived);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor for using a single string delimiter\n\t\t/// </summary>\n\t\t/// <param name=\"port\"></param>\n\t\t/// <param name=\"delimiter\"></param>\n        public CommunicationGather(ICommunicationReceiver port, string delimiter)\n            :this(port, new string[] { delimiter} )\n\t\t{\n\t\t}\n\n        /// <summary>\n        /// Constructor for using an array of string delimiters\n        /// </summary>\n        /// <param name=\"port\"></param>\n        /// <param name=\"delimiters\"></param>\n        public CommunicationGather(ICommunicationReceiver port, string[] delimiters)\n        {\n            Port = port;\n            StringDelimiters = delimiters;\n            port.TextReceived += Port_TextReceivedStringDelimiter;\n        }\n\n  /// <summary>\n  /// Stop method\n  /// </summary>\n\t\tpublic void Stop()\n\t\t{\n\t\t\tPort.TextReceived -= Port_TextReceived;\n\t\t\tPort.TextReceived -= Port_TextReceivedStringDelimiter;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handler for raw data coming from port \n\t\t/// </summary>\n\t\tvoid Port_TextReceived(object sender, GenericCommMethodReceiveTextArgs args)\n\t\t{\n\t\t\tvar handler = LineReceived;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tReceiveBuffer.Append(args.Text);\n\t\t\t\tvar str = ReceiveBuffer.ToString();\n\t\t\t\tvar lines = str.Split(Delimiter);\n\t\t\t\tif (lines.Length > 0)\n\t\t\t\t{\n\t\t\t\t\tfor (int i = 0; i < lines.Length - 1; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tstring strToSend = null;\n\t\t\t\t\t\tif (IncludeDelimiter)\n\t\t\t\t\t\t\tstrToSend = lines[i] + Delimiter;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tstrToSend = lines[i];\n\t\t\t\t\t\thandler(this, new GenericCommMethodReceiveTextArgs(strToSend));\n\t\t\t\t\t}\n\t\t\t\t\tReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"sender\"></param>\n\t\t/// <param name=\"args\"></param>\n\t\tvoid Port_TextReceivedStringDelimiter(object sender, GenericCommMethodReceiveTextArgs args)\n\t\t{\n\t\t\tvar handler = LineReceived;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\t// Receive buffer should either be empty or not contain the delimiter\n\t\t\t\t// If the line does not have a delimiter, append the \n\t\t\t\tReceiveBuffer.Append(args.Text);\n\t\t\t\tvar str = ReceiveBuffer.ToString();\n\n                // Case: Receiving DEVICE get version\\x0d\\0x0a+OK \"value\":\"1234\"\\x0d\\x0a\n\n                // RX: DEV\n                //  Split: (1) \"DEV\"\n                // RX: I\n                //  Split: (1) \"DEVI\"\n                // RX: CE get version\n                //  Split: (1) \"DEVICE get version\"\n                // RX: \\x0d\\x0a+OK \"value\":\"1234\"\\x0d\\x0a\n                //  Split: (2) DEVICE get version, +OK \"value\":\"1234\"\n\n                // Iterate the delimiters and fire an event for any matching delimiter\n                foreach (var delimiter in StringDelimiters)\n                {\n                    var lines = Regex.Split(str, delimiter);\n                    if (lines.Length == 1)\n                        continue;\n                  \n                    for (int i = 0; i < lines.Length - 1; i++)\n                    {\n                        string strToSend = null;\n                        if (IncludeDelimiter)\n                            strToSend = lines[i] + delimiter;\n                        else\n                            strToSend = lines[i];\n                        handler(this, new GenericCommMethodReceiveTextArgs(strToSend, delimiter));\n                    }\n                    ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);          \n                }\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Deconstructor.  Disconnects from port TextReceived events.\n\t\t/// </summary>\n\t\t~CommunicationGather()\n\t\t{\n\t\t\tStop();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Controls the ability to disable/enable debugging of TX/RX data sent to/from a device with a built in timer to disable\n    /// </summary>\n    public class CommunicationStreamDebugging\n    {\n        /// <summary>\n        /// Device Key that this instance configures\n        /// </summary>\n        public string ParentDeviceKey { get; private set; }\n\n        /// <summary>\n        /// Timer to disable automatically if not manually disabled\n        /// </summary>\n        private CTimer DebugExpiryPeriod;\n\n        /// <summary>\n        /// Gets or sets the DebugSetting\n        /// </summary>\n        public eStreamDebuggingSetting DebugSetting { get; private set; }\n\n        private uint _DebugTimeoutInMs;\n        private const uint _DefaultDebugTimeoutMin = 30;\n\n        /// <summary>\n        /// Timeout in Minutes\n        /// </summary>\n        public uint DebugTimeoutMinutes\n        {\n            get\n            {\n                return _DebugTimeoutInMs / 60000;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the RxStreamDebuggingIsEnabled\n        /// </summary>\n        public bool RxStreamDebuggingIsEnabled { get; private set; }\n\n        /// <summary>\n        /// Indicates that transmit stream debugging is enabled\n        /// </summary>\n        public bool TxStreamDebuggingIsEnabled { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"parentDeviceKey\"></param>\n        public CommunicationStreamDebugging(string parentDeviceKey)\n        {\n            ParentDeviceKey = parentDeviceKey;\n        }\n\n\n        /// <summary>\n        /// Sets the debugging setting and if not setting to off, assumes the default of 30 mintues\n        /// </summary>\n        /// <param name=\"setting\"></param>\n        /// <summary>\n        /// SetDebuggingWithDefaultTimeout method\n        /// </summary>\n        public void SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting setting)\n        {\n            if (setting == eStreamDebuggingSetting.Off)\n            {\n                DisableDebugging();\n                return;\n            }\n\n            SetDebuggingWithSpecificTimeout(setting, _DefaultDebugTimeoutMin);\n        }\n\n        /// <summary>\n        /// Sets the debugging setting for the specified number of minutes\n        /// </summary>\n        /// <param name=\"setting\"></param>\n        /// <param name=\"minutes\"></param>\n        /// <summary>\n        /// SetDebuggingWithSpecificTimeout method\n        /// </summary>\n        public void SetDebuggingWithSpecificTimeout(eStreamDebuggingSetting setting, uint minutes)\n        {\n            if (setting == eStreamDebuggingSetting.Off)\n            {\n                DisableDebugging();\n                return;\n            }\n\n            _DebugTimeoutInMs = minutes * 60000;\n\n            StopDebugTimer();\n\n            DebugExpiryPeriod = new CTimer((o) => DisableDebugging(), _DebugTimeoutInMs);\n\n            if ((setting & eStreamDebuggingSetting.Rx) == eStreamDebuggingSetting.Rx)\n                RxStreamDebuggingIsEnabled = true;\n\n            if ((setting & eStreamDebuggingSetting.Tx) == eStreamDebuggingSetting.Tx)\n                TxStreamDebuggingIsEnabled = true;\n\n            Debug.SetDeviceDebugSettings(ParentDeviceKey, setting);\n\n        }\n\n        /// <summary>\n        /// Disabled debugging\n        /// </summary>\n        private void DisableDebugging()\n        {\n            StopDebugTimer();\n\n            Debug.SetDeviceDebugSettings(ParentDeviceKey, eStreamDebuggingSetting.Off);\n        }\n\n        private void StopDebugTimer()\n        {\n            RxStreamDebuggingIsEnabled = false;\n            TxStreamDebuggingIsEnabled = false;\n\n            if (DebugExpiryPeriod == null)\n            {\n                return;\n            }\n\n            DebugExpiryPeriod.Stop();\n            DebugExpiryPeriod.Dispose();\n            DebugExpiryPeriod = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Comm/ControlPropertiesConfig.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Represents a ControlPropertiesConfig\n    /// </summary>\n    public class ControlPropertiesConfig\n    {\n        /// <summary>\n        /// The method of control\n        /// </summary>\n        [JsonProperty(\"method\")]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public eControlMethod Method { get; set; }\n\n        /// <summary>\n        /// The key of the device that contains the control port\n        /// </summary>\n        [JsonProperty(\"controlPortDevKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string ControlPortDevKey { get; set; }\n\n        /// <summary>\n        /// The number of the control port on the device specified by ControlPortDevKey\n        /// </summary>\n        [JsonProperty(\"controlPortNumber\", NullValueHandling = NullValueHandling.Ignore)] // In case \"null\" is present in config on this value\n        public uint? ControlPortNumber { get; set; }\n\n        /// <summary>\n        /// The name of the control port on the device specified by ControlPortDevKey\n        /// </summary>\n        [JsonProperty(\"controlPortName\", NullValueHandling = NullValueHandling.Ignore)] // In case \"null\" is present in config on this value\n        public string ControlPortName { get; set; }\n\n        /// <summary>\n        /// Properties for ethernet based communications\n        /// </summary>\n        [JsonProperty(\"tcpSshProperties\", NullValueHandling = NullValueHandling.Ignore)]\n        public TcpSshPropertiesConfig TcpSshProperties { get; set; }\n\n        /// <summary>\n        /// The filename and path for the IR file\n        /// </summary>\n        [JsonProperty(\"irFile\", NullValueHandling = NullValueHandling.Ignore)]\n        public string IrFile { get; set; }\n\n        /// <summary>\n        /// The IpId of a Crestron device\n        /// </summary>\n        [JsonProperty(\"ipId\", NullValueHandling = NullValueHandling.Ignore)]\n        public string IpId { get; set; }\n\n        /// <summary>\n        /// Readonly uint representation of the IpId\n        /// </summary>\n        [JsonIgnore]\n        public uint IpIdInt { get { return Convert.ToUInt32(IpId, 16); } }\n\n        /// <summary>\n        /// Char indicating end of line\n        /// </summary>\n        [JsonProperty(\"endOfLineChar\", NullValueHandling = NullValueHandling.Ignore)]\n        public char EndOfLineChar { get; set; }\n\n        /// <summary>\n        /// Defaults to Environment.NewLine;\n        /// </summary>\n        [JsonProperty(\"endOfLineString\", NullValueHandling = NullValueHandling.Ignore)]\n        public string EndOfLineString { get; set; }\n\n        /// <summary>\n        /// Indicates \n        /// </summary>\n        [JsonProperty(\"deviceReadyResponsePattern\", NullValueHandling = NullValueHandling.Ignore)]\n        public string DeviceReadyResponsePattern { get; set; }\n\n        /// <summary>\n        /// Used when communcating to programs running in VC-4\n        /// </summary>\n        [JsonProperty(\"roomId\", NullValueHandling = NullValueHandling.Ignore)]\n        public string RoomId { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public ControlPropertiesConfig()\n        {            \n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/EventArgs.cs",
    "content": "﻿/*PepperDash Technology Corp.\nCopyright:\t\t2017\n------------------------------------\n***Notice of Ownership and Copyright***\nThe material in which this notice appears is the property of PepperDash Technology Corporation, \nwhich claims copyright under the laws of the United States of America in the entire body of material \nand in all parts thereof, regardless of the use to which it is being put.  Any use, in whole or in part, \nof this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.  \nPepperDash Technology Corporation reserves all rights under applicable laws.\n------------------------------------ */\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\n\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Delegate for notifying of socket status changes\n    /// </summary>\n    /// <param name=\"client\"></param>\n    public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client);\n\n    /// <summary>\n    /// EventArgs class for socket status changes\n    /// </summary>\n\tpublic class GenericSocketStatusChageEventArgs : EventArgs\n\t{\n  /// <summary>\n  /// Gets or sets the Client\n  /// </summary>\n\t\tpublic ISocketStatus Client { get; private set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"client\"></param>\n\t\tpublic GenericSocketStatusChageEventArgs(ISocketStatus client)\n\t\t{\n\t\t\tClient = client;\n\t\t}\n\t\t/// <summary>\n\t\t/// S+ Constructor\n\t\t/// </summary>\n\t\tpublic GenericSocketStatusChageEventArgs() { }\n    }\n\n    /// <summary>\n    /// Delegate for notifying of TCP Server state changes\n    /// </summary>\n    /// <param name=\"state\"></param>\n    public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);\n\n    /// <summary>\n    /// EventArgs class for TCP Server state changes\n    /// </summary>\n    public class GenericTcpServerStateChangedEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the State\n        /// </summary>\n        public ServerState State { get; private set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"state\"></param>\n        public GenericTcpServerStateChangedEventArgs(ServerState state)\n        {\n            State = state;\n        }\n\t\t/// <summary>\n\t\t/// S+ Constructor\n\t\t/// </summary>\n\t\tpublic GenericTcpServerStateChangedEventArgs() { }\n    }\n\n    /// <summary>\n    /// Delegate for TCP Server socket status changes\n    /// </summary>\n    /// <param name=\"socket\"></param>\n    /// <param name=\"clientIndex\"></param>\n    /// <param name=\"clientStatus\"></param>\n    public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);\n    /// <summary>\n    /// EventArgs for TCP server socket status changes\n    /// </summary>\n    public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        public object Socket { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n        public uint ReceivedFromClientIndex { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n        public SocketStatus ClientStatus { get; set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"socket\"></param>\n        /// <param name=\"clientStatus\"></param>\n        public GenericTcpServerSocketStatusChangeEventArgs(object socket, SocketStatus clientStatus)\n        {\n            Socket = socket;\n            ClientStatus = clientStatus;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"socket\"></param>\n        /// <param name=\"clientIndex\"></param>\n        /// <param name=\"clientStatus\"></param>\n        public GenericTcpServerSocketStatusChangeEventArgs(object socket, uint clientIndex, SocketStatus clientStatus)\n        {\n            Socket = socket;\n            ReceivedFromClientIndex = clientIndex;\n            ClientStatus = clientStatus;\n        }\n\t\t/// <summary>\n\t\t/// S+ Constructor\n\t\t/// </summary>\n\t\tpublic GenericTcpServerSocketStatusChangeEventArgs() { }\n    }\n\n    /// <summary>\n    /// EventArgs for TCP server com method receive text\n    /// </summary>\n    public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        public uint ReceivedFromClientIndex { get; private set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort ReceivedFromClientIndexShort\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn (ushort)ReceivedFromClientIndex;\n\t\t\t}\n\t\t}\n\n        /// <summary>\n        /// Gets or sets the Text\n        /// </summary>\n        public string Text { get; private set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"text\"></param>\n        public GenericTcpServerCommMethodReceiveTextArgs(string text)\n        {\n            Text = text;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"text\"></param>\n        /// <param name=\"clientIndex\"></param>\n        public GenericTcpServerCommMethodReceiveTextArgs(string text, uint clientIndex)\n        {\n            Text = text;\n            ReceivedFromClientIndex = clientIndex;\n        }\n\t\t/// <summary>\n\t\t/// S+ Constructor\n\t\t/// </summary>\n\t\tpublic GenericTcpServerCommMethodReceiveTextArgs() { }\n    }\n\n    /// <summary>\n    /// EventArgs for TCP server client ready for communication\n    /// </summary>\n    public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        public bool IsReady;\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"isReady\"></param>\n        public GenericTcpServerClientReadyForcommunicationsEventArgs(bool isReady)\n        {\n            IsReady = isReady;\n        }\n\t\t/// <summary>\n\t\t/// S+ Constructor\n\t\t/// </summary>\n\t\tpublic GenericTcpServerClientReadyForcommunicationsEventArgs() { }\n    }\n\n    /// <summary>\n    /// EventArgs for UDP connected\n    /// </summary>\n    public class GenericUdpConnectedEventArgs : EventArgs\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        public ushort UConnected;\n        /// <summary>\n        /// \n        /// </summary>\n        public bool Connected;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public GenericUdpConnectedEventArgs() { }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"uconnected\"></param>\n        public GenericUdpConnectedEventArgs(ushort uconnected)\n        {\n            UConnected = uconnected;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"connected\"></param>\n        public GenericUdpConnectedEventArgs(bool connected)\n        {\n            Connected = connected;\n        }\n\n    }\n\n   \n\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericSecureTcpIpClient.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing PepperDash.Core.Logging;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// A class to handle secure TCP/IP communications with a server\n    /// </summary>\n    public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect\n    {\n        private const string SplusKey = \"Uninitialized Secure Tcp _client\";\n        /// <summary>\n        /// Stream debugging \n        /// </summary>\n        public CommunicationStreamDebugging StreamDebugging { get; private set; }\n\n        /// <summary>\n        /// Fires when data is received from the server and returns it as a Byte array\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// Fires when data is received from the server and returns it as text\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n        #region GenericSecureTcpIpClient Events & Delegates\n\n        /// <summary>\n        /// \n        /// </summary>\n        //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;\t\t\n        public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;\n\n        /// <summary>\n        /// Auto reconnect evant handler\n        /// </summary>\n        public event EventHandler AutoReconnectTriggered;\n\n        /// <summary>\n        /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread. \n        /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event. \n        /// </summary>\n        public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceivedQueueInvoke;\n        \n        /// <summary>\n        /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require\n        /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.\n        /// </summary>\n        public event EventHandler<GenericTcpServerClientReadyForcommunicationsEventArgs> ClientReadyForCommunications;\n\n        #endregion\n\n\n        #region GenricTcpIpClient properties\n\n        private string _hostname;\n\n        /// <summary>\n        /// Address of server\n        /// </summary>\n        public string Hostname\n        {\n            get { return _hostname; }\n            set\n            {\n                _hostname = value;\n                if (_client != null)\n                {\n                    _client.AddressClientConnectedTo = _hostname;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// S+ helper\n        /// </summary>\n        public ushort UPort\n        {\n            get { return Convert.ToUInt16(Port); }\n            set { Port = Convert.ToInt32(value); }\n        }\n\n        /// <summary>\n        /// Defaults to 2000\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// Internal secure client\n        /// </summary>\n        private SecureTCPClient _client;\n\n        /// <summary>\n        /// Bool showing if socket is connected\n        /// </summary>\n        public bool IsConnected\n        {\n            get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return (ushort)(IsConnected ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// _client socket status Read only\n        /// </summary>\n        public SocketStatus ClientStatus\n        {\n            get\n            {\n                return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;\n            }\n        }\n\n        /// <summary>\n        /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event\n        /// and IsConnected would be true when this == 2.\n        /// </summary>\n        public ushort UStatus\n        {\n            get { return (ushort)ClientStatus; }\n        }\n\n        /// <summary>\n        /// Status text shows the message associated with socket status\n        /// </summary>\n        public string ClientStatusText { get { return ClientStatus.ToString(); } }\n\n        /// <summary>\n        /// Connection failure reason\n        /// </summary>\n        public string ConnectionFailure { get { return ClientStatus.ToString(); } }\n\n        /// <summary>\n        /// Gets or sets the AutoReconnect\n        /// </summary>\n        public bool AutoReconnect { get; set; }\n\n        /// <summary>\n        /// S+ helper for AutoReconnect\n        /// </summary>\n        public ushort UAutoReconnect\n        {\n            get { return (ushort)(AutoReconnect ? 1 : 0); }\n            set { AutoReconnect = value == 1; }\n        }\n\n        /// <summary>\n        /// Milliseconds to wait before attempting to reconnect. Defaults to 5000\n        /// </summary>\n        public int AutoReconnectIntervalMs { get; set; }\n\n        /// <summary>\n        /// Flag Set only when the disconnect method is called.\n        /// </summary>\n        bool DisconnectCalledByUser;\n\n        /// <summary>\n        /// \n        /// </summary>\n        public bool Connected\n        {\n            get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }\n        }\n\n        // private Timer for auto reconnect\n        private CTimer RetryTimer;\n\n        #endregion\n\n        #region GenericSecureTcpIpClient properties\n\n        /// <summary>\n        /// Gets or sets the SharedKeyRequired\n        /// </summary>\n        public bool SharedKeyRequired { get; set; }\n\n        /// <summary>\n        /// S+ helper for requires shared key bool\n        /// </summary>\n        public ushort USharedKeyRequired\n        {\n            set\n            {\n                if (value == 1)\n                    SharedKeyRequired = true;\n                else\n                    SharedKeyRequired = false;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the SharedKey\n        /// </summary>\n        public string SharedKey { get; set; }\n\n        /// <summary>\n        /// flag to show the client is waiting for the server to send the shared key\n        /// </summary>\n        private bool WaitingForSharedKeyResponse { get; set; }\n\n        /// <summary>\n        /// Semaphore on connect method\n        /// </summary>\n        bool IsTryingToConnect;\n\n        /// <summary>\n        /// Gets or sets the IsReadyForCommunication\n        /// </summary>\n        public bool IsReadyForCommunication { get; set; }\n\n        /// <summary>\n        /// S+ helper for IsReadyForCommunication\n        /// </summary>\n        public ushort UIsReadyForCommunication\n        {\n            get { return (ushort)(IsReadyForCommunication ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// Bool Heartbeat Enabled flag\n        /// </summary>\n        public bool HeartbeatEnabled { get; set; }\n\n        /// <summary>\n        /// S+ helper for Heartbeat Enabled\n        /// </summary>\n        public ushort UHeartbeatEnabled\n        {\n            get { return (ushort)(HeartbeatEnabled ? 1 : 0); }\n            set { HeartbeatEnabled = value == 1; }\n        }\n\n        /// <summary>\n        /// Heartbeat String\n        /// </summary>\n        public string HeartbeatString { get; set; }\n        //public int HeartbeatInterval = 50000;\n\n        /// <summary>\n        /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+\n        /// </summary>\n        public int HeartbeatInterval { get; set; }\n\n        /// <summary>\n        /// Simpl+ Heartbeat Analog value in seconds\n        /// </summary>\n        public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }\n\n        CTimer HeartbeatSendTimer;\n        CTimer HeartbeatAckTimer;\n\n        // Used to force disconnection on a dead connect attempt\n        CTimer ConnectFailTimer;\n        CTimer WaitForSharedKey;\n        private int ConnectionCount;\n\n        bool ProgramIsStopping;\n\n        /// <summary>\n        /// Queue lock\n        /// </summary>\n        CCriticalSection DequeueLock = new CCriticalSection();\n\n        /// <summary>\n        /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before\n        /// calling initialize. \n        /// </summary>\n        public int ReceiveQueueSize { get; set; }\n\n        /// <summary>\n        /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before\n        /// calling initialize. \n        /// </summary>\n        private CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs> MessageQueue;\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"address\"></param>\n        /// <param name=\"port\"></param>\n        /// <param name=\"bufferSize\"></param>\n        public GenericSecureTcpIpClient(string key, string address, int port, int bufferSize)\n            : base(key)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(key);\n            Hostname = address;\n            Port = port;\n            BufferSize = bufferSize;\n            AutoReconnectIntervalMs = 5000;\n\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n        }\n\n        /// <summary>\n        /// Contstructor that sets all properties by calling the initialize method with a config object. \n        /// </summary>        \n        /// <param name=\"key\"></param>\n        /// <param name=\"clientConfigObject\"></param>\n        public GenericSecureTcpIpClient(string key, TcpClientConfigObject clientConfigObject)\n            : base(key)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(key);\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            AutoReconnectIntervalMs = 5000;\n            BufferSize = 2000;\n\n            Initialize(clientConfigObject);\n        }\n\n        /// <summary>\n        /// Default constructor for S+\n        /// </summary>\n        public GenericSecureTcpIpClient()\n            : base(SplusKey)\n        {\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            AutoReconnectIntervalMs = 5000;\n            BufferSize = 2000;\n        }\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public void Initialize(string key)\n        {\n            Key = key;\n        }\n\n        /// <summary>\n        /// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client. \n        /// </summary>\n        /// <param name=\"config\"></param>\n        public void Initialize(TcpClientConfigObject config)\n        {\n            if (config == null)\n            {\n                Debug.Console(0, this, \"Could not initialize client with key: {0}\", Key);\n                return;\n            }\n            try\n            {\n                Hostname = config.Control.TcpSshProperties.Address;\n                Port = config.Control.TcpSshProperties.Port > 0 && config.Control.TcpSshProperties.Port <= 65535\n                    ? config.Control.TcpSshProperties.Port\n                    : 80;\n\n                AutoReconnect = config.Control.TcpSshProperties.AutoReconnect;\n                AutoReconnectIntervalMs = config.Control.TcpSshProperties.AutoReconnectIntervalMs > 1000\n                    ? config.Control.TcpSshProperties.AutoReconnectIntervalMs\n                    : 5000;\n\n                SharedKey = config.SharedKey;\n                SharedKeyRequired = config.SharedKeyRequired;\n\n                HeartbeatEnabled = config.HeartbeatRequired;\n                HeartbeatRequiredIntervalInSeconds = config.HeartbeatRequiredIntervalInSeconds > 0\n                    ? config.HeartbeatRequiredIntervalInSeconds\n                    : (ushort)15;\n\n\n                HeartbeatString = string.IsNullOrEmpty(config.HeartbeatStringToMatch)\n                    ? \"heartbeat\"\n                    : config.HeartbeatStringToMatch;\n\n                BufferSize = config.Control.TcpSshProperties.BufferSize > 2000\n                    ? config.Control.TcpSshProperties.BufferSize\n                    : 2000;\n\n                ReceiveQueueSize = config.ReceiveQueueSize > 20\n                    ? config.ReceiveQueueSize\n                    : 20;\n\n                MessageQueue = new CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs>(ReceiveQueueSize);\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(0, this, \"Exception initializing client with key: {0}\\rException: {1}\", Key, ex);\n            }\n        }\n\n        #endregion\n\n        /// <summary>\n        /// Handles closing this up when the program shuts down\n        /// </summary>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Program stopping. Closing _client connection\");\n                ProgramIsStopping = true;\n                Disconnect();\n            }\n\n        }\n\n        /// <summary>\n        /// Deactivate the client\n        /// </summary>\n        /// <returns></returns>\n        /// <summary>\n        /// Deactivate method\n        /// </summary>\n        public override bool Deactivate()\n        {\n            if (_client != null)\n            {\n                _client.SocketStatusChange -= this.Client_SocketStatusChange;\n                DisconnectClient();\n            }\n            return true;\n        }\n\n        /// <summary>\n        /// Connect method\n        /// </summary>\n        public void Connect()\n        {\n            ConnectionCount++;\n            Debug.Console(2, this, \"Attempting connect Count:{0}\", ConnectionCount);\n\n\n            if (IsConnected)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Already connected. Ignoring.\");\n                return;\n            }\n            if (IsTryingToConnect)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Already trying to connect. Ignoring.\");\n                return;\n            }\n            try\n            {\n                IsTryingToConnect = true;\n                if (RetryTimer != null)\n                {\n                    RetryTimer.Stop();\n                    RetryTimer = null;\n                }\n                if (string.IsNullOrEmpty(Hostname))\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: No address set\");\n                    return;\n                }\n                if (Port < 1 || Port > 65535)\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: Invalid port\");\n                    return;\n                }\n                if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: No Shared Key set\");\n                    return;\n                }\n\n                // clean up previous client\n                if (_client != null)\n                {\n                    Disconnect();\n                }\n                DisconnectCalledByUser = false;\n\n                _client = new SecureTCPClient(Hostname, Port, BufferSize);\n                _client.SocketStatusChange += Client_SocketStatusChange;\n                if (HeartbeatEnabled)\n                    _client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);\n                _client.AddressClientConnectedTo = Hostname;\n                _client.PortNumber = Port;\n                // SecureClient = c;\n\n                //var timeOfConnect = DateTime.Now.ToString(\"HH:mm:ss.fff\");\n\n                ConnectFailTimer = new CTimer(o =>\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Connect attempt has not finished after 30sec Count:{0}\", ConnectionCount);\n                    if (IsTryingToConnect)\n                    {\n                        IsTryingToConnect = false;\n\n                        //if (ConnectionHasHungCallback != null)\n                        //{\n                        //    ConnectionHasHungCallback();\n                        //}\t\t\n                        //SecureClient.DisconnectFromServer();\n                        //CheckClosedAndTryReconnect();\n                    }\n                }, 30000);\n\n                Debug.Console(2, this, \"Making Connection Count:{0}\", ConnectionCount);\n                _client.ConnectToServerAsync(o =>\n                {\n                    Debug.Console(2, this, \"ConnectToServerAsync Count:{0} Ran!\", ConnectionCount);\n\n                    if (ConnectFailTimer != null)\n                    {\n                        ConnectFailTimer.Stop();\n                    }\n                    IsTryingToConnect = false;\n\n                    if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    {\n                        Debug.Console(2, this, \"_client connected to {0} on port {1}\", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);\n                        o.ReceiveDataAsync(Receive);\n\n                        if (SharedKeyRequired)\n                        {\n                            WaitingForSharedKeyResponse = true;\n                            WaitForSharedKey = new CTimer(timer =>\n                            {\n\n                                Debug.Console(1, this, Debug.ErrorLogLevel.Warning, \"Shared key exchange timer expired. IsReadyForCommunication={0}\", IsReadyForCommunication);\n                                // Debug.Console(1, this, \"Connect attempt failed {0}\", c.ClientStatus);\n                                // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup \n                                o.DisconnectFromServer();\n                                //CheckClosedAndTryReconnect();\n                                //OnClientReadyForcommunications(false); // Should send false event\n                            }, 15000);\n                        }\n                        else\n                        {\n                            //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key \n                            //required this is called by the shared key being negotiated\n                            if (IsReadyForCommunication == false)\n                            {\n                                OnClientReadyForcommunications(true); // Key not required\n                            }\n                        }\n                    }\n                    else\n                    {\n                        Debug.Console(1, this, \"Connect attempt failed {0}\", o.ClientStatus);\n                        CheckClosedAndTryReconnect();\n                    }\n                });\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Error, \"_client connection exception: {0}\", ex.Message);\n                IsTryingToConnect = false;\n                CheckClosedAndTryReconnect();\n            }\n        }\n\n        /// <summary>\n        /// Disconnect method\n        /// </summary>\n        public void Disconnect()\n        {\n            this.LogVerbose(\"Disconnect Called\");\n\n            DisconnectCalledByUser = true;\n\n            // stop trying reconnects, if we are\n            if (RetryTimer != null)\n            {\n                RetryTimer.Stop();\n                RetryTimer = null;\n            }\n\n            if (_client != null)\n            {\n                DisconnectClient();\n                this.LogDebug(\"Disconnected\");\n            }\n        }\n\n        /// <summary>\n        /// DisconnectClient method\n        /// </summary>\n        public void DisconnectClient()\n        {\n            if (_client == null) return;\n\n            Debug.Console(1, this, \"Disconnecting client\");\n            if (IsConnected)\n                _client.DisconnectFromServer();\n\n            // close up client. ALWAYS use this when disconnecting.\n            IsTryingToConnect = false;\n\n            Debug.Console(2, this, \"Disconnecting _client {0}\", DisconnectCalledByUser ? \", Called by user\" : \"\");\n            _client.SocketStatusChange -= Client_SocketStatusChange;\n            _client.Dispose();\n            _client = null;\n\n            if (ConnectFailTimer == null) return;\n            ConnectFailTimer.Stop();\n            ConnectFailTimer.Dispose();\n            ConnectFailTimer = null;\n        }\n       \n        #region Methods\n\n        /// <summary>\n        /// Called from Connect failure or Socket Status change if \n        /// auto reconnect and socket disconnected (Not disconnected by user)\n        /// </summary>\n        void CheckClosedAndTryReconnect()\n        {\n            if (_client != null)\n            {\n                Debug.Console(2, this, \"Cleaning up remotely closed/failed connection.\");\n                Disconnect();\n            }\n            if (!DisconnectCalledByUser && AutoReconnect)\n            {\n                var halfInterval = AutoReconnectIntervalMs / 2;\n                var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;\n                Debug.Console(2, this, \"Attempting reconnect in {0} ms, randomized\", rndTime);\n                if (RetryTimer != null)\n                {\n                    RetryTimer.Stop();\n                    RetryTimer = null;\n                }\n                if (AutoReconnectTriggered != null)\n                    AutoReconnectTriggered(this, new EventArgs());\n                RetryTimer = new CTimer(o => Connect(), rndTime);\n            }\n        }\n\n        /// <summary>\n        /// Receive callback\n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <param name=\"numBytes\"></param>\n        void Receive(SecureTCPClient client, int numBytes)\n        {\n            if (numBytes > 0)\n            {\n                string str = string.Empty;\n                var handler = TextReceivedQueueInvoke;\n                try\n                {\n                    var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();\n                    str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);\n                    Debug.Console(2, this, \"_client Received:\\r--------\\r{0}\\r--------\", str);\n                    if (!string.IsNullOrEmpty(checkHeartbeat(str)))\n                    {\n\n                        if (SharedKeyRequired && str == \"SharedKey:\")\n                        {\n                            Debug.Console(2, this, \"Server asking for shared key, sending\");\n                            SendText(SharedKey + \"\\n\");\n                        }\n                        else if (SharedKeyRequired && str == \"Shared Key Match\")\n                        {\n                            StopWaitForSharedKeyTimer();\n\n\n                            Debug.Console(2, this, \"Shared key confirmed. Ready for communication\");\n                            OnClientReadyForcommunications(true); // Successful key exchange\n                        }\n                        else\n                        {\n                            //var bytesHandler = BytesReceived;\n                            //if (bytesHandler != null)\n                            //    bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));\n                            var textHandler = TextReceived;\n                            if (textHandler != null)\n                                textHandler(this, new GenericCommMethodReceiveTextArgs(str));\n                            if (handler != null)\n                            {\n                                MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));\n                            }\n                        }\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(1, this, \"Error receiving data: {1}. Error: {0}\", ex.Message, str);\n                }\n                if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    client.ReceiveDataAsync(Receive);\n\n                //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread. \n                if (handler != null)\n                {\n                    var gotLock = DequeueLock.TryEnter();\n                    if (gotLock)\n                        CrestronInvoke.BeginInvoke((o) => DequeueEvent());\n                }\n            }\n            else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync\n            {\n                client.DisconnectFromServer();\n            }\n        }\n\n        /// <summary>\n        /// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.\n        /// It will dequeue items as they are enqueued automatically.\n        /// </summary>\n        void DequeueEvent()\n        {\n            try\n            {\n                while (true)\n                {\n                    // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.\n                    var message = MessageQueue.Dequeue();\n                    var handler = TextReceivedQueueInvoke;\n                    if (handler != null)\n                    {\n                        handler(this, message);\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                this.LogException(e, \"DequeueEvent error\");\n            }\n            // Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.\n            if (DequeueLock != null)\n            {\n                DequeueLock.Leave();\n            }\n        }\n\n        void HeartbeatStart()\n        {\n            if (HeartbeatEnabled)\n            {\n                this.LogVerbose(\"Starting Heartbeat\");\n                if (HeartbeatSendTimer == null)\n                {\n\n                    HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);\n                }\n                if (HeartbeatAckTimer == null)\n                {\n                    HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));\n                }\n            }\n\n        }\n        void HeartbeatStop()\n        {\n\n            if (HeartbeatSendTimer != null)\n            {\n                Debug.Console(2, this, \"Stoping Heartbeat Send\");\n                HeartbeatSendTimer.Stop();\n                HeartbeatSendTimer = null;\n            }\n            if (HeartbeatAckTimer != null)\n            {\n                Debug.Console(2, this, \"Stoping Heartbeat Ack\");\n                HeartbeatAckTimer.Stop();\n                HeartbeatAckTimer = null;\n            }\n\n        }\n        void SendHeartbeat(object notused)\n        {\n            this.SendText(HeartbeatString);\n            Debug.Console(2, this, \"Sending Heartbeat\");\n\n        }\n\n        //private method to check heartbeat requirements and start or reset timer\n        string checkHeartbeat(string received)\n        {\n            try\n            {\n                if (HeartbeatEnabled)\n                {\n                    if (!string.IsNullOrEmpty(HeartbeatString))\n                    {\n                        var remainingText = received.Replace(HeartbeatString, \"\");\n                        var noDelimiter = received.Trim(new char[] { '\\r', '\\n' });\n                        if (noDelimiter.Contains(HeartbeatString))\n                        {\n                            if (HeartbeatAckTimer != null)\n                            {\n                                HeartbeatAckTimer.Reset(HeartbeatInterval * 2);\n                            }\n                            else\n                            {\n                                HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));\n                            }\n                            Debug.Console(2, this, \"Heartbeat Received: {0}, from Server\", HeartbeatString);\n                            return remainingText;\n                        }\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(1, this, \"Error checking heartbeat: {0}\", ex.Message);\n            }\n            return received;\n        }\n\n\n\n        void HeartbeatAckTimerFail(object o)\n        {\n            try\n            {\n\n                if (IsConnected)\n                {\n                    Debug.Console(1, Debug.ErrorLogLevel.Warning, \"Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE\");\n                    SendText(\"Heartbeat not received by server, closing connection\");\n                    CheckClosedAndTryReconnect();\n                }\n\n            }\n            catch (Exception ex)\n            {\n                ErrorLog.Error(\"Heartbeat timeout Error on _client: {0}, {1}\", Key, ex);\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void StopWaitForSharedKeyTimer()\n        {\n            if (WaitForSharedKey != null)\n            {\n                WaitForSharedKey.Stop();\n                WaitForSharedKey = null;\n            }\n        }\n\n        /// <summary>\n        /// SendText method\n        /// </summary>\n        public void SendText(string text)\n        {\n            if (!string.IsNullOrEmpty(text))\n            {\n                try\n                {\n                    var bytes = Encoding.GetEncoding(28591).GetBytes(text);\n                    if (_client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    {\n                        _client.SendDataAsync(bytes, bytes.Length, (c, n) =>\n                        {\n                            // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????\n                            if (n <= 0)\n                            {\n                                Debug.Console(1, Debug.ErrorLogLevel.Warning, \"[{0}] Sent zero bytes. Was there an error?\", this.Key);\n                            }\n                        });\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(0, this, \"Error sending text: {1}. Error: {0}\", ex.Message, text);\n                }\n            }\n        }\n\n        /// <summary>\n        /// SendBytes method\n        /// </summary>\n        public void SendBytes(byte[] bytes)\n        {\n            if (bytes.Length > 0)\n            {\n                try\n                {\n                    if (_client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                        _client.SendData(bytes, bytes.Length);\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(0, this, \"Error sending bytes. Error: {0}\", ex.Message);\n                }\n            }\n        }\n\n        /// <summary>\n        /// SocketStatusChange Callback \n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <param name=\"clientSocketStatus\"></param>\n        void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)\n        {\n            if (ProgramIsStopping)\n            {\n                ProgramIsStopping = false;\n                return;\n            }\n            try\n            {\n                Debug.Console(2, this, \"Socket status change: {0} ({1})\", client.ClientStatus, (ushort)(client.ClientStatus));\n\n                OnConnectionChange();\n                // The client could be null or disposed by this time...\n                if (_client == null || _client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)\n                {\n                    HeartbeatStop();\n                    OnClientReadyForcommunications(false); // socket has gone low\n                    CheckClosedAndTryReconnect();\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Error in socket status change callback. Error: {0}\\r\\r{1}\", ex, ex.InnerException);\n            }\n        }\n\n        /// <summary>\n        /// Helper for ConnectionChange event\n        /// </summary>\n        void OnConnectionChange()\n        {\n            var handler = ConnectionChange;\n            if (handler == null) return;\n\n            handler(this, new GenericSocketStatusChageEventArgs(this));\n        }\n\n        /// <summary>\n        /// Helper to fire ClientReadyForCommunications event\n        /// </summary>\n        void OnClientReadyForcommunications(bool isReady)\n        {\n            IsReadyForCommunication = isReady;\n            if (IsReadyForCommunication) \n                HeartbeatStart();\n\n            var handler = ClientReadyForCommunications;\n            if (handler == null) return;\n            \n            handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));\n        }\n        #endregion\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericSecureTcpIpClient_ForServer.cs",
    "content": "﻿/*PepperDash Technology Corp.\nJAG\nCopyright:\t\t2017\n------------------------------------\n***Notice of Ownership and Copyright***\nThe material in which this notice appears is the property of PepperDash Technology Corporation, \nwhich claims copyright under the laws of the United States of America in the entire body of material \nand in all parts thereof, regardless of the use to which it is being put.  Any use, in whole or in part, \nof this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.  \nPepperDash Technology Corporation reserves all rights under applicable laws.\n------------------------------------ */\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing PepperDash.Core.Logging;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Generic secure TCP/IP client for server\n    /// </summary>\n    public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect\n    {\n        /// <summary>\n        /// Band aid delegate for choked server\n        /// </summary>\n        internal delegate void ConnectionHasHungCallbackDelegate();\n\n        #region Events\n\n        //public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// Notifies of text received\n        /// </summary>\n        public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Notifies of auto reconnect sequence triggered\n        /// </summary>\n        public event EventHandler AutoReconnectTriggered;\n\n        /// <summary>\n        /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread. \n        /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event. \n        /// </summary>\n        public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceivedQueueInvoke;\n\n        /// <summary>\n        /// Notifies of socket status change\n        /// </summary>\n        public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ConnectionChange;\n\n\n        /// <summary>\n        /// This is something of a band-aid callback. If the client times out during the connection process, because the server\n        /// is stuck, this will fire.  It is intended to be used by the Server class monitor client, to help\n        /// keep a watch on the server and reset it if necessary.\n        /// </summary>\n        internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;\n\n        /// <summary>\n        /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require\n        /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.\n        /// </summary>\n        public event EventHandler<GenericTcpServerClientReadyForcommunicationsEventArgs> ClientReadyForCommunications;\n\n        #endregion\n\n        #region Properties & Variables\n\n        /// <summary>\n        /// Address of server\n        /// </summary>\n        public string Hostname { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// S+ helper\n        /// </summary>\n        public ushort UPort\n        {\n            get { return Convert.ToUInt16(Port); }\n            set { Port = Convert.ToInt32(value); }\n        }\n\n        /// <summary>\n        /// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class\n        /// </summary>\n        public bool SharedKeyRequired { get; set; }\n\n        /// <summary>\n        /// S+ helper for requires shared key bool\n        /// </summary>\n        public ushort USharedKeyRequired\n        {\n            set\n            {\n                if (value == 1)\n                    SharedKeyRequired = true;\n                else\n                    SharedKeyRequired = false;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the SharedKey\n        /// </summary>\n        public string SharedKey { get; set; }\n\n        /// <summary>\n        /// flag to show the client is waiting for the server to send the shared key\n        /// </summary>\n        private bool WaitingForSharedKeyResponse { get; set; }\n\n        /// <summary>\n        /// Gets or sets the BufferSize\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// Semaphore on connect method\n        /// </summary>\n        bool IsTryingToConnect;\n\n        /// <summary>\n        /// Bool showing if socket is connected\n        /// </summary>\n        public bool IsConnected\n        {\n            get\n            {\n                if (Client != null)\n                    return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;\n                else\n                    return false;\n            }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return (ushort)(IsConnected ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// Bool showing if socket is ready for communication after shared key exchange\n        /// </summary>\n        public bool IsReadyForCommunication { get; set; }\n\n        /// <summary>\n        /// S+ helper for IsReadyForCommunication\n        /// </summary>\n        public ushort UIsReadyForCommunication\n        {\n            get { return (ushort)(IsReadyForCommunication ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// Client socket status Read only\n        /// </summary>\n        public SocketStatus ClientStatus\n        {\n            get\n            {\n                if (Client != null)\n                    return Client.ClientStatus;\n                else\n                    return SocketStatus.SOCKET_STATUS_NO_CONNECT;\n            }\n        }\n\n        /// <summary>\n        /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event\n        /// and IsConnected would be true when this == 2.\n        /// </summary>\n        public ushort UStatus\n        {\n            get { return (ushort)ClientStatus; }\n        }\n\n        /// <summary>\n        /// Status text shows the message associated with socket status\n        /// </summary>\n        public string ClientStatusText { get { return ClientStatus.ToString(); } }\n\n        /// <summary>\n        /// bool to track if auto reconnect should be set on the socket\n        /// </summary>\n        public bool AutoReconnect { get; set; }\n\n        /// <summary>\n        /// S+ helper for AutoReconnect\n        /// </summary>\n        public ushort UAutoReconnect\n        {\n            get { return (ushort)(AutoReconnect ? 1 : 0); }\n            set { AutoReconnect = value == 1; }\n        }\n        /// <summary>\n        /// Milliseconds to wait before attempting to reconnect. Defaults to 5000\n        /// </summary>\n        public int AutoReconnectIntervalMs { get; set; }\n\n        /// <summary>\n        /// Flag Set only when the disconnect method is called.\n        /// </summary>\n        bool DisconnectCalledByUser;\n\n        /// <summary>\n        /// private Timer for auto reconnect\n        /// </summary>\n        CTimer RetryTimer;\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        public bool HeartbeatEnabled { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n        public ushort UHeartbeatEnabled\n        {\n            get { return (ushort)(HeartbeatEnabled ? 1 : 0); }\n            set { HeartbeatEnabled = value == 1; }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public string HeartbeatString { get; set; }\n        //public int HeartbeatInterval = 50000;\n\n        /// <summary>\n        /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+\n        /// </summary>\n        public int HeartbeatInterval { get; set; }\n\n        /// <summary>\n        /// Simpl+ Heartbeat Analog value in seconds\n        /// </summary>\n        public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }\n\n        CTimer HeartbeatSendTimer;\n        CTimer HeartbeatAckTimer;\n        /// <summary>\n        /// Used to force disconnection on a dead connect attempt\n        /// </summary>\n        CTimer ConnectFailTimer;\n        CTimer WaitForSharedKey;\n        private int ConnectionCount;\n        /// <summary>\n        /// Internal secure client\n        /// </summary>\n        SecureTCPClient Client;\n\n        bool ProgramIsStopping;\n\n        /// <summary>\n        /// Queue lock\n        /// </summary>\n        CCriticalSection DequeueLock = new CCriticalSection();\n\n        /// <summary>\n        /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before\n        /// calling initialize. \n        /// </summary>\n        public int ReceiveQueueSize { get; set; }\n\n        /// <summary>\n        /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before\n        /// calling initialize. \n        /// </summary>\n        private CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs> MessageQueue;\n\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"address\"></param>\n        /// <param name=\"port\"></param>\n        /// <param name=\"bufferSize\"></param>\n        public GenericSecureTcpIpClient_ForServer(string key, string address, int port, int bufferSize)\n            : base(key)\n        {\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            Hostname = address;\n            Port = port;\n            BufferSize = bufferSize;\n            AutoReconnectIntervalMs = 5000;\n\n        }\n\n        /// <summary>\n        /// Constructor for S+\n        /// </summary>\n        public GenericSecureTcpIpClient_ForServer()\n            : base(\"Uninitialized Secure Tcp Client For Server\")\n        {\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            AutoReconnectIntervalMs = 5000;\n            BufferSize = 2000;\n        }\n\n        /// <summary>\n        /// Contstructor that sets all properties by calling the initialize method with a config object. \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"clientConfigObject\"></param>\n        public GenericSecureTcpIpClient_ForServer(string key, TcpClientConfigObject clientConfigObject)\n            : base(key)\n        {\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            Initialize(clientConfigObject);\n        }\n\n        #endregion\n\n        #region Methods\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public void Initialize(string key)\n        {\n            Key = key;\n        }\n\n        /// <summary>\n        /// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client. \n        /// </summary>\n        /// <param name=\"clientConfigObject\"></param>\n        public void Initialize(TcpClientConfigObject clientConfigObject)\n        {\n            try\n            {\n                if (clientConfigObject != null)\n                {\n                    var TcpSshProperties = clientConfigObject.Control.TcpSshProperties;\n                    Hostname = TcpSshProperties.Address;\n                    AutoReconnect = TcpSshProperties.AutoReconnect;\n                    AutoReconnectIntervalMs = TcpSshProperties.AutoReconnectIntervalMs > 1000 ?\n                        TcpSshProperties.AutoReconnectIntervalMs : 5000;\n                    SharedKey = clientConfigObject.SharedKey;\n                    SharedKeyRequired = clientConfigObject.SharedKeyRequired;\n                    HeartbeatEnabled = clientConfigObject.HeartbeatRequired;\n                    HeartbeatRequiredIntervalInSeconds = clientConfigObject.HeartbeatRequiredIntervalInSeconds > 0 ? \n                        clientConfigObject.HeartbeatRequiredIntervalInSeconds : (ushort)15;\n                    HeartbeatString = string.IsNullOrEmpty(clientConfigObject.HeartbeatStringToMatch) ? \"heartbeat\" : clientConfigObject.HeartbeatStringToMatch;\n                    Port = TcpSshProperties.Port;\n                    BufferSize = TcpSshProperties.BufferSize > 2000 ? TcpSshProperties.BufferSize : 2000;\n                    ReceiveQueueSize = clientConfigObject.ReceiveQueueSize > 20 ? clientConfigObject.ReceiveQueueSize : 20;\n                    MessageQueue = new CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs>(ReceiveQueueSize);\n                }\n                else\n                {\n                    ErrorLog.Error(\"Could not initialize client with key: {0}\", Key);\n                }\n            }\n            catch\n            {\n                ErrorLog.Error(\"Could not initialize client with key: {0}\", Key);\n            }\n        }\n\n        /// <summary>\n        /// Handles closing this up when the program shuts down\n        /// </summary>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Program stopping. Closing Client connection\");\n                ProgramIsStopping = true;\n                Disconnect();\n            }\n\n        }\n\n        /// <summary>\n        /// Connect method\n        /// </summary>\n        public void Connect()\n        {\n            ConnectionCount++;\n            Debug.Console(2, this, \"Attempting connect Count:{0}\", ConnectionCount);\n\n\n            if (IsConnected)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Already connected. Ignoring.\");\n                return;\n            }\n            if (IsTryingToConnect)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Already trying to connect. Ignoring.\");\n                return;\n            }\n            try\n            {\n                IsTryingToConnect = true;\n                if (RetryTimer != null)\n                {\n                    RetryTimer.Stop();\n                    RetryTimer = null;\n                }\n                if (string.IsNullOrEmpty(Hostname))\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: No address set\");\n                    return;\n                }\n                if (Port < 1 || Port > 65535)\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: Invalid port\");\n                    return;\n                }\n                if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: No Shared Key set\");\n                    return;\n                }\n\n                // clean up previous client\n                if (Client != null)\n                {\n                    Cleanup();\n                }\n                DisconnectCalledByUser = false;\n\n                Client = new SecureTCPClient(Hostname, Port, BufferSize);\n                Client.SocketStatusChange += Client_SocketStatusChange;\n                if (HeartbeatEnabled)\n                    Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);\n                Client.AddressClientConnectedTo = Hostname;\n                Client.PortNumber = Port;\n                // SecureClient = c;\n\n                //var timeOfConnect = DateTime.Now.ToString(\"HH:mm:ss.fff\");\n\n                ConnectFailTimer = new CTimer(o =>\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Connect attempt has not finished after 30sec Count:{0}\", ConnectionCount);\n                    if (IsTryingToConnect)\n                    {\n                        IsTryingToConnect = false;\n\n                        //if (ConnectionHasHungCallback != null)\n                        //{\n                        //    ConnectionHasHungCallback();\n                        //}\t\t\n                        //SecureClient.DisconnectFromServer();\n                        //CheckClosedAndTryReconnect();\n                    }\n                }, 30000);\n\n                Debug.Console(2, this, \"Making Connection Count:{0}\", ConnectionCount);\n                Client.ConnectToServerAsync(o =>\n                {\n                    Debug.Console(2, this, \"ConnectToServerAsync Count:{0} Ran!\", ConnectionCount);\n\n                    if (ConnectFailTimer != null)\n                    {\n                        ConnectFailTimer.Stop();\n                    }\n                    IsTryingToConnect = false;\n\n                    if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    {\n                        Debug.Console(2, this, \"Client connected to {0} on port {1}\", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);\n                        o.ReceiveDataAsync(Receive);\n\n                        if (SharedKeyRequired)\n                        {\n                            WaitingForSharedKeyResponse = true;\n                            WaitForSharedKey = new CTimer(timer =>\n                            {\n\n                                Debug.Console(1, this, Debug.ErrorLogLevel.Warning, \"Shared key exchange timer expired. IsReadyForCommunication={0}\", IsReadyForCommunication);\n                                // Debug.Console(1, this, \"Connect attempt failed {0}\", c.ClientStatus);\n                                // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup \n                                o.DisconnectFromServer();\n                                //CheckClosedAndTryReconnect();\n                                //OnClientReadyForcommunications(false); // Should send false event\n                            }, 15000);\n                        }\n                        else\n                        {\n                            //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key \n                            //required this is called by the shared key being negotiated\n                            if (IsReadyForCommunication == false)\n                            {\n                                OnClientReadyForcommunications(true); // Key not required\n                            }\n                        }\n                    }\n                    else\n                    {\n                        Debug.Console(1, this, \"Connect attempt failed {0}\", o.ClientStatus);\n                        CheckClosedAndTryReconnect();\n                    }\n                });\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Error, \"Client connection exception: {0}\", ex.Message);\n                IsTryingToConnect = false;\n                CheckClosedAndTryReconnect();\n            }\n        }\n\n        /// <summary>\n        /// Disconnect method\n        /// </summary>\n        public void Disconnect()\n        {\n            this.LogVerbose(\"Disconnect Called\");\n\n            DisconnectCalledByUser = true;\n            if (IsConnected)\n            {\n                Client.DisconnectFromServer();\n\n            }\n            if (RetryTimer != null)\n            {\n                RetryTimer.Stop();\n                RetryTimer = null;\n            }\n            Cleanup();\n        }\n\n        /// <summary>\n        ///  Internal call to close up client. ALWAYS use this when disconnecting.\n        /// </summary>\n        void Cleanup()\n        {\n            IsTryingToConnect = false;\n\n            if (Client != null)\n            {\n                //SecureClient.DisconnectFromServer();\n                Debug.Console(2, this, \"Disconnecting Client {0}\", DisconnectCalledByUser ? \", Called by user\" : \"\");\n                Client.SocketStatusChange -= Client_SocketStatusChange;\n                Client.Dispose();\n                Client = null;\n            }\n            if (ConnectFailTimer != null)\n            {\n                ConnectFailTimer.Stop();\n                ConnectFailTimer.Dispose();\n                ConnectFailTimer = null;\n            }\n        }\n\n\n        /// <summary>ff\n        /// Called from Connect failure or Socket Status change if \n        /// auto reconnect and socket disconnected (Not disconnected by user)\n        /// </summary>\n        void CheckClosedAndTryReconnect()\n        {\n            if (Client != null)\n            {\n                Debug.Console(2, this, \"Cleaning up remotely closed/failed connection.\");\n                Cleanup();\n            }\n            if (!DisconnectCalledByUser && AutoReconnect)\n            {\n                var halfInterval = AutoReconnectIntervalMs / 2;\n                var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;\n                Debug.Console(2, this, \"Attempting reconnect in {0} ms, randomized\", rndTime);\n                if (RetryTimer != null)\n                {\n                    RetryTimer.Stop();\n                    RetryTimer = null;\n                }\n                if(AutoReconnectTriggered != null)\n                    AutoReconnectTriggered(this, new EventArgs());\n                RetryTimer = new CTimer(o => Connect(), rndTime);\n            }\n        }\n\n        /// <summary>\n        /// Receive callback\n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <param name=\"numBytes\"></param>\n        void Receive(SecureTCPClient client, int numBytes)\n        {\n            if (numBytes > 0)\n            {\n                string str = string.Empty;\n                var handler = TextReceivedQueueInvoke;\n                try\n                {\n                    var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();\n                    str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);\n                    Debug.Console(2, this, \"Client Received:\\r--------\\r{0}\\r--------\", str);\n                    if (!string.IsNullOrEmpty(checkHeartbeat(str)))\n                    {\n\n                        if (SharedKeyRequired && str == \"SharedKey:\")\n                        {\n                            Debug.Console(2, this, \"Server asking for shared key, sending\");\n                            SendText(SharedKey + \"\\n\");\n                        }\n                        else if (SharedKeyRequired && str == \"Shared Key Match\")\n                        {\n                            StopWaitForSharedKeyTimer();\n\n\n                            Debug.Console(2, this, \"Shared key confirmed. Ready for communication\");\n                            OnClientReadyForcommunications(true); // Successful key exchange\n                        }\n                        else\n                        {\n                            //var bytesHandler = BytesReceived;\n                            //if (bytesHandler != null)\n                            //    bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));\n                            var textHandler = TextReceived;\n                            if (textHandler != null)\n                                textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));\n                            if (handler != null)\n                            {\n                                MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));\n                            }\n                        }\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(1, this, \"Error receiving data: {1}. Error: {0}\", ex.Message, str);\n                }\n                if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    client.ReceiveDataAsync(Receive);\n\n                //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread. \n                if (handler != null)\n                {\n                    var gotLock = DequeueLock.TryEnter();\n                    if (gotLock)\n                        CrestronInvoke.BeginInvoke((o) => DequeueEvent());\n                }\n            }\n            else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync\n            {\n                client.DisconnectFromServer();\n            }\n        }\n\n        /// <summary>\n        /// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.\n        /// It will dequeue items as they are enqueued automatically.\n        /// </summary>\n        void DequeueEvent()\n        {\n            try\n            {\n                while (true)\n                {\n                    // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.\n                    var message = MessageQueue.Dequeue();\n                    var handler = TextReceivedQueueInvoke;\n                    if (handler != null)\n                    {\n                        handler(this, message);\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                this.LogException(e, \"DequeueEvent error\");\n            }\n            // Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.\n            if (DequeueLock != null)\n            {\n                DequeueLock.Leave();\n            }\n        }\n\n        void HeartbeatStart()\n        {\n            if (HeartbeatEnabled)\n            {\n                Debug.Console(2, this, \"Starting Heartbeat\");\n                if (HeartbeatSendTimer == null)\n                {\n\n                    HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);\n                }\n                if (HeartbeatAckTimer == null)\n                {\n                    HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));\n                }\n            }\n\n        }\n        void HeartbeatStop()\n        {\n\n            if (HeartbeatSendTimer != null)\n            {\n                Debug.Console(2, this, \"Stoping Heartbeat Send\");\n                HeartbeatSendTimer.Stop();\n                HeartbeatSendTimer = null;\n            }\n            if (HeartbeatAckTimer != null)\n            {\n                Debug.Console(2, this, \"Stoping Heartbeat Ack\");\n                HeartbeatAckTimer.Stop();\n                HeartbeatAckTimer = null;\n            }\n\n        }\n        void SendHeartbeat(object notused)\n        {\n            this.SendText(HeartbeatString);\n            Debug.Console(2, this, \"Sending Heartbeat\");\n\n        }\n\n        //private method to check heartbeat requirements and start or reset timer\n        string checkHeartbeat(string received)\n        {\n            try\n            {\n                if (HeartbeatEnabled)\n                {\n                    if (!string.IsNullOrEmpty(HeartbeatString))\n                    {\n                        var remainingText = received.Replace(HeartbeatString, \"\");\n                        var noDelimiter = received.Trim(new char[] { '\\r', '\\n' });\n                        if (noDelimiter.Contains(HeartbeatString))\n                        {\n                            if (HeartbeatAckTimer != null)\n                            {\n                                HeartbeatAckTimer.Reset(HeartbeatInterval * 2);\n                            }\n                            else\n                            {\n                                HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));\n                            }\n                            Debug.Console(2, this, \"Heartbeat Received: {0}, from Server\", HeartbeatString);\n                            return remainingText;\n                        }\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(1, this, \"Error checking heartbeat: {0}\", ex.Message);\n            }\n            return received;\n        }\n\n\n\n        void HeartbeatAckTimerFail(object o)\n        {\n            try\n            {\n\n                if (IsConnected)\n                {\n                    Debug.Console(1, Debug.ErrorLogLevel.Warning, \"Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE\");\n                    SendText(\"Heartbeat not received by server, closing connection\");\n                    CheckClosedAndTryReconnect();\n                }\n\n            }\n            catch (Exception ex)\n            {\n                ErrorLog.Error(\"Heartbeat timeout Error on Client: {0}, {1}\", Key, ex);\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void StopWaitForSharedKeyTimer()\n        {\n            if (WaitForSharedKey != null)\n            {\n                WaitForSharedKey.Stop();\n                WaitForSharedKey = null;\n            }\n        }\n\n        /// <summary>\n        /// SendText method\n        /// </summary>\n        public void SendText(string text)\n        {\n            if (!string.IsNullOrEmpty(text))\n            {\n                try\n                {\n                    var bytes = Encoding.GetEncoding(28591).GetBytes(text);\n                    if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    {\n                        Client.SendDataAsync(bytes, bytes.Length, (c, n) =>\n                        {\n                            // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????\n                            if (n <= 0)\n                            {\n                                Debug.Console(1, Debug.ErrorLogLevel.Warning, \"[{0}] Sent zero bytes. Was there an error?\", this.Key);\n                            }\n                        });\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(0, this, \"Error sending text: {1}. Error: {0}\", ex.Message, text);\n                }\n            }\n        }\n\n        /// <summary>\n        /// SendBytes method\n        /// </summary>\n        public void SendBytes(byte[] bytes)\n        {\n            if (bytes.Length > 0)\n            {\n                try\n                {\n                    if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                        Client.SendData(bytes, bytes.Length);\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(0, this, \"Error sending bytes. Error: {0}\", ex.Message);\n                }\n            }\n        }\n\n        /// <summary>\n        /// SocketStatusChange Callback \n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <param name=\"clientSocketStatus\"></param>\n        void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)\n        {\n            if (ProgramIsStopping)\n            {\n                ProgramIsStopping = false;\n                return;\n            }\n            try\n            {\n                Debug.Console(2, this, \"Socket status change: {0} ({1})\", client.ClientStatus, (ushort)(client.ClientStatus));\n\n                OnConnectionChange();\n                // The client could be null or disposed by this time...\n                if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)\n                {\n                    HeartbeatStop();\n                    OnClientReadyForcommunications(false); // socket has gone low\n                    CheckClosedAndTryReconnect();\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Error in socket status change callback. Error: {0}\\r\\r{1}\", ex, ex.InnerException);\n            }\n        }\n\n        /// <summary>\n        /// Helper for ConnectionChange event\n        /// </summary>\n        void OnConnectionChange()\n        {\n            var handler = ConnectionChange;\n            if (handler != null)\n                ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));\n        }\n\n        /// <summary>\n        /// Helper to fire ClientReadyForCommunications event\n        /// </summary>\n        void OnClientReadyForcommunications(bool isReady)\n        {\n            IsReadyForCommunication = isReady;\n            if (this.IsReadyForCommunication) { HeartbeatStart(); }\n            var handler = ClientReadyForCommunications;\n            if (handler != null)\n                handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));\n        }\n        #endregion\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericSecureTcpIpServer.cs",
    "content": "﻿/*PepperDash Technology Corp.\nJAG\nCopyright:\t\t2017\n------------------------------------\n***Notice of Ownership and Copyright***\nThe material in which this notice appears is the property of PepperDash Technology Corporation,\nwhich claims copyright under the laws of the United States of America in the entire body of material\nand in all parts thereof, regardless of the use to which it is being put.  Any use, in whole or in part,\nof this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.\nPepperDash Technology Corporation reserves all rights under applicable laws.\n------------------------------------ */\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing PepperDash.Core.Logging;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Generic secure TCP/IP server\n    /// </summary>\n    public class GenericSecureTcpIpServer : Device\n    {\n        #region Events\n        /// <summary>\n        /// Event for Receiving text\n        /// </summary>\n        public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread. \n        /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event. \n        /// </summary>\n        public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceivedQueueInvoke;\n\n        /// <summary>\n        /// Event for client connection socket status change\n        /// </summary>\n        public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ClientConnectionChange;\n\n        /// <summary>\n        /// Event for Server State Change\n        /// </summary>\n        public event EventHandler<GenericTcpServerStateChangedEventArgs> ServerStateChange;\n\n        /// <summary>\n        /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire\n        /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key. \n        /// </summary>\n        public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ServerClientReadyForCommunications;\n\n        /// <summary>\n        /// A band aid event to notify user that the server has choked.\n        /// </summary>\n        public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }\n\n        /// <summary>\n        /// Delegate for ServerHasChokedCallbackDelegate\n        /// </summary>\n        public delegate void ServerHasChokedCallbackDelegate();\n\n        #endregion\n\n        #region Properties/Variables\n\n        /// <summary>\n        /// Server listen lock\n        /// </summary>\n        CCriticalSection ServerCCSection = new CCriticalSection();\n\n        /// <summary>\n        /// Queue lock\n        /// </summary>\n        CCriticalSection DequeueLock = new CCriticalSection();\n\n        /// <summary>\n        /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before\n        /// calling initialize. \n        /// </summary>\n        public int ReceiveQueueSize { get; set; }\n\n        /// <summary>\n        /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before\n        /// calling initialize. \n        /// </summary>\n        private CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs> MessageQueue;\n\n        /// <summary>\n        /// A bandaid client that monitors whether the server is reachable\n        /// </summary>\n        GenericSecureTcpIpClient_ForServer MonitorClient;\n\n        /// <summary>\n        /// Timer to operate the bandaid monitor client in a loop.\n        /// </summary>\n        CTimer MonitorClientTimer;\n\n        /// <summary>\n        /// \n        /// </summary>\n        int MonitorClientFailureCount;\n\n        /// <summary>\n        /// Gets or sets the MonitorClientMaxFailureCount\n        /// </summary>\n        public int MonitorClientMaxFailureCount { get; set; }\n\n        /// <summary>\n        /// Text representation of the Socket Status enum values for the server\n        /// </summary>\n        public string Status\n        {\n            get\n            {\n                if (SecureServer != null)\n                    return SecureServer.State.ToString();\n                return ServerState.SERVER_NOT_LISTENING.ToString();\n\n            }\n\n        }\n\n        /// <summary>\n        /// Bool showing if socket is connected\n        /// </summary>\n        public bool IsConnected\n        {\n            get\n            {\n                if (SecureServer != null)\n                    return (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;\n                return false;\n\n                //return (Secure ? SecureServer != null : UnsecureServer != null) && \n                //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :\n                //          (UnsecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED); \n            }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return (ushort)(IsConnected ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// Bool showing if socket is connected\n        /// </summary>\n        public bool IsListening\n        {\n            get\n            {\n                if (SecureServer != null)\n                    return (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;\n                else\n                    return false;\n                //return (Secure ? SecureServer != null : UnsecureServer != null) &&\n                //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :\n                //          (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);\n            }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsListening\n        {\n            get { return (ushort)(IsListening ? 1 : 0); }\n        }\n        /// <summary>\n        /// Max number of clients this server will allow for connection. Crestron max is 64. This number should be less than 65 \n        /// </summary>\n        public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable\n        /// <summary>\n        /// Number of clients currently connected.\n        /// </summary>\n        public ushort NumberOfClientsConnected\n        {\n            get\n            {\n                if (SecureServer != null)\n                    return (ushort)SecureServer.NumberOfClientsConnected;\n                return 0;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// S+ helper for Port\n        /// </summary>\n        public ushort UPort\n        {\n            get { return Convert.ToUInt16(Port); }\n            set { Port = Convert.ToInt32(value); }\n        }\n\n        /// <summary>\n        /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client\n        /// </summary>\n        public bool SharedKeyRequired { get; set; }\n\n        /// <summary>\n        /// S+ helper for requires shared key bool\n        /// </summary>\n        public ushort USharedKeyRequired\n        {\n            set\n            {\n                if (value == 1)\n                    SharedKeyRequired = true;\n                else\n                    SharedKeyRequired = false;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the SharedKey\n        /// </summary>\n        public string SharedKey { get; set; }\n\n        /// <summary>\n        /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received\n        /// </summary>\n        public bool HeartbeatRequired { get; set; }\n\n        /// <summary>\n        /// S+ Helper for Heartbeat Required\n        /// </summary>\n        public ushort UHeartbeatRequired\n        {\n            set\n            {\n                if (value == 1)\n                    HeartbeatRequired = true;\n                else\n                    HeartbeatRequired = false;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the HeartbeatRequiredIntervalMs\n        /// </summary>\n        public int HeartbeatRequiredIntervalMs { get; set; }\n\n        /// <summary>\n        /// Simpl+ Heartbeat Analog value in seconds\n        /// </summary>\n        public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }\n\n        /// <summary>\n        /// Gets or sets the HeartbeatStringToMatch\n        /// </summary>\n        public string HeartbeatStringToMatch { get; set; }\n\n        //private timers for Heartbeats per client\n        Dictionary<uint, CTimer> HeartbeatTimerDictionary = new Dictionary<uint, CTimer>();\n\n        //flags to show the secure server is waiting for client at index to send the shared key\n        List<uint> WaitingForSharedKey = new List<uint>();\n\n        List<uint> ClientReadyAfterKeyExchange = new List<uint>();\n\n        /// <summary>\n        /// The connected client indexes\n        /// </summary>\n        public List<uint> ConnectedClientsIndexes = new List<uint>();\n\n        /// <summary>\n        /// Gets or sets the BufferSize\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// Private flag to note that the server has stopped intentionally\n        /// </summary>\n        private bool ServerStopped { get; set; }\n\n        //Servers\n        SecureTCPServer SecureServer;\n\n        /// <summary>\n        /// \n        /// </summary>\n        bool ProgramIsStopping;\n\n        #endregion\n\n        #region Constructors\n        /// <summary>\n        /// constructor S+ Does not accept a key. Use initialze with key to set the debug key on this device. If using with + make sure to set all properties manually.\n        /// </summary>\n        public GenericSecureTcpIpServer()\n            : base(\"Uninitialized Secure TCP Server\")\n        {\n            HeartbeatRequiredIntervalInSeconds = 15;\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            BufferSize = 2000;\n            MonitorClientMaxFailureCount = 3;\n        }\n\n        /// <summary>\n        /// constructor with debug key set at instantiation. Make sure to set all properties before listening. \n        /// </summary>\n        /// <param name=\"key\"></param>\n        public GenericSecureTcpIpServer(string key)\n            : base(\"Uninitialized Secure TCP Server\")\n        {\n            HeartbeatRequiredIntervalInSeconds = 15;\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            BufferSize = 2000;\n            MonitorClientMaxFailureCount = 3;\n            Key = key;\n        }\n\n        /// <summary>\n        /// Contstructor that sets all properties by calling the initialize method with a config object. This does set Queue size. \n        /// </summary>\n        /// <param name=\"serverConfigObject\"></param>\n        public GenericSecureTcpIpServer(TcpServerConfigObject serverConfigObject)\n            : base(\"Uninitialized Secure TCP Server\")\n        {\n            HeartbeatRequiredIntervalInSeconds = 15;\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            BufferSize = 2000;\n            MonitorClientMaxFailureCount = 3;\n            Initialize(serverConfigObject);\n        }\n        #endregion\n\n        #region Methods - Server Actions\n        /// <summary>\n        /// KillServer method\n        /// </summary>\n        public void KillServer()\n        {\n            ServerStopped = true;\n            if (MonitorClient != null)\n            {\n                MonitorClient.Disconnect();\n            }\n            DisconnectAllClientsForShutdown();\n            StopListening();\n        }\n\n        /// <summary>\n        /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public void Initialize(string key)\n        {\n            Key = key;\n        }\n\n        /// <summary>\n        /// Initialze the server\n        /// </summary>\n        /// <param name=\"serverConfigObject\"></param>\n        public void Initialize(TcpServerConfigObject serverConfigObject)\n        {\n            try\n            {\n                if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))\n                {\n                    Key = serverConfigObject.Key;\n                    MaxClients = serverConfigObject.MaxClients;\n                    Port = serverConfigObject.Port;\n                    SharedKeyRequired = serverConfigObject.SharedKeyRequired;\n                    SharedKey = serverConfigObject.SharedKey;\n                    HeartbeatRequired = serverConfigObject.HeartbeatRequired;\n                    HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;\n                    HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;\n                    BufferSize = serverConfigObject.BufferSize;\n                    ReceiveQueueSize = serverConfigObject.ReceiveQueueSize > 20 ? serverConfigObject.ReceiveQueueSize : 20;\n                    MessageQueue = new CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs>(ReceiveQueueSize);\n                }\n                else\n                {\n                    ErrorLog.Error(\"Could not initialize server with key: {0}\", serverConfigObject.Key);\n                }\n            }\n            catch\n            {\n                ErrorLog.Error(\"Could not initialize server with key: {0}\", serverConfigObject.Key);\n            }\n        }\n\n        /// <summary>\n        /// Listen method\n        /// </summary>\n        public void Listen()\n        {\n            ServerCCSection.Enter();\n            try\n            {\n                if (Port < 1 || Port > 65535)\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Server '{0}': Invalid port\", Key);\n                    ErrorLog.Warn(string.Format(\"Server '{0}': Invalid port\", Key));\n                    return;\n                }\n                if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Server '{0}': No Shared Key set\", Key);\n                    ErrorLog.Warn(string.Format(\"Server '{0}': No Shared Key set\", Key));\n                    return;\n                }\n\n\n\t\t\t\t\tif (SecureServer == null)\n\t\t\t\t\t{\n\t\t\t\t\t\tSecureServer = new SecureTCPServer(Port, MaxClients);\n\t\t\t\t\t\tif (HeartbeatRequired)\n\t\t\t\t\t\t\tSecureServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);\n\t\t\t\t\t\tSecureServer.HandshakeTimeout = 30;\n\t\t\t\t\t\tSecureServer.SocketStatusChange += new SecureTCPServerSocketStatusChangeEventHandler(SecureServer_SocketStatusChange);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tSecureServer.PortNumber = Port;\n\t\t\t\t\t}\n\t\t\t\t\tServerStopped = false;\n\n\t\t\t\t\t// Start the listner\n\t\t\t\t\tSocketErrorCodes status = SecureServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);\n\t\t\t\t\tif (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.Console(0, this, Debug.ErrorLogLevel.Error, \"Error starting WaitForConnectionAsync {0}\", status);\n\t\t\t\t\t}\n\t\t\t\telse \n\t\t\t\t{\n\t\t\t\t\tServerStopped = false;\n\t\t\t\t} \n\t\t\t\tOnServerStateChange(SecureServer.State);\n\t\t\t\tDebug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Secure Server Status: {0}, Socket Status: {1}\", SecureServer.State, SecureServer.ServerSocketStatus);\n\t\t\t\tServerCCSection.Leave();\n\t\t\t\n            }\n            catch (Exception ex)\n            {\n                ServerCCSection.Leave();\n                ErrorLog.Error(\"{1} Error with Dynamic Server: {0}\", ex.ToString(), Key);\n            }\n        }\n\n        /// <summary>\n        /// StopListening method\n        /// </summary>\n        public void StopListening()\n        {\n\t\t\ttry\n\t\t\t{\n\t\t\t\tDebug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Stopping Listener\");\n\t\t\t\tif (SecureServer != null)\n\t\t\t\t{\n\t\t\t\t\tSecureServer.Stop();\n\t\t\t\t\tDebug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Server State: {0}\", SecureServer.State);\n\t\t\t\t\tOnServerStateChange(SecureServer.State);\n\t\t\t\t}\n\t\t\t\tServerStopped = true;\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tDebug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error stopping server. Error: {0}\", ex);\n\t\t\t}\n        }\n\n        /// <summary>\n        /// Disconnects Client\n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <summary>\n        /// DisconnectClient method\n        /// </summary>\n        public void DisconnectClient(uint client)\n        {\n            try\n            {\n                SecureServer.Disconnect(client);\n                Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Disconnected client index: {0}\", client);\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error Disconnecting client index: {0}. Error: {1}\", client, ex);\n            }\n        }\n        /// <summary>\n        /// DisconnectAllClientsForShutdown method\n        /// </summary>\n        public void DisconnectAllClientsForShutdown()\n        {\n            Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Disconnecting All Clients\");\n            if (SecureServer != null)\n            {\n                SecureServer.SocketStatusChange -= SecureServer_SocketStatusChange;\n                foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly\n                {\n                    var i = index;\n                    if (!SecureServer.ClientConnected(index))\n                        continue;\n                    try\n                    {\n                        SecureServer.Disconnect(i);\n                        Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Disconnected client index: {0}\", i);\n                    }\n                    catch (Exception ex)\n                    {\n                        Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error Disconnecting client index: {0}. Error: {1}\", i, ex);\n                    }\n                }\n                Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Server Status: {0}\", SecureServer.ServerSocketStatus);\n            }\n\n            Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Disconnected All Clients\");\n            ConnectedClientsIndexes.Clear();\n\n            if (!ProgramIsStopping)\n            {\n                OnConnectionChange();\n                OnServerStateChange(SecureServer.State); //State shows both listening and connected\n            }\n\n           // var o = new { };\n        }\n\n        /// <summary>\n        /// Broadcast text from server to all connected clients\n        /// </summary>\n        /// <param name=\"text\"></param>\n        /// <summary>\n        /// BroadcastText method\n        /// </summary>\n        public void BroadcastText(string text)\n        {\n            CCriticalSection CCBroadcast = new CCriticalSection();\n            CCBroadcast.Enter();\n            try\n            {\n                if (ConnectedClientsIndexes.Count > 0)\n                {\n                    byte[] b = Encoding.GetEncoding(28591).GetBytes(text);\n                    foreach (uint i in ConnectedClientsIndexes)\n                    {\n                        if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))\n                        {\n                            SocketErrorCodes error = SecureServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });\n                            if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)\n                                this.LogVerbose(\"{error}\", error);\n                        }\n                    }\n                }\n                CCBroadcast.Leave();\n            }\n            catch (Exception ex)\n            {\n                CCBroadcast.Leave();\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error Broadcasting messages from server. Error: {0}\", ex.Message);\n            }\n        }\n\n        /// <summary>\n        /// Not sure this is useful in library, maybe Pro??\n        /// </summary>\n        /// <param name=\"text\"></param>\n        /// <param name=\"clientIndex\"></param>\n        /// <summary>\n        /// SendTextToClient method\n        /// </summary>\n        public void SendTextToClient(string text, uint clientIndex)\n        {\n            try\n            {\n                byte[] b = Encoding.GetEncoding(28591).GetBytes(text);\n                if (SecureServer != null && SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)\n                {\n                    if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))\n                        SecureServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, \"Error sending text to client. Text: {1}. Error: {0}\", ex.Message, text);\n            }\n        }\n\n        //private method to check heartbeat requirements and start or reset timer\n        string checkHeartbeat(uint clientIndex, string received)\n        {\n            try\n            {\n                if (HeartbeatRequired)\n                {\n                    if (!string.IsNullOrEmpty(HeartbeatStringToMatch))\n                    {\n                        var remainingText = received.Replace(HeartbeatStringToMatch, \"\");\n                        var noDelimiter = received.Trim(new char[] { '\\r', '\\n' });\n                        if (noDelimiter.Contains(HeartbeatStringToMatch))\n                        {\n                            if (HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                                HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);\n                            else\n                            {\n                                CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);\n                                HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);\n                            }\n                            Debug.Console(1, this, \"Heartbeat Received: {0}, from client index: {1}\", HeartbeatStringToMatch, clientIndex);\n                            // Return Heartbeat\n                            SendTextToClient(HeartbeatStringToMatch, clientIndex);\n                            return remainingText;\n                        }\n                    }\n                    else\n                    {\n                        if (HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                            HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);\n                        else\n                        {\n                            CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);\n                            HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);\n                        }\n                        Debug.Console(1, this, \"Heartbeat Received: {0}, from client index: {1}\", received, clientIndex);\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(1, this, \"Error checking heartbeat: {0}\", ex.Message);\n            }\n            return received;\n        }\n\n        /// <summary>\n        /// Get the IP Address for the client at the specifed index\n        /// </summary>\n        /// <param name=\"clientIndex\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetClientIPAddress method\n        /// </summary>\n        public string GetClientIPAddress(uint clientIndex)\n        {\n            Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"GetClientIPAddress Index: {0}\", clientIndex);\n            if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))\n            {\n                var ipa = this.SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"GetClientIPAddress IPAddreess: {0}\", ipa);\n                return ipa;\n\n            }\n            else\n            {\n                return \"\";\n            }\n        }\n\n        #endregion\n\n        #region Methods - HeartbeatTimer Callback\n\n        void HeartbeatTimer_CallbackFunction(object o)\n        {\n            uint clientIndex = 99999;\n            string address = string.Empty;\n            try\n            {\n                clientIndex = (uint)o;\n                address = SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);\n\n                Debug.Console(1, this, Debug.ErrorLogLevel.Warning, \"Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}\",\n                    address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? \"\" : (\"HeartbeatStringToMatch: \" + HeartbeatStringToMatch), clientIndex);\n\n                if (SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    SendTextToClient(\"Heartbeat not received by server, closing connection\", clientIndex);\n\n                var discoResult = SecureServer.Disconnect(clientIndex);\n                //Debug.Console(1, this, \"{0}\", discoResult);  \n\n                if (HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                {\n                    HeartbeatTimerDictionary[clientIndex].Stop();\n                    HeartbeatTimerDictionary[clientIndex].Dispose();\n                    HeartbeatTimerDictionary.Remove(clientIndex);\n                }\n            }\n            catch (Exception ex)\n            {\n                ErrorLog.Error(\"{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}\", clientIndex, address, ex.Message, Key);\n            }\n        }\n\n        #endregion\n\n        #region Methods - Socket Status Changed Callbacks\n        /// <summary>\n        /// Secure Server Socket Status Changed Callback\n        /// </summary>\n        /// <param name=\"server\"></param>\n        /// <param name=\"clientIndex\"></param>\n        /// <param name=\"serverSocketStatus\"></param>\n        void SecureServer_SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)\n        {\n            try\n            {\n\t\t\t\t\n\n               // Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}\", clientIndex, serverSocketStatus, this.SecureServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.SecureServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));\n                if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)\n                {\n\t\t\t\t\tDebug.Console(1, this, Debug.ErrorLogLevel.Notice, \"SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}\", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);\n                \n                    if (ConnectedClientsIndexes.Contains(clientIndex))\n                        ConnectedClientsIndexes.Remove(clientIndex);\n                    if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                    {\n                        HeartbeatTimerDictionary[clientIndex].Stop();\n                        HeartbeatTimerDictionary[clientIndex].Dispose();\n                        HeartbeatTimerDictionary.Remove(clientIndex);\n                    }\n                    if (ClientReadyAfterKeyExchange.Contains(clientIndex))\n                        ClientReadyAfterKeyExchange.Remove(clientIndex);\n\t\t\t\t\tif (WaitingForSharedKey.Contains(clientIndex))\n\t\t\t\t\t\tWaitingForSharedKey.Remove(clientIndex);\n\t\t\t\t\tif (SecureServer.MaxNumberOfClientSupported > SecureServer.NumberOfClientsConnected)\n\t\t\t\t\t{\n\t\t\t\t\t\tListen();\n\t\t\t\t\t}\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error in Socket Status Change Callback. Error: {0}\", ex);\n            }\n            //Use a thread for this event so that the server state updates to listening while this event is processed. Listening must be added to the server state\n            //after every client connection so that the server can check and see if it is at max clients. Due to this the event fires and server listening enum bit flag\n            //is not set. Putting in a thread allows the state to update before this event processes so that the subscribers to this event get accurate isListening in the event. \n            CrestronInvoke.BeginInvoke(o => onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex)), null);\n        }\n\n        #endregion\n\n        #region Methods Connected Callbacks\n        /// <summary>\n        /// Secure TCP Client Connected to Secure Server Callback\n        /// </summary>\n        /// <param name=\"server\"></param>\n        /// <param name=\"clientIndex\"></param>\n        void SecureConnectCallback(SecureTCPServer server, uint clientIndex)\n        {\n            try\n            {\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}\",\n                    server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),\n                    clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));\n                if (clientIndex != 0)\n                {\n                    if (server.ClientConnected(clientIndex))\n                    {\n\n                        if (!ConnectedClientsIndexes.Contains(clientIndex))\n                        {\n                            ConnectedClientsIndexes.Add(clientIndex);\n                        }\n                        if (SharedKeyRequired)\n                        {\n                            if (!WaitingForSharedKey.Contains(clientIndex))\n                            {\n                                WaitingForSharedKey.Add(clientIndex);\n                            }\n                            byte[] b = Encoding.GetEncoding(28591).GetBytes(\"SharedKey:\");\n                            server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });\n                            Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Sent Shared Key Request to client at {0}\", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));\n                        }\n                        else\n                        {\n                            OnServerClientReadyForCommunications(clientIndex);\n                        }\n                        if (HeartbeatRequired)\n                        {\n                            if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                            {\n                                HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));\n                            }\n                        }\n\n                        server.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);\n                    }\n                }\n                else\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Client attempt faulty.\");                    \n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error in Socket Status Connect Callback. Error: {0}\", ex);\n            }\n\n\t\t\t// Rearm the listner \n\t\t\tSocketErrorCodes status = server.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);\n\t\t\tif (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)\n\t\t\t{\n\t\t\t\tDebug.Console(0, this, Debug.ErrorLogLevel.Error, \"Socket status connect callback status {0}\", status);\n\t\t\t\tif (status == SocketErrorCodes.SOCKET_CONNECTION_IN_PROGRESS)\n\t\t\t\t{\n\t\t\t\t\t// There is an issue where on a failed negotiation we need to stop and start the server. This should still leave connected clients intact. \n\t\t\t\t\tserver.Stop();\n\t\t\t\t\tListen();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n        #endregion\n\n        #region Methods - Send/Receive Callbacks\n        /// <summary>\n        /// Secure Received Data Async Callback\n        /// </summary>\n        /// <param name=\"mySecureTCPServer\"></param>\n        /// <param name=\"clientIndex\"></param>\n        /// <param name=\"numberOfBytesReceived\"></param>\n        void SecureReceivedDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)\n        {\n            if (numberOfBytesReceived > 0)\n            {\n\t\t\t\t\n                string received = \"Nothing\";\n                var handler = TextReceivedQueueInvoke;\n                try\n                {\n                    byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);\n                    received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);\n                    if (WaitingForSharedKey.Contains(clientIndex))\n                    {\n                        received = received.Replace(\"\\r\", \"\");\n                        received = received.Replace(\"\\n\", \"\");\n                        if (received != SharedKey)\n                        {\n                            byte[] b = Encoding.GetEncoding(28591).GetBytes(\"Shared key did not match server. Disconnecting\");\n                            Debug.Console(1, this, Debug.ErrorLogLevel.Warning, \"Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}\", clientIndex, received);\n                            mySecureTCPServer.SendData(clientIndex, b, b.Length);\n                            mySecureTCPServer.Disconnect(clientIndex);\n                            \n                            return;\n                        }\n\n                        WaitingForSharedKey.Remove(clientIndex);\n                        byte[] success = Encoding.GetEncoding(28591).GetBytes(\"Shared Key Match\");\n                        mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);\n                        OnServerClientReadyForCommunications(clientIndex);\n                        Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Client with index {0} provided the shared key and successfully connected to the server\", clientIndex);                        \n                    }\n                    else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))\n                    {\n                        onTextReceived(received, clientIndex);\n                        if (handler != null)\n                        {\n                            MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(received, clientIndex));\n                        }\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error Receiving data: {0}. Error: {1}\", received, ex);\n                }\n\t\t\t\tif (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)\n\t\t\t\t\tmySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);\n\n                //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread. \n                if (handler != null)\n                {\n                    var gotLock = DequeueLock.TryEnter();\n                    if (gotLock)\n                        CrestronInvoke.BeginInvoke((o) => DequeueEvent());\n                }\n            }\n\t\t\telse\n\t\t\t{\n\t\t\t\tmySecureTCPServer.Disconnect(clientIndex);\n            }\n        }\n\n        /// <summary>\n        /// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.\n        /// It will dequeue items as they are enqueued automatically.\n        /// </summary>\n        void DequeueEvent()\n        {\n            try\n            {\n                while (true)\n                {\n                    // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.\n                    var message = MessageQueue.Dequeue();\n                    var handler = TextReceivedQueueInvoke;\n                    if (handler != null)\n                    {\n                        handler(this, message);\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                this.LogException(e, \"DequeueEvent error\");\n            }\n            // Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.\n            if (DequeueLock != null)\n            {\n                DequeueLock.Leave();\n            }\n        }\n\n        #endregion\n\n        #region Methods - EventHelpers/Callbacks\n\n        //Private Helper method to call the Connection Change Event\n        void onConnectionChange(uint clientIndex, SocketStatus clientStatus)\n        {\n            if (clientIndex != 0) //0 is error not valid client change\n            {\n                var handler = ClientConnectionChange;\n                if (handler != null)\n                {\n                    handler(this, new GenericTcpServerSocketStatusChangeEventArgs(SecureServer, clientIndex, clientStatus));\n                }\n            }\n        }\n\n        //Private Helper method to call the Connection Change Event\n        void OnConnectionChange()\n        {\n            if (ProgramIsStopping)\n            {\n                return;\n            }\n            var handler = ClientConnectionChange;\n            if (handler != null)\n            {\n                handler(this, new GenericTcpServerSocketStatusChangeEventArgs());\n            }\n        }\n\n        //Private Helper Method to call the Text Received Event\n        void onTextReceived(string text, uint clientIndex)\n        {\n            var handler = TextReceived;\n            if (handler != null)\n                handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));\n        }\n\n        //Private Helper Method to call the Server State Change Event\n        void OnServerStateChange(ServerState state)\n        {\n            if (ProgramIsStopping)\n            {\n                return;\n            }\n            var handler = ServerStateChange;\n            if (handler != null)\n            {\n                handler(this, new GenericTcpServerStateChangedEventArgs(state));\n            }\n        }\n\n        /// <summary>\n        /// Private Event Handler method to handle the closing of connections when the program stops\n        /// </summary>\n        /// <param name=\"programEventType\"></param>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping)\n            {\n                ProgramIsStopping = true;\n                // kill bandaid things\n                if (MonitorClientTimer != null)\n                    MonitorClientTimer.Stop();\n                if (MonitorClient != null)\n                    MonitorClient.Disconnect();\n\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Program stopping. Closing server\");\n                KillServer();\n            }\n        }\n\n        //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation\n        void OnServerClientReadyForCommunications(uint clientIndex)\n        {\n            ClientReadyAfterKeyExchange.Add(clientIndex);\n            var handler = ServerClientReadyForCommunications;\n            if (handler != null)\n                handler(this, new GenericTcpServerSocketStatusChangeEventArgs(\n                    this, clientIndex, SecureServer.GetServerSocketStatusForSpecificClient(clientIndex)));\n        }\n        #endregion\n\n        #region Monitor Client\n        /// <summary>\n        /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient\n        /// </summary>\n        void StartMonitorClient()\n        {\n            if (MonitorClientTimer != null)\n            {\n                return;\n            }\n            MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void RunMonitorClient()\n        {\n            MonitorClient = new GenericSecureTcpIpClient_ForServer(Key + \"-MONITOR\", \"127.0.0.1\", Port, 2000);\n            MonitorClient.SharedKeyRequired = this.SharedKeyRequired;\n            MonitorClient.SharedKey = this.SharedKey;\n            MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;\n            //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;\n            MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;\n\n            Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Starting monitor check\");\n\n            MonitorClient.Connect();\n            // From here MonitorCLient either connects or hangs, MonitorClient will call back \n\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void StopMonitorClient()\n        {\n            if (MonitorClient == null)\n                return;\n\n            MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;\n            MonitorClient.Disconnect();\n            MonitorClient = null;\n        }\n\n        /// <summary>\n        /// On monitor connect, restart the operation\n        /// </summary>\n        void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)\n        {\n            if (args.IsReady)\n            {\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Monitor client connection success. Disconnecting in 2s\");\n                MonitorClientTimer.Stop();\n                MonitorClientTimer = null;\n                MonitorClientFailureCount = 0;\n                CrestronEnvironment.Sleep(2000);\n                StopMonitorClient();\n                StartMonitorClient();\n            }\n        }\n\n        /// <summary>\n        /// If the client hangs, add to counter and maybe fire the choke event\n        /// </summary>\n        void MonitorClientHasHungCallback()\n        {\n            MonitorClientFailureCount++;\n            MonitorClientTimer.Stop();\n            MonitorClientTimer = null;\n            StopMonitorClient();\n            if (MonitorClientFailureCount < MonitorClientMaxFailureCount)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Warning, \"Monitor client connection has hung {0} time{1}, maximum {2}\",\n                    MonitorClientFailureCount, MonitorClientFailureCount > 1 ? \"s\" : \"\", MonitorClientMaxFailureCount);\n                StartMonitorClient();\n            }\n            else\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error,\n                    \"\\r***************************\\rMonitor client connection has hung a maximum of {0} times. \\r***************************\",\n                    MonitorClientMaxFailureCount);\n\n                var handler = ServerHasChoked;\n                if (handler != null)\n                    handler();\n                // Some external thing is in charge here.  Expected reset of program\n            }\n        }\n        #endregion\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericSshClient.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing Org.BouncyCastle.Utilities;\nusing PepperDash.Core.Logging;\nusing Renci.SshNet;\nusing Renci.SshNet.Common;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// SSH Client\n    /// </summary>\n    public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect\n    {\n        private const string SPlusKey = \"Uninitialized SshClient\";\n\n        /// <summary>\n        /// Object to enable stream debugging\n        /// </summary>\n        public CommunicationStreamDebugging StreamDebugging { get; private set; }\n\n        /// <summary>\n        /// Event that fires when data is received.  Delivers args with byte array\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// Event that fires when data is received.  Delivered as text.\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Event when the connection status changes.\n        /// </summary>\n        public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;\n\n        /// <summary>\n        /// Gets or sets the Hostname\n        /// </summary>\n        public string Hostname { get; set; }\n\n        /// <summary>\n        /// Port on server\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Username\n        /// </summary>\n        public string Username { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Password\n        /// </summary>\n        public string Password { get; set; }\n\n        /// <summary>\n        /// True when the server is connected - when status == 2.\n        /// </summary>\n        public bool IsConnected\n        {\n            // returns false if no client or not connected\n            get { return client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return (ushort)(IsConnected ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// Socket status change event\n        /// </summary>\n        public SocketStatus ClientStatus\n        {\n            get { lock (_stateLock) { return _ClientStatus; } }\n            private set\n            {\n                bool shouldFireEvent = false;\n                lock (_stateLock)\n                {\n                    if (_ClientStatus != value)\n                    {\n                        _ClientStatus = value;\n                        shouldFireEvent = true;\n                    }\n                }\n                // Fire event outside lock to avoid deadlock\n                if (shouldFireEvent)\n                    OnConnectionChange();\n            }\n        }\n\n        private SocketStatus _ClientStatus;\n        private bool _ConnectEnabled;\n\n        /// <summary>\n        /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event\n        /// and IsConnected with be true when this == 2.\n        /// </summary>\n        public ushort UStatus\n        {\n            get { lock (_stateLock) { return (ushort)_ClientStatus; } }\n        }\n\n        /// <summary>\n        /// Determines whether client will attempt reconnection on failure. Default is true\n        /// </summary>\n        public bool AutoReconnect { get; set; }\n\n        /// <summary>\n        /// Will be set and unset by connect and disconnect only\n        /// </summary>\n        public bool ConnectEnabled\n        {\n            get { lock (_stateLock) { return _ConnectEnabled; } }\n            private set { lock (_stateLock) { _ConnectEnabled = value; } }\n        }\n\n        /// <summary>\n        /// S+ helper for AutoReconnect\n        /// </summary>\n        public ushort UAutoReconnect\n        {\n            get { return (ushort)(AutoReconnect ? 1 : 0); }\n            set { AutoReconnect = value == 1; }\n        }\n\n        /// <summary>\n        /// Gets or sets the AutoReconnectIntervalMs\n        /// </summary>\n        public int AutoReconnectIntervalMs { get; set; }\n\n        private SshClient client;\n\n        private ShellStream shellStream;\n\n        private readonly Timer reconnectTimer;\n\n        //Lock object to prevent simulatneous connect/disconnect operations\n        //private CCriticalSection connectLock = new CCriticalSection();\n        private readonly SemaphoreSlim connectLock = new SemaphoreSlim(1);\n\n        // Thread-safety lock for state changes\n        private readonly object _stateLock = new object();\n\n        private bool disconnectLogged = false;\n\n        /// <summary>\n        /// When true, turns off echo for the SSH session\n        /// </summary>\n        public bool DisableEcho { get; set; }\n\n        /// <summary>\n        /// Typical constructor.\n        /// </summary>\n        public GenericSshClient(string key, string hostname, int port, string username, string password) :\n            base(key)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(key);\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            Key = key;\n            Hostname = hostname;\n            Port = port;\n            Username = username;\n            Password = password;\n            AutoReconnectIntervalMs = 5000;\n\n            reconnectTimer = new Timer(o =>\n                {\n                    if (ConnectEnabled) // Now thread-safe property access\n                    {\n                        Connect();\n                    }\n                }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);\n        }\n\n        /// <summary>\n        /// S+ Constructor - Must set all properties before calling Connect\n        /// </summary>\n        public GenericSshClient()\n            : base(SPlusKey)\n        {\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            AutoReconnectIntervalMs = 5000;\n\n            reconnectTimer = new Timer(o =>\n            {\n                if (ConnectEnabled) // Now thread-safe property access\n                {\n                    Connect();\n                }\n            }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);\n        }\n\n        /// <summary>\n        /// Handles closing this up when the program shuts down\n        /// </summary>\n        private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping)\n            {\n                if (client != null)\n                {\n                    this.LogDebug(\"Program stopping. Closing connection\");\n                    Disconnect();\n                }\n            }\n        }\n\n        /// <summary>\n        /// Connect method\n        /// </summary>\n        public void Connect()\n        {\n            // Don't go unless everything is here\n            if (string.IsNullOrEmpty(Hostname) || Port < 1 || Port > 65535\n                || Username == null || Password == null)\n            {\n                this.LogError(\"Connect failed.  Check hostname, port, username and password are set or not null\");\n                return;\n            }\n\n            ConnectEnabled = true;\n\n            try\n            {\n                connectLock.Wait();\n                if (IsConnected)\n                {\n                    this.LogDebug(\"Connection already connected.  Exiting Connect\");\n                }\n                else\n                {\n                    this.LogDebug(\"Attempting connect\");\n\n                    // Cancel reconnect if running.\n                    StopReconnectTimer();\n\n                    // Cleanup the old client if it already exists\n                    if (client != null)\n                    {\n                        this.LogDebug(\"Cleaning up disconnected client\");\n                        KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);\n                    }\n\n                    // This handles both password and keyboard-interactive (like on OS-X, 'nixes)\n                    KeyboardInteractiveAuthenticationMethod kauth = new KeyboardInteractiveAuthenticationMethod(Username);\n                    kauth.AuthenticationPrompt += new EventHandler<AuthenticationPromptEventArgs>(kauth_AuthenticationPrompt);\n                    PasswordAuthenticationMethod pauth = new PasswordAuthenticationMethod(Username, Password);\n\n                    this.LogDebug(\"Creating new SshClient\");\n                    ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);\n                    client = new SshClient(connectionInfo);\n                    client.ErrorOccurred += Client_ErrorOccurred;\n\n                    //Attempt to connect\n                    ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;\n                    try\n                    {\n                        client.Connect();\n\n                        var modes = new Dictionary<TerminalModes, uint>();\n\n                        if (DisableEcho)\n                        {\n                            modes.Add(TerminalModes.ECHO, 0);\n                        }\n\n                        shellStream = client.CreateShellStream(\"PDTShell\", 0, 0, 0, 0, 65534, modes);\n                        if (shellStream.DataAvailable)\n                        {\n                            // empty the buffer if there is data\n                            shellStream.Read();\n                        }\n                        shellStream.DataReceived += Stream_DataReceived;\n                        this.LogInformation(\"Connected\");\n                        ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;\n                        disconnectLogged = false;\n                    }\n                    catch (SshConnectionException e)\n                    {\n                        var ie = e.InnerException; // The details are inside!!\n\n                        if (ie is SocketException)\n                        {\n                            this.LogError(\"CONNECTION failure: Cannot reach host\");\n                            this.LogVerbose(ie, \"Exception details: \");\n                        }\n\n                        if (ie is System.Net.Sockets.SocketException socketException)\n                        {\n                            this.LogError(\"Connection failure: Cannot reach {host} on {port}\",\n                                Hostname, Port);\n                            this.LogVerbose(socketException, \"SocketException details: \");\n                        }\n                        if (ie is SshAuthenticationException)\n                        {\n                            this.LogError(\"Authentication failure for username {userName}\", Username);\n                            this.LogVerbose(ie, \"AuthenticationException details: \");\n                        }\n                        else\n                        {\n                            this.LogError(\"Error on connect: {error}\", ie.Message);\n                            this.LogVerbose(ie, \"Exception details: \");\n                        }\n\n                        disconnectLogged = true;\n                        KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);\n                        if (AutoReconnect)\n                        {\n                            this.LogDebug(\"Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms\", AutoReconnect, AutoReconnectIntervalMs);\n                            StartReconnectTimer();\n                        }\n                    }\n                    catch (SshOperationTimeoutException ex)\n                    {\n                        this.LogWarning(\"Connection attempt timed out: {message}\", ex.Message);\n\n                        disconnectLogged = true;\n                        KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);\n                        if (AutoReconnect)\n                        {\n                            this.LogDebug(\"Checking autoreconnect: {0}, {1}ms\", AutoReconnect, AutoReconnectIntervalMs);\n                            StartReconnectTimer();\n                        }\n                    }\n                    catch (Exception e)\n                    {\n                        this.LogError(\"Unhandled exception on connect: {error}\", e.Message);\n                        this.LogVerbose(e, \"Exception details: \");\n                        disconnectLogged = true;\n                        KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);\n                        if (AutoReconnect)\n                        {\n                            this.LogDebug(\"Checking autoreconnect: {0}, {1}ms\", AutoReconnect, AutoReconnectIntervalMs);\n                            StartReconnectTimer();\n                        }\n                    }\n                }\n            }\n            finally\n            {\n                connectLock.Release();\n            }\n        }\n\n        /// <summary>\n        /// Disconnect method\n        /// </summary>\n        public void Disconnect()\n        {\n            ConnectEnabled = false;\n            // Stop trying reconnects, if we are\n            StopReconnectTimer();\n\n            KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);\n        }\n\n        /// <summary>\n        /// Kills the stream, cleans up the client and sets it to null\n        /// </summary>\n        private void KillClient(SocketStatus status)\n        {\n            KillStream();\n\n            try\n            {\n                if (client != null)\n                {\n                    client.ErrorOccurred -= Client_ErrorOccurred;\n                    client.Disconnect();\n                    client.Dispose();\n                    client = null;\n                    ClientStatus = status;\n                    this.LogDebug(\"Disconnected\");\n                }\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex, \"Exception in Kill Client\");\n            }\n        }\n\n        /// <summary>\n        /// Kills the stream\n        /// </summary>\n        private void KillStream()\n        {\n            try\n            {\n                if (shellStream != null)\n                {\n                    shellStream.DataReceived -= Stream_DataReceived;\n                    shellStream.Close();\n                    shellStream.Dispose();\n                    shellStream = null;\n                    this.LogDebug(\"Disconnected stream\");\n                }\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex, \"Exception in Kill Stream:{0}\");\n            }\n        }\n\n        /// <summary>\n        /// Handles the keyboard interactive authentication, should it be required.\n        /// </summary>\n        private void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)\n        {\n            foreach (AuthenticationPrompt prompt in e.Prompts)\n                if (prompt.Request.IndexOf(\"Password:\", StringComparison.InvariantCultureIgnoreCase) != -1)\n                    prompt.Response = Password;\n        }\n\n        /// <summary>\n        /// Handler for data receive on ShellStream.  Passes data across to queue for line parsing.\n        /// </summary>\n        private void Stream_DataReceived(object sender, ShellDataEventArgs e)\n        {\n            if (((ShellStream)sender).Length <= 0L)\n            {\n                return;\n            }\n            var response = ((ShellStream)sender).Read();\n\n            var bytesHandler = BytesReceived;\n\n            if (bytesHandler != null)\n            {\n                var bytes = Encoding.UTF8.GetBytes(response);\n                this.PrintReceivedBytes(bytes);\n                bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));\n            }\n\n            var textHandler = TextReceived;\n            if (textHandler != null)\n            {\n                this.PrintReceivedText(response);\n\n                textHandler(this, new GenericCommMethodReceiveTextArgs(response));\n            }\n\n        }\n\n\n        /// <summary>\n        /// Error event handler for client events - disconnect, etc.  Will forward those events via ConnectionChange\n        /// event\n        /// </summary>\n        private void Client_ErrorOccurred(object sender, ExceptionEventArgs e)\n        {\n            CrestronInvoke.BeginInvoke(o =>\n            {\n                if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)\n                    this.LogError(\"Disconnected by remote\");\n                else\n                    this.LogException(e.Exception, \"Unhandled SSH client error\");\n                try\n                {\n                    connectLock.Wait();\n                    KillClient(SocketStatus.SOCKET_STATUS_BROKEN_REMOTELY);\n                }\n                finally\n                {\n                    connectLock.Release();\n                }\n                if (AutoReconnect && ConnectEnabled)\n                {\n                    this.LogDebug(\"Checking autoreconnect: {0}, {1}ms\", AutoReconnect, AutoReconnectIntervalMs);\n                    StartReconnectTimer();\n                }\n            });\n        }\n\n        /// <summary>\n        /// Helper for ConnectionChange event\n        /// </summary>\n        private void OnConnectionChange()\n        {\n            ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));\n        }\n\n        #region IBasicCommunication Members\n\n        /// <summary>\n        /// Sends text to the server\n        /// </summary>\n        /// <param name=\"text\">The text to send</param>\n        public void SendText(string text)\n        {\n            try\n            {\n                if (client != null && shellStream != null && IsConnected)\n                {\n                    this.PrintSentText(text);\n\n                    shellStream.Write(text);\n                    shellStream.Flush();\n                }\n                else\n                {\n                    this.LogDebug(\"Client is null or disconnected.  Cannot Send Text\");\n                }\n            }\n            catch (ObjectDisposedException)\n            {\n                this.LogError(\"ObjectDisposedException sending '{message}'. Restarting connection...\", text.Trim());\n\n                KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);\n                StartReconnectTimer();\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex, \"Exception sending text: '{message}'\", text);\n            }\n        }\n\n        /// <summary>\n        /// Sends Bytes to the server\n        /// </summary>\n        /// <param name=\"bytes\">The bytes to send</param>\n        public void SendBytes(byte[] bytes)\n        {\n            try\n            {\n                if (client != null && shellStream != null && IsConnected)\n                {\n                    this.PrintSentBytes(bytes);\n\n                    shellStream.Write(bytes, 0, bytes.Length);\n                    shellStream.Flush();\n                }\n                else\n                {\n                    this.LogDebug(\"Client is null or disconnected.  Cannot Send Bytes\");\n                }\n            }\n            catch (ObjectDisposedException ex)\n            {\n                this.LogException(ex, \"ObjectDisposedException sending {message}\", ComTextHelper.GetEscapedText(bytes));\n\n                KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);\n                StartReconnectTimer();\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex, \"Exception sending {message}\", ComTextHelper.GetEscapedText(bytes));\n            }\n        }\n        #endregion\n\n        /// <summary>\n        /// Safely starts the reconnect timer with exception handling\n        /// </summary>\n        private void StartReconnectTimer()\n        {\n            try\n            {\n                reconnectTimer?.Change(AutoReconnectIntervalMs, System.Threading.Timeout.Infinite);\n            }\n            catch (ObjectDisposedException)\n            {\n                // Timer was disposed, ignore\n                this.LogDebug(\"Attempted to start timer but it was already disposed\");\n            }\n        }\n\n        /// <summary>\n        /// Safely stops the reconnect timer with exception handling\n        /// </summary>\n        private void StopReconnectTimer()\n        {\n            try\n            {\n                reconnectTimer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);\n            }\n            catch (ObjectDisposedException)\n            {\n                // Timer was disposed, ignore\n                this.LogDebug(\"Attempted to stop timer but it was already disposed\");\n            }\n        }\n\n        /// <summary>\n        /// Deactivate method - properly dispose of resources\n        /// </summary>\n        public override bool Deactivate()\n        {\n            try\n            {\n                this.LogDebug(\"Deactivating SSH client - disposing resources\");\n\n                // Stop trying reconnects\n                ConnectEnabled = false;\n                StopReconnectTimer();\n\n                // Disconnect and cleanup client\n                KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);\n\n                // Dispose timer\n                try\n                {\n                    reconnectTimer?.Dispose();\n                }\n                catch (ObjectDisposedException)\n                {\n                    // Already disposed, ignore\n                }\n\n                // Dispose semaphore\n                try\n                {\n                    connectLock?.Dispose();\n                }\n                catch (ObjectDisposedException)\n                {\n                    // Already disposed, ignore\n                }\n\n                return base.Deactivate();\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex, \"Error during SSH client deactivation\");\n                return false;\n            }\n        }\n\n    }\n\n    //*****************************************************************************************************\n    //*****************************************************************************************************\n    /// <summary>\n    /// Represents a SshConnectionChangeEventArgs\n    /// </summary>\n    public class SshConnectionChangeEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Connection State\n        /// </summary>\n\t\tpublic bool IsConnected { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the UIsConnected\n        /// </summary>\n        public ushort UIsConnected { get { return (ushort)(Client.IsConnected ? 1 : 0); } }\n\n        /// <summary>\n        /// Gets or sets the Client\n        /// </summary>\n        public GenericSshClient Client { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Status\n        /// </summary>\n        public ushort Status { get { return Client.UStatus; } }\n\n        /// <summary>\n        ///  S+ Constructor\n        /// </summary>\n        public SshConnectionChangeEventArgs() { }\n\n        /// <summary>\n        /// EventArgs class\n        /// </summary>\n        /// <param name=\"isConnected\">Connection State</param>\n        /// <param name=\"client\">The Client</param>\n\t\tpublic SshConnectionChangeEventArgs(bool isConnected, GenericSshClient client)\n        {\n            IsConnected = isConnected;\n            Client = client;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericTcpIpClient.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// A class to handle basic TCP/IP communications with a server\n    /// </summary>\n\tpublic class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect\n    {\n        private const string SplusKey = \"Uninitialized TcpIpClient\";\n        /// <summary>\n        /// Object to enable stream debugging\n        /// </summary>\n        public CommunicationStreamDebugging StreamDebugging { get; private set; }\n\n        /// <summary>\n        /// Fires when data is received from the server and returns it as a Byte array\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// Fires when data is received from the server and returns it as text\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// \n        /// </summary>\n        //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;\n        public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;\n\n\n        private string _hostname;\n\n        /// <summary>\n        /// Address of server\n        /// </summary>\n        public string Hostname\n        {\n            get\n            {\n                return _hostname;\n            }\n\n            set\n            {\n                _hostname = value;\n                if (_client != null)\n                {\n                    _client.AddressClientConnectedTo = _hostname;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// Another damn S+ helper because S+ seems to treat large port nums as signed ints\n        /// which screws up things\n        /// </summary>\n        public ushort UPort\n        {\n            get { return Convert.ToUInt16(Port); }\n            set { Port = Convert.ToInt32(value); }\n        }\n\n        /// <summary>\n        /// Defaults to 2000\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// The actual client class\n        /// </summary>\n        private TCPClient _client;\n\n        /// <summary>\n        /// Bool showing if socket is connected\n        /// </summary>\n        public bool IsConnected\n        {\n            get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return (ushort)(IsConnected ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// _client socket status Read only\n        /// </summary>\n        public SocketStatus ClientStatus\n        {\n            get\n            {\n                return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;\n            }\n        }\n\n        /// <summary>\n        /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event\n        /// and IsConnected would be true when this == 2.\n        /// </summary>\n        public ushort UStatus\n        {\n            get { return (ushort)ClientStatus; }\n        }\n\n        /// <summary>\n        /// Status text shows the message associated with socket status\n        /// </summary>\n        public string ClientStatusText { get { return ClientStatus.ToString(); } }\n\n        /// <summary>\n        /// Ushort representation of client status\n        /// </summary>\n        [Obsolete]\n        public ushort UClientStatus { get { return (ushort)ClientStatus; } }\n\n        /// <summary>\n        /// Connection failure reason\n        /// </summary>\n        public string ConnectionFailure { get { return ClientStatus.ToString(); } }\n\n        /// <summary>\n        /// Gets or sets the AutoReconnect\n        /// </summary>\n        public bool AutoReconnect { get; set; }\n\n        /// <summary>\n        /// S+ helper for AutoReconnect\n        /// </summary>\n        public ushort UAutoReconnect\n        {\n            get { return (ushort)(AutoReconnect ? 1 : 0); }\n            set { AutoReconnect = value == 1; }\n        }\n\n        /// <summary>\n        /// Milliseconds to wait before attempting to reconnect. Defaults to 5000\n        /// </summary>\n        public int AutoReconnectIntervalMs { get; set; }\n\n        /// <summary>\n        /// Set only when the disconnect method is called\n        /// </summary>\n        bool DisconnectCalledByUser;\n\n        /// <summary>\n        /// \n        /// </summary>\n        public bool Connected\n        {\n            get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }\n        }\n\n        //Lock object to prevent simulatneous connect/disconnect operations\n        private CCriticalSection connectLock = new CCriticalSection();\n\n        // private Timer for auto reconnect\n        private CTimer RetryTimer;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">unique string to differentiate between instances</param>\n        /// <param name=\"address\"></param>\n        /// <param name=\"port\"></param>\n        /// <param name=\"bufferSize\"></param>\n\t\tpublic GenericTcpIpClient(string key, string address, int port, int bufferSize)\n            : base(key)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(key);\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            AutoReconnectIntervalMs = 5000;\n            Hostname = address;\n            Port = port;\n            BufferSize = bufferSize;\n\n            RetryTimer = new CTimer(o =>\n            {\n                Reconnect();\n            }, Timeout.Infinite);\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        public GenericTcpIpClient(string key)\n            : base(key)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(key);\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            AutoReconnectIntervalMs = 5000;\n            BufferSize = 2000;\n\n            RetryTimer = new CTimer(o =>\n            {\n                Reconnect();\n            }, Timeout.Infinite);\n        }\n\n        /// <summary>\n        /// Default constructor for S+\n        /// </summary>\n        public GenericTcpIpClient()\n            : base(SplusKey)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(SplusKey);\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            AutoReconnectIntervalMs = 5000;\n            BufferSize = 2000;\n\n            RetryTimer = new CTimer(o =>\n            {\n                Reconnect();\n            }, Timeout.Infinite);\n        }\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public void Initialize(string key)\n        {\n            Key = key;\n        }\n\n        /// <summary>\n        /// Handles closing this up when the program shuts down\n        /// </summary>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping)\n            {\n                Debug.Console(1, this, \"Program stopping. Closing connection\");\n                Deactivate();\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <returns></returns>\n        /// <summary>\n        /// Deactivate method\n        /// </summary>\n        public override bool Deactivate()\n        {\n            RetryTimer.Stop();\n            RetryTimer.Dispose();\n            if (_client != null)\n            {\n                _client.SocketStatusChange -= this.Client_SocketStatusChange;\n                DisconnectClient();\n            }\n            return true;\n        }\n\n        /// <summary>\n        /// Connect method\n        /// </summary>\n        public void Connect()\n        {\n            if (string.IsNullOrEmpty(Hostname))\n            {\n                Debug.Console(1, Debug.ErrorLogLevel.Warning, \"GenericTcpIpClient '{0}': No address set\", Key);\n                return;\n            }\n            if (Port < 1 || Port > 65535)\n            {\n                {\n                    Debug.Console(1, Debug.ErrorLogLevel.Warning, \"GenericTcpIpClient '{0}': Invalid port\", Key);\n                    return;\n                }\n            }\n\n            try\n            {\n                connectLock.Enter();\n                if (IsConnected)\n                {\n                    Debug.Console(1, this, \"Connection already connected. Exiting Connect()\");\n                }\n                else\n                {\n                    //Stop retry timer if running\n                    RetryTimer.Stop();\n                    _client = new TCPClient(Hostname, Port, BufferSize);\n                    _client.SocketStatusChange -= Client_SocketStatusChange;\n                    _client.SocketStatusChange += Client_SocketStatusChange;\n                    DisconnectCalledByUser = false;\n                    _client.ConnectToServerAsync(ConnectToServerCallback);\n                }\n            }\n            finally\n            {\n                connectLock.Leave();\n            }\n        }\n\n        private void Reconnect()\n        {\n            if (_client == null)\n            {\n                return;\n            }\n            try\n            {\n                connectLock.Enter();\n                if (IsConnected || DisconnectCalledByUser == true)\n                {\n                    Debug.Console(1, this, \"Reconnect no longer needed. Exiting Reconnect()\");\n                }\n                else\n                {\n                    Debug.Console(1, this, \"Attempting reconnect now\");\n                    _client.ConnectToServerAsync(ConnectToServerCallback);\n                }\n            }\n            finally\n            {\n                connectLock.Leave();\n            }\n        }\n\n        /// <summary>\n        /// Disconnect method\n        /// </summary>\n        public void Disconnect()\n        {\n            try\n            {\n                connectLock.Enter();\n                DisconnectCalledByUser = true;\n\n                // Stop trying reconnects, if we are\n                RetryTimer.Stop();\n                DisconnectClient();\n            }\n            finally\n            {\n                connectLock.Leave();\n            }\n        }\n\n        /// <summary>\n        /// DisconnectClient method\n        /// </summary>\n        public void DisconnectClient()\n        {\n            if (_client != null)\n            {\n                Debug.Console(1, this, \"Disconnecting client\");\n                if (IsConnected)\n                    _client.DisconnectFromServer();\n            }\n        }\n\n        /// <summary>\n        /// Callback method for connection attempt\n        /// </summary>\n        /// <param name=\"c\"></param>\n\t\tvoid ConnectToServerCallback(TCPClient c)\n        {\n            if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)\n            {\n                Debug.Console(0, this, \"Server connection result: {0}\", c.ClientStatus);\n                WaitAndTryReconnect();\n            }\n            else\n            {\n                Debug.Console(1, this, \"Server connection result: {0}\", c.ClientStatus);\n            }\n        }\n\n        /// <summary>\n        /// Disconnects, waits and attemtps to connect again\n        /// </summary>\n\t\tvoid WaitAndTryReconnect()\n        {\n            CrestronInvoke.BeginInvoke(o =>\n            {\n                try\n                {\n                    connectLock.Enter();\n                    if (!IsConnected && AutoReconnect && !DisconnectCalledByUser && _client != null)\n                    {\n                        DisconnectClient();\n                        Debug.Console(1, this, \"Attempting reconnect, status={0}\", _client.ClientStatus);\n                        RetryTimer.Reset(AutoReconnectIntervalMs);\n                    }\n                }\n                finally\n                {\n                    connectLock.Leave();\n                }\n            });\n        }\n\n        /// <summary>\n        /// Recieves incoming data\n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <param name=\"numBytes\"></param>\n\t\tvoid Receive(TCPClient client, int numBytes)\n        {\n            if (client != null)\n            {\n                if (numBytes > 0)\n                {\n                    var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();\n                    var bytesHandler = BytesReceived;\n                    if (bytesHandler != null)\n                    {\n                        this.PrintReceivedBytes(bytes);\n                        bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));\n                    }\n                    var textHandler = TextReceived;\n                    if (textHandler != null)\n                    {\n                        var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);\n\n                        this.PrintReceivedText(str);\n\n                        textHandler(this, new GenericCommMethodReceiveTextArgs(str));\n                    }\n                }\n                client.ReceiveDataAsync(Receive);\n            }\n        }\n\n        /// <summary>\n        /// SendText method\n        /// </summary>\n        public void SendText(string text)\n        {\n            var bytes = Encoding.GetEncoding(28591).GetBytes(text);\n            // Check debug level before processing byte array\n            this.PrintSentText(text);\n            if (_client != null)\n                _client.SendData(bytes, bytes.Length);\n        }\n\n        /// <summary>\n        /// SendEscapedText method\n        /// </summary>\n        public void SendEscapedText(string text)\n        {\n            var unescapedText = Regex.Replace(text, @\"\\\\x([0-9a-fA-F][0-9a-fA-F])\", s =>\n                {\n                    var hex = s.Groups[1].Value;\n                    return ((char)Convert.ToByte(hex, 16)).ToString();\n                });\n            SendText(unescapedText);\n        }\n\n        /// <summary>\n        /// Sends Bytes to the server\n        /// </summary>\n        /// <param name=\"bytes\"></param>\n        /// <summary>\n        /// SendBytes method\n        /// </summary>\n        public void SendBytes(byte[] bytes)\n        {\n            this.PrintSentBytes(bytes);\n            if (_client != null)\n                _client.SendData(bytes, bytes.Length);\n        }\n\n        /// <summary>\n        /// Socket Status Change Handler\n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <param name=\"clientSocketStatus\"></param>\n\t\tvoid Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)\n        {\n            if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)\n            {\n                Debug.Console(0, this, \"Socket status change {0} ({1})\", clientSocketStatus, ClientStatusText);\n                WaitAndTryReconnect();\n            }\n            else\n            {\n                Debug.Console(1, this, \"Socket status change {0} ({1})\", clientSocketStatus, ClientStatusText);\n                _client.ReceiveDataAsync(Receive);\n            }\n\n            var handler = ConnectionChange;\n            if (handler != null)\n                ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));\n        }\n    }\n\n    /// <summary>\n    /// Represents a TcpSshPropertiesConfig\n    /// </summary>\n    public class TcpSshPropertiesConfig\n    {\n        /// <summary>\n        /// Address to connect to\n        /// </summary>\n\t\t[JsonProperty(Required = Required.Always)]\n        public string Address { get; set; }\n\n        /// <summary>\n        /// Port to connect to\n        /// </summary>\n        [JsonProperty(Required = Required.Always)]\n        public int Port { get; set; }\n\n        /// <summary>\n        /// Username credential\n        /// </summary>\n        public string Username { get; set; }\n        /// <summary>\n        /// Gets or sets the Password\n        /// </summary>\n        public string Password { get; set; }\n\n        /// <summary>\n        /// Defaults to 32768\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// Gets or sets the AutoReconnect\n        /// </summary>\n        public bool AutoReconnect { get; set; }\n\n        /// <summary>\n        /// Gets or sets the AutoReconnectIntervalMs\n        /// </summary>\n        public int AutoReconnectIntervalMs { get; set; }\n\n        /// <summary>\n        /// When true, turns off echo for the SSH session\n        /// </summary>\n        [JsonProperty(\"disableSshEcho\")]\n        public bool DisableSshEcho { get; set; }\n\n        /// <summary>\n        /// Default constructor\n        /// </summary>\n\t\tpublic TcpSshPropertiesConfig()\n        {\n            BufferSize = 32768;\n            AutoReconnect = true;\n            AutoReconnectIntervalMs = 5000;\n            Username = \"\";\n            Password = \"\";\n            DisableSshEcho = false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericTcpIpClient_ForServer.cs",
    "content": "﻿/*PepperDash Technology Corp.\nJAG\nCopyright:\t\t2017\n------------------------------------\n***Notice of Ownership and Copyright***\nThe material in which this notice appears is the property of PepperDash Technology Corporation, \nwhich claims copyright under the laws of the United States of America in the entire body of material \nand in all parts thereof, regardless of the use to which it is being put.  Any use, in whole or in part, \nof this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.  \nPepperDash Technology Corporation reserves all rights under applicable laws.\n------------------------------------ */\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing PepperDash.Core.Logging;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Generic TCP/IP client for server\n    /// </summary>\n    public class GenericTcpIpClient_ForServer : Device, IAutoReconnect\n    {\n        /// <summary>\n        /// Band aid delegate for choked server\n        /// </summary>\n        internal delegate void ConnectionHasHungCallbackDelegate();\n\n        #region Events\n\n        //public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// Notifies of text received\n        /// </summary>\n        public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Notifies of socket status change\n        /// </summary>\n        public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ConnectionChange;\n\n\n        /// <summary>\n        /// This is something of a band-aid callback. If the client times out during the connection process, because the server\n        /// is stuck, this will fire.  It is intended to be used by the Server class monitor client, to help\n        /// keep a watch on the server and reset it if necessary.\n        /// </summary>\n        internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;\n\n        /// <summary>\n        /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require\n        /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.\n        /// </summary>\n        public event EventHandler<GenericTcpServerClientReadyForcommunicationsEventArgs> ClientReadyForCommunications;\n\n        #endregion\n\n        #region Properties & Variables\n\n        /// <summary>\n        /// Address of server\n        /// </summary>\n        public string Hostname { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// S+ helper\n        /// </summary>\n        public ushort UPort\n        {\n            get { return Convert.ToUInt16(Port); }\n            set { Port = Convert.ToInt32(value); }\n        }\n\n        /// <summary>\n        /// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class\n        /// </summary>\n        public bool SharedKeyRequired { get; set; }\n\n        /// <summary>\n        /// S+ helper for requires shared key bool\n        /// </summary>\n        public ushort USharedKeyRequired\n        {\n            set\n            {\n                if (value == 1)\n                    SharedKeyRequired = true;\n                else\n                    SharedKeyRequired = false;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the SharedKey\n        /// </summary>\n        public string SharedKey { get; set; }\n\n        /// <summary>\n        /// flag to show the client is waiting for the server to send the shared key\n        /// </summary>\n        private bool WaitingForSharedKeyResponse { get; set; }\n\n        /// <summary>\n        /// Gets or sets the BufferSize\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// Semaphore on connect method\n        /// </summary>\n        bool IsTryingToConnect;\n\n        /// <summary>\n        /// Bool showing if socket is connected\n        /// </summary>\n        public bool IsConnected\n        {\n            get\n            {\n                if (Client != null)\n                    return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;\n                else\n                    return false;\n            }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return (ushort)(IsConnected ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// Bool showing if socket is ready for communication after shared key exchange\n        /// </summary>\n        public bool IsReadyForCommunication { get; set; }\n\n        /// <summary>\n        /// S+ helper for IsReadyForCommunication\n        /// </summary>\n        public ushort UIsReadyForCommunication\n        {\n            get { return (ushort)(IsReadyForCommunication ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// Client socket status Read only\n        /// </summary>\n        public SocketStatus ClientStatus\n        {\n            get\n            {\n                if (Client != null)\n                    return Client.ClientStatus;\n                else\n                    return SocketStatus.SOCKET_STATUS_NO_CONNECT;\n            }\n        }\n\n        /// <summary>\n        /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event\n        /// and IsConnected would be true when this == 2.\n        /// </summary>\n        public ushort UStatus\n        {\n            get { return (ushort)ClientStatus; }\n        }\n\n        /// <summary>\n        /// Status text shows the message associated with socket status\n        /// </summary>\n        public string ClientStatusText { get { return ClientStatus.ToString(); } }\n\n        /// <summary>\n        /// bool to track if auto reconnect should be set on the socket\n        /// </summary>\n        public bool AutoReconnect { get; set; }\n\n        /// <summary>\n        /// S+ helper for AutoReconnect\n        /// </summary>\n        public ushort UAutoReconnect\n        {\n            get { return (ushort)(AutoReconnect ? 1 : 0); }\n            set { AutoReconnect = value == 1; }\n        }\n        /// <summary>\n        /// Milliseconds to wait before attempting to reconnect. Defaults to 5000\n        /// </summary>\n        public int AutoReconnectIntervalMs { get; set; }\n\n        /// <summary>\n        /// Flag Set only when the disconnect method is called.\n        /// </summary>\n        bool DisconnectCalledByUser;\n\n        /// <summary>\n        /// private Timer for auto reconnect\n        /// </summary>\n        CTimer RetryTimer;\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        public bool HeartbeatEnabled { get; set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public ushort UHeartbeatEnabled\n        {\n            get { return (ushort)(HeartbeatEnabled ? 1 : 0); }\n            set { HeartbeatEnabled = value == 1; }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public string HeartbeatString = \"heartbeat\";\n\n        /// <summary>\n        /// \n        /// </summary>\n        public int HeartbeatInterval = 50000;\n\n        CTimer HeartbeatSendTimer;\n        CTimer HeartbeatAckTimer;\n        /// <summary>\n        /// Used to force disconnection on a dead connect attempt\n        /// </summary>\n        CTimer ConnectFailTimer;\n        CTimer WaitForSharedKey;\n        private int ConnectionCount;\n        /// <summary>\n        /// Internal secure client\n        /// </summary>\n        TCPClient Client;\n\n        bool ProgramIsStopping;\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"address\"></param>\n        /// <param name=\"port\"></param>\n        /// <param name=\"bufferSize\"></param>\n        public GenericTcpIpClient_ForServer(string key, string address, int port, int bufferSize)\n            : base(key)\n        {\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            Hostname = address;\n            Port = port;\n            BufferSize = bufferSize;\n            AutoReconnectIntervalMs = 5000;\n\n        }\n\n        /// <summary>\n        /// Constructor for S+\n        /// </summary>\n        public GenericTcpIpClient_ForServer()\n            : base(\"Uninitialized DynamicTcpClient\")\n        {\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            AutoReconnectIntervalMs = 5000;\n            BufferSize = 2000;\n        }\n        #endregion\n\n        #region Methods\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public void Initialize(string key)\n        {\n            Key = key;\n        }\n\n        /// <summary>\n        /// Handles closing this up when the program shuts down\n        /// </summary>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Program stopping. Closing Client connection\");\n                ProgramIsStopping = true;\n                Disconnect();\n            }\n\n        }\n\n        /// <summary>\n        /// Connect method\n        /// </summary>\n        public void Connect()\n        {\n            ConnectionCount++;\n            Debug.Console(2, this, \"Attempting connect Count:{0}\", ConnectionCount);\n\n\n            if (IsConnected)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Already connected. Ignoring.\");\n                return;\n            }\n            if (IsTryingToConnect)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Notice, \"Already trying to connect. Ignoring.\");\n                return;\n            }\n            try\n            {\n                IsTryingToConnect = true;\n                if (RetryTimer != null)\n                {\n                    RetryTimer.Stop();\n                    RetryTimer = null;\n                }\n                if (string.IsNullOrEmpty(Hostname))\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: No address set\");\n                    return;\n                }\n                if (Port < 1 || Port > 65535)\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: Invalid port\");\n                    return;\n                }\n                if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)\n                {\n                    Debug.Console(0, this, Debug.ErrorLogLevel.Warning, \"DynamicTcpClient: No Shared Key set\");\n                    return;\n                }\n\n                // clean up previous client\n                if (Client != null)\n                {\n                    Cleanup();\n                }\n                DisconnectCalledByUser = false;\n\n                Client = new TCPClient(Hostname, Port, BufferSize);\n                Client.SocketStatusChange += Client_SocketStatusChange;\n                if(HeartbeatEnabled)\n                    Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);\n                Client.AddressClientConnectedTo = Hostname;\n                Client.PortNumber = Port;\n                // SecureClient = c;\n\n                //var timeOfConnect = DateTime.Now.ToString(\"HH:mm:ss.fff\");\n\n                ConnectFailTimer = new CTimer(o =>\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Connect attempt has not finished after 30sec Count:{0}\", ConnectionCount);\n                    if (IsTryingToConnect)\n                    {\n                        IsTryingToConnect = false;\n\n                        //if (ConnectionHasHungCallback != null)\n                        //{\n                        //    ConnectionHasHungCallback();\n                        //}\t\t\n                        //SecureClient.DisconnectFromServer();\n                        //CheckClosedAndTryReconnect();\n                    }\n                }, 30000);\n\n                Debug.Console(2, this,  \"Making Connection Count:{0}\", ConnectionCount);\n                Client.ConnectToServerAsync(o =>\n                {\n                    Debug.Console(2, this, \"ConnectToServerAsync Count:{0} Ran!\", ConnectionCount);\n\n                    if (ConnectFailTimer != null)\n                    {\n                        ConnectFailTimer.Stop();\n                    }\n                    IsTryingToConnect = false;\n\n                    if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    {\n                        Debug.Console(2, this, \"Client connected to {0} on port {1}\", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);\n                        o.ReceiveDataAsync(Receive);\n\n                        if (SharedKeyRequired)\n                        {\n                            WaitingForSharedKeyResponse = true;\n                            WaitForSharedKey = new CTimer(timer =>\n                            {\n\n                                Debug.Console(1, this, Debug.ErrorLogLevel.Warning, \"Shared key exchange timer expired. IsReadyForCommunication={0}\", IsReadyForCommunication);\n                                // Debug.Console(1, this, \"Connect attempt failed {0}\", c.ClientStatus);\n                                // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup \n                                o.DisconnectFromServer();\n                                //CheckClosedAndTryReconnect();\n                                //OnClientReadyForcommunications(false); // Should send false event\n                            }, 15000);\n                        }\n                        else\n                        {\n                            //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key \n                            //required this is called by the shared key being negotiated\n                            if (IsReadyForCommunication == false)\n                            {\n                                OnClientReadyForcommunications(true); // Key not required\n                            }\n                        }\n                    }\n                    else\n                    {\n                        Debug.Console(1, this, \"Connect attempt failed {0}\", o.ClientStatus);\n                        CheckClosedAndTryReconnect();\n                    }\n                });\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(0, this, Debug.ErrorLogLevel.Error, \"Client connection exception: {0}\", ex.Message);\n                IsTryingToConnect = false;\n                CheckClosedAndTryReconnect();\n            }\n        }\n\n        /// <summary>\n        /// Disconnect method\n        /// </summary>\n        public void Disconnect()\n        {\n            this.LogVerbose(\"Disconnect Called\");\n\n            DisconnectCalledByUser = true;\n            if (IsConnected)\n            {\n                Client.DisconnectFromServer();\n\n            }\n            if (RetryTimer != null)\n            {\n                RetryTimer.Stop();\n                RetryTimer = null;\n            }\n            Cleanup();\n        }\n\n        /// <summary>\n        ///  Internal call to close up client. ALWAYS use this when disconnecting.\n        /// </summary>\n        void Cleanup()\n        {\n            IsTryingToConnect = false;\n\n            if (Client != null)\n            {\n                //SecureClient.DisconnectFromServer();\n                Debug.Console(2, this, \"Disconnecting Client {0}\", DisconnectCalledByUser ? \", Called by user\" : \"\");\n                Client.SocketStatusChange -= Client_SocketStatusChange;\n                Client.Dispose();\n                Client = null;\n            }\n            if (ConnectFailTimer != null)\n            {\n                ConnectFailTimer.Stop();\n                ConnectFailTimer.Dispose();\n                ConnectFailTimer = null;\n            }\n        }\n\n\n        /// <summary>ff\n        /// Called from Connect failure or Socket Status change if \n        /// auto reconnect and socket disconnected (Not disconnected by user)\n        /// </summary>\n        void CheckClosedAndTryReconnect()\n        {\n            if (Client != null)\n            {\n                Debug.Console(2, this, \"Cleaning up remotely closed/failed connection.\");\n                Cleanup();\n            }\n            if (!DisconnectCalledByUser && AutoReconnect)\n            {\n                var halfInterval = AutoReconnectIntervalMs / 2;\n                var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;\n                Debug.Console(2, this, \"Attempting reconnect in {0} ms, randomized\", rndTime);\n                if (RetryTimer != null)\n                {\n                    RetryTimer.Stop();\n                    RetryTimer = null;\n                }\n                RetryTimer = new CTimer(o => Connect(), rndTime);\n            }\n        }\n\n        /// <summary>\n        /// Receive callback\n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <param name=\"numBytes\"></param>\n        void Receive(TCPClient client, int numBytes)\n        {\n            if (numBytes > 0)\n            {\n                string str = string.Empty;\n\n                try\n                {\n                    var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();\n                    str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);\n                    Debug.Console(2, this, \"Client Received:\\r--------\\r{0}\\r--------\", str);\n                    if (!string.IsNullOrEmpty(checkHeartbeat(str)))\n                    {\n                        if (SharedKeyRequired && str == \"SharedKey:\")\n                        {\n                            Debug.Console(2, this, \"Server asking for shared key, sending\");\n                            SendText(SharedKey + \"\\n\");\n                        }\n                        else if (SharedKeyRequired && str == \"Shared Key Match\")\n                        {\n                            StopWaitForSharedKeyTimer();\n                            Debug.Console(2, this, \"Shared key confirmed. Ready for communication\");\n                            OnClientReadyForcommunications(true); // Successful key exchange\n                        }\n                        else\n                        {\n                            //var bytesHandler = BytesReceived;\n                            //if (bytesHandler != null)\n                            //    bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));\n                            var textHandler = TextReceived;\n                            if (textHandler != null)\n                                textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));\n                        }\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(1, this, \"Error receiving data: {1}. Error: {0}\", ex.Message, str);\n                }\n            }\n            if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                client.ReceiveDataAsync(Receive);\n        }\n\n        void HeartbeatStart()\n        {\n            if (HeartbeatEnabled)\n            {\n                Debug.Console(2, this,  \"Starting Heartbeat\");\n                if (HeartbeatSendTimer == null)\n                {\n\n                    HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);\n                }\n                if (HeartbeatAckTimer == null)\n                {\n                    HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));\n                }\n            }\n\n        }\n        void HeartbeatStop()\n        {\n\n            if (HeartbeatSendTimer != null)\n            {\n                Debug.Console(2, this,  \"Stoping Heartbeat Send\");\n                HeartbeatSendTimer.Stop();\n                HeartbeatSendTimer = null;\n            }\n            if (HeartbeatAckTimer != null)\n            {\n                Debug.Console(2, this, \"Stoping Heartbeat Ack\");\n                HeartbeatAckTimer.Stop();\n                HeartbeatAckTimer = null;\n            }\n\n        }\n        void SendHeartbeat(object notused)\n        {\n            this.SendText(HeartbeatString);\n            Debug.Console(2, this, \"Sending Heartbeat\");\n\n        }\n\n        //private method to check heartbeat requirements and start or reset timer\n        string checkHeartbeat(string received)\n        {\n            try\n            {\n                if (HeartbeatEnabled)\n                {\n                    if (!string.IsNullOrEmpty(HeartbeatString))\n                    {\n                        var remainingText = received.Replace(HeartbeatString, \"\");\n                        var noDelimiter = received.Trim(new char[] { '\\r', '\\n' });\n                        if (noDelimiter.Contains(HeartbeatString))\n                        {\n                            if (HeartbeatAckTimer != null)\n                            {\n                                HeartbeatAckTimer.Reset(HeartbeatInterval * 2);\n                            }\n                            else\n                            {\n                                HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));\n                            }\n                            Debug.Console(2, this, \"Heartbeat Received: {0}, from Server\", HeartbeatString);\n                            return remainingText;\n                        }\n                    }                    \n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(1, this, \"Error checking heartbeat: {0}\", ex.Message);\n            }\n            return received;\n        }\n\n\n\n        void HeartbeatAckTimerFail(object o)\n        {\n            try\n            {\n\n                if (IsConnected)\n                {\n                    Debug.Console(1, Debug.ErrorLogLevel.Warning, \"Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE\");\n                    SendText(\"Heartbeat not received by server, closing connection\");\n                    CheckClosedAndTryReconnect();\n                }\n\n            }\n            catch (Exception ex)\n            {\n                ErrorLog.Error(\"Heartbeat timeout Error on Client: {0}, {1}\", Key, ex);\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void StopWaitForSharedKeyTimer()\n        {\n            if (WaitForSharedKey != null)\n            {\n                WaitForSharedKey.Stop();\n                WaitForSharedKey = null;\n            }\n        }\n\n        /// <summary>\n        /// SendText method\n        /// </summary>\n        public void SendText(string text)\n        {\n            if (!string.IsNullOrEmpty(text))\n            {\n                try\n                {\n                    var bytes = Encoding.GetEncoding(28591).GetBytes(text);\n                    if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    {\n                        Client.SendDataAsync(bytes, bytes.Length, (c, n) =>\n                        {\n                            // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????\n                            if (n <= 0)\n                            {\n                                Debug.Console(1, Debug.ErrorLogLevel.Warning, \"[{0}] Sent zero bytes. Was there an error?\", this.Key);\n                            }\n                        });\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(0, this, \"Error sending text: {1}. Error: {0}\", ex.Message, text);\n                }\n            }\n        }\n\n        /// <summary>\n        /// SendBytes method\n        /// </summary>\n        public void SendBytes(byte[] bytes)\n        {\n            if (bytes.Length > 0)\n            {\n                try\n                {\n                    if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)\n                        Client.SendData(bytes, bytes.Length);\n                }\n                catch (Exception ex)\n                {\n                    Debug.Console(0, this, \"Error sending bytes. Error: {0}\", ex.Message);\n                }\n            }\n        }\n\n        /// <summary>\n        /// SocketStatusChange Callback \n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <param name=\"clientSocketStatus\"></param>\n        void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)\n        {\n            if (ProgramIsStopping)\n            {\n                ProgramIsStopping = false;\n                return;\n            }\n            try\n            {\n                Debug.Console(2, this, \"Socket status change: {0} ({1})\", client.ClientStatus, (ushort)(client.ClientStatus));\n\n                OnConnectionChange();\n                \n                // The client could be null or disposed by this time...\n                if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)\n                {\n                    HeartbeatStop();\n                    OnClientReadyForcommunications(false); // socket has gone low\n                    CheckClosedAndTryReconnect();\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Error in socket status change callback. Error: {0}\\r\\r{1}\", ex, ex.InnerException);\n            }\n        }\n\n        /// <summary>\n        /// Helper for ConnectionChange event\n        /// </summary>\n        void OnConnectionChange()\n        {\n            var handler = ConnectionChange;\n            if (handler != null)\n                ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));\n        }\n\n        /// <summary>\n        /// Helper to fire ClientReadyForCommunications event\n        /// </summary>\n        void OnClientReadyForcommunications(bool isReady)\n        {\n            IsReadyForCommunication = isReady;\n            if (this.IsReadyForCommunication) { HeartbeatStart(); }\n            var handler = ClientReadyForCommunications;\n            if (handler != null)\n                handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));\n        }\n        #endregion\n    }\n    \n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericTcpIpServer.cs",
    "content": "﻿/*PepperDash Technology Corp.\nJAG\nCopyright:\t\t2017\n------------------------------------\n***Notice of Ownership and Copyright***\nThe material in which this notice appears is the property of PepperDash Technology Corporation,\nwhich claims copyright under the laws of the United States of America in the entire body of material\nand in all parts thereof, regardless of the use to which it is being put.  Any use, in whole or in part,\nof this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.\nPepperDash Technology Corporation reserves all rights under applicable laws.\n------------------------------------ */\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing PepperDash.Core.Logging;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Generic TCP/IP server device\n    /// </summary>\n    public class GenericTcpIpServer : Device\n    {\n        #region Events\n        /// <summary>\n        /// Event for Receiving text\n        /// </summary>\n        public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Event for client connection socket status change\n        /// </summary>\n        public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ClientConnectionChange;\n\n        /// <summary>\n        /// Event for Server State Change\n        /// </summary>\n        public event EventHandler<GenericTcpServerStateChangedEventArgs> ServerStateChange;\n\n        /// <summary>\n        /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire\n        /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key. \n        /// </summary>\n        public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ServerClientReadyForCommunications;\n\n        /// <summary>\n        /// A band aid event to notify user that the server has choked.\n        /// </summary>\n        public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }\n\n        /// <summary>\n        /// Delegate for ServerHasChokedCallbackDelegate\n        /// </summary>\n        public delegate void ServerHasChokedCallbackDelegate();\n\n        #endregion\n\n        #region Properties/Variables\n\n        /// <summary>\n        /// \n        /// </summary>\n        CCriticalSection ServerCCSection = new CCriticalSection();\n\n\n        /// <summary>\n        /// A bandaid client that monitors whether the server is reachable\n        /// </summary>\n        GenericTcpIpClient_ForServer MonitorClient;\n\n        /// <summary>\n        /// Timer to operate the bandaid monitor client in a loop.\n        /// </summary>\n        CTimer MonitorClientTimer;\n\n        /// <summary>\n        /// \n        /// </summary>\n        int MonitorClientFailureCount;\n\n        /// <summary>\n        /// Gets or sets the MonitorClientMaxFailureCount\n        /// </summary>\n        public int MonitorClientMaxFailureCount { get; set; }\n\n        /// <summary>\n        /// Text representation of the Socket Status enum values for the server\n        /// </summary>\n        public string Status\n        {\n            get\n            {\n                if (myTcpServer != null)\n                    return myTcpServer.State.ToString();\n                return ServerState.SERVER_NOT_LISTENING.ToString();\n\n            }\n\n        }\n\n        /// <summary>\n        /// Bool showing if socket is connected\n        /// </summary>\n        public bool IsConnected\n        {\n            get\n            {\n                if (myTcpServer != null)\n                    return (myTcpServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;\n                return false;\n\n                //return (Secure ? SecureServer != null : UnsecureServer != null) && \n                //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :\n                //          (UnsecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED); \n            }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return (ushort)(IsConnected ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// Bool showing if socket is connected\n        /// </summary>\n        public bool IsListening\n        {\n            get\n            {\n                if (myTcpServer != null)\n                    return (myTcpServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;\n                else\n                    return false;\n                //return (Secure ? SecureServer != null : UnsecureServer != null) &&\n                //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :\n                //          (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);\n            }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsListening\n        {\n            get { return (ushort)(IsListening ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// The maximum number of clients.\n        /// Should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable\n        /// </summary>\n        public ushort MaxClients { get; set; }\n\n        /// <summary>\n        /// Number of clients currently connected.\n        /// </summary>\n        public ushort NumberOfClientsConnected\n        {\n            get\n            {\n                if (myTcpServer != null)\n                    return (ushort)myTcpServer.NumberOfClientsConnected;\n                return 0;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// S+ helper for Port\n        /// </summary>\n        public ushort UPort\n        {\n            get { return Convert.ToUInt16(Port); }\n            set { Port = Convert.ToInt32(value); }\n        }\n\n        /// <summary>\n        /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client\n        /// </summary>\n        public bool SharedKeyRequired { get; set; }\n\n        /// <summary>\n        /// S+ helper for requires shared key bool\n        /// </summary>\n        public ushort USharedKeyRequired\n        {\n            set\n            {\n                if (value == 1)\n                    SharedKeyRequired = true;\n                else\n                    SharedKeyRequired = false;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the SharedKey\n        /// </summary>\n        public string SharedKey { get; set; }\n\n        /// <summary>\n        /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received\n        /// </summary>\n        public bool HeartbeatRequired { get; set; }\n\n        /// <summary>\n        /// S+ Helper for Heartbeat Required\n        /// </summary>\n        public ushort UHeartbeatRequired\n        {\n            set\n            {\n                if (value == 1)\n                    HeartbeatRequired = true;\n                else\n                    HeartbeatRequired = false;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the HeartbeatRequiredIntervalMs\n        /// </summary>\n        public int HeartbeatRequiredIntervalMs { get; set; }\n\n        /// <summary>\n        /// Simpl+ Heartbeat Analog value in seconds\n        /// </summary>\n        public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }\n\n        /// <summary>\n        /// Gets or sets the HeartbeatStringToMatch\n        /// </summary>\n        public string HeartbeatStringToMatch { get; set; }\n\n        //private timers for Heartbeats per client\n        Dictionary<uint, CTimer> HeartbeatTimerDictionary = new Dictionary<uint, CTimer>();\n\n        //flags to show the secure server is waiting for client at index to send the shared key\n        List<uint> WaitingForSharedKey = new List<uint>();\n\n        List<uint> ClientReadyAfterKeyExchange = new List<uint>();\n\n        /// <summary>\n        /// The connected client indexes\n        /// </summary>\n        public List<uint> ConnectedClientsIndexes = new List<uint>();\n\n        /// <summary>\n        /// Gets or sets the BufferSize\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// Private flag to note that the server has stopped intentionally\n        /// </summary>\n        private bool ServerStopped { get; set; }\n\n        //Servers\n        TCPServer myTcpServer;\n\n        /// <summary>\n        /// \n        /// </summary>\n        bool ProgramIsStopping;\n\n        #endregion\n\n        #region Constructors\n        /// <summary>\n        /// constructor S+ Does not accept a key. Use initialze with key to set the debug key on this device. If using with + make sure to set all properties manually.\n        /// </summary>\n        public GenericTcpIpServer()\n            : base(\"Uninitialized Dynamic TCP Server\")\n        {\n            HeartbeatRequiredIntervalInSeconds = 15;\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            BufferSize = 2000;\n            MonitorClientMaxFailureCount = 3;\n        }\n\n        /// <summary>\n        /// constructor with debug key set at instantiation. Make sure to set all properties before listening. \n        /// </summary>\n        /// <param name=\"key\"></param>\n        public GenericTcpIpServer(string key)\n            : base(\"Uninitialized Dynamic TCP Server\")\n        {\n            HeartbeatRequiredIntervalInSeconds = 15;\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            BufferSize = 2000;\n            MonitorClientMaxFailureCount = 3;\n            Key = key;\n        }\n\n        /// <summary>\n        /// Contstructor that sets all properties by calling the initialize method with a config object.\n        /// </summary>\n        /// <param name=\"serverConfigObject\"></param>\n        public GenericTcpIpServer(TcpServerConfigObject serverConfigObject)\n            : base(\"Uninitialized Dynamic TCP Server\")\n        {\n            HeartbeatRequiredIntervalInSeconds = 15;\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            BufferSize = 2000;\n            MonitorClientMaxFailureCount = 3;\n            Initialize(serverConfigObject);\n        }\n        #endregion\n\n        #region Methods - Server Actions\n        /// <summary>\n        /// KillServer method\n        /// </summary>\n        public void KillServer()\n        {\n            ServerStopped = true;\n            if (MonitorClient != null)\n            {\n                MonitorClient.Disconnect();\n            }\n            DisconnectAllClientsForShutdown();\n            StopListening();\n        }\n\n        /// <summary>\n        /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public void Initialize(string key)\n        {\n            Key = key;\n        }\n\n        /// <summary>\n        /// Initialze with server configuration object\n        /// </summary>\n        /// <param name=\"serverConfigObject\"></param>\n        public void Initialize(TcpServerConfigObject serverConfigObject)\n        {\n            try\n            {\n                if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))\n                {\n                    Key = serverConfigObject.Key;\n                    MaxClients = serverConfigObject.MaxClients;\n                    Port = serverConfigObject.Port;\n                    SharedKeyRequired = serverConfigObject.SharedKeyRequired;\n                    SharedKey = serverConfigObject.SharedKey;\n                    HeartbeatRequired = serverConfigObject.HeartbeatRequired;\n                    HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;\n                    HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;\n                    BufferSize = serverConfigObject.BufferSize;\n\n                }\n                else\n                {\n                    ErrorLog.Error(\"Could not initialize server with key: {0}\", serverConfigObject.Key);\n                }\n            }\n            catch\n            {\n                ErrorLog.Error(\"Could not initialize server with key: {0}\", serverConfigObject.Key);\n            }\n        }\n\n        /// <summary>\n        /// Listen method\n        /// </summary>\n        public void Listen()\n        {\n            ServerCCSection.Enter();\n            try\n            {\n                if (Port < 1 || Port > 65535)\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Server '{0}': Invalid port\", Key);\n                    ErrorLog.Warn(string.Format(\"Server '{0}': Invalid port\", Key));\n                    return;\n                }\n                if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Server '{0}': No Shared Key set\", Key);\n                    ErrorLog.Warn(string.Format(\"Server '{0}': No Shared Key set\", Key));\n                    return;\n                }\n                if (IsListening)\n                    return;\n\n                if (myTcpServer == null)\n                {\n                    myTcpServer = new TCPServer(Port, MaxClients);\n                    if(HeartbeatRequired)\n                        myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);\n                    \n\t\t\t\t\t// myTcpServer.HandshakeTimeout = 30;\n                }\n                else\n                {\n                    KillServer();\n                    myTcpServer.PortNumber = Port;\n                }\n\n                myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;\n                myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange;\n\n                ServerStopped = false;\n                myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);\n                OnServerStateChange(myTcpServer.State);\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"TCP Server Status: {0}, Socket Status: {1}\", myTcpServer.State, myTcpServer.ServerSocketStatus);\n\n                // StartMonitorClient();\n\n\n                ServerCCSection.Leave();\n            }\n            catch (Exception ex)\n            {\n                ServerCCSection.Leave();\n                ErrorLog.Error(\"{1} Error with Dynamic Server: {0}\", ex.ToString(), Key);\n            }\n        }\n\n        /// <summary>\n        /// StopListening method\n        /// </summary>\n        public void StopListening()\n        {\n            try\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Stopping Listener\");\n                if (myTcpServer != null)\n                {\n                    myTcpServer.Stop();\n                    Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Server State: {0}\", myTcpServer.State);\n\t\t\t\t\tOnServerStateChange(myTcpServer.State);\n                }\n\t\t        ServerStopped = true;\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error stopping server. Error: {0}\", ex);\n            }\n        }\n\n        /// <summary>\n        /// Disconnects Client\n        /// </summary>\n        /// <param name=\"client\"></param>\n        /// <summary>\n        /// DisconnectClient method\n        /// </summary>\n        public void DisconnectClient(uint client)\n        {\n            try\n            {\n                myTcpServer.Disconnect(client);\n                Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Disconnected client index: {0}\", client);\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error Disconnecting client index: {0}. Error: {1}\", client, ex);\n            }\n        }\n        /// <summary>\n        /// DisconnectAllClientsForShutdown method\n        /// </summary>\n        public void DisconnectAllClientsForShutdown()\n        {\n            Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Disconnecting All Clients\");\n            if (myTcpServer != null)\n            {\n                myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;\n                foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly\n                {\n                    var i = index;\n                    if (!myTcpServer.ClientConnected(index))\n                        continue;\n                    try\n                    {\n                        myTcpServer.Disconnect(i);\n                        Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Disconnected client index: {0}\", i);\n                    }\n                    catch (Exception ex)\n                    {\n                        Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error Disconnecting client index: {0}. Error: {1}\", i, ex);\n                    }\n                }\n                Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Server Status: {0}\", myTcpServer.ServerSocketStatus);\n            }\n\n            Debug.Console(2, this, Debug.ErrorLogLevel.Notice, \"Disconnected All Clients\");\n            ConnectedClientsIndexes.Clear();\n\n            if (!ProgramIsStopping)\n            {\n                OnConnectionChange();\n                OnServerStateChange(myTcpServer.State); //State shows both listening and connected\n            }\n\n            // var o = new { };\n        }\n\n        /// <summary>\n        /// Broadcast text from server to all connected clients\n        /// </summary>\n        /// <param name=\"text\"></param>\n        /// <summary>\n        /// BroadcastText method\n        /// </summary>\n        public void BroadcastText(string text)\n        {\n            CCriticalSection CCBroadcast = new CCriticalSection();\n            CCBroadcast.Enter();\n            try\n            {\n                if (ConnectedClientsIndexes.Count > 0)\n                {\n                    byte[] b = Encoding.GetEncoding(28591).GetBytes(text);\n                    foreach (uint i in ConnectedClientsIndexes)\n                    {\n                        if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))\n                        {\n                            SocketErrorCodes error = myTcpServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });\n                            if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)\n                                this.LogError(\"{error}\",error.ToString());\n                        }\n                    }\n                }\n                CCBroadcast.Leave();\n            }\n            catch (Exception ex)\n            {\n                CCBroadcast.Leave();\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error Broadcasting messages from server. Error: {0}\", ex.Message);\n            }\n        }\n\n        /// <summary>\n        /// Not sure this is useful in library, maybe Pro??\n        /// </summary>\n        /// <param name=\"text\"></param>\n        /// <param name=\"clientIndex\"></param>\n        /// <summary>\n        /// SendTextToClient method\n        /// </summary>\n        public void SendTextToClient(string text, uint clientIndex)\n        {\n            try\n            {\n                byte[] b = Encoding.GetEncoding(28591).GetBytes(text);\n                if (myTcpServer != null && myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)\n                {\n                    if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))\n                        myTcpServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, \"Error sending text to client. Text: {1}. Error: {0}\", ex.Message, text);\n            }\n        }\n\n        //private method to check heartbeat requirements and start or reset timer\n        string checkHeartbeat(uint clientIndex, string received)\n        {\n            try\n            {\n                if (HeartbeatRequired)\n                {\n                    if (!string.IsNullOrEmpty(HeartbeatStringToMatch))\n                    {\n                        var remainingText = received.Replace(HeartbeatStringToMatch, \"\");\n                        var noDelimiter = received.Trim(new char[] { '\\r', '\\n' });\n                        if (noDelimiter.Contains(HeartbeatStringToMatch))\n                        {\n                            if (HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                                HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);\n                            else\n                            {\n                                CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);\n                                HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);\n                            }\n                            Debug.Console(1, this, \"Heartbeat Received: {0}, from client index: {1}\", HeartbeatStringToMatch, clientIndex);\n                            // Return Heartbeat\n                            SendTextToClient(HeartbeatStringToMatch, clientIndex);\n                            return remainingText;\n                        }\n                    }\n                    else\n                    {\n                        if (HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                            HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);\n                        else\n                        {\n                            CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);\n                            HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);\n                        }\n                        Debug.Console(1, this, \"Heartbeat Received: {0}, from client index: {1}\", received, clientIndex);\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(1, this, \"Error checking heartbeat: {0}\", ex.Message);\n            }\n            return received;\n        }\n\n        /// <summary>\n        /// Gets the IP address based on the client index\n        /// </summary>\n        /// <param name=\"clientIndex\"></param>\n        /// <returns>IP address of the client</returns>\n        /// <summary>\n        /// GetClientIPAddress method\n        /// </summary>\n        public string GetClientIPAddress(uint clientIndex)\n        {\n            Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"GetClientIPAddress Index: {0}\", clientIndex);\n            if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))\n            {\n                var ipa = this.myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"GetClientIPAddress IPAddreess: {0}\", ipa);\n                return ipa;\n\n            }\n            else\n            {\n                return \"\";\n            }\n        }\n\n        #endregion\n\n        #region Methods - HeartbeatTimer Callback\n\n        void HeartbeatTimer_CallbackFunction(object o)\n        {\n            uint clientIndex = 99999;\n            string address = string.Empty;\n            try\n            {\n                clientIndex = (uint)o;\n                address = myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);\n\n                Debug.Console(1, this, Debug.ErrorLogLevel.Warning, \"Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}\",\n                    address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? \"\" : (\"HeartbeatStringToMatch: \" + HeartbeatStringToMatch), clientIndex);\n\n                if (myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)\n                    SendTextToClient(\"Heartbeat not received by server, closing connection\", clientIndex);\n\n                var discoResult = myTcpServer.Disconnect(clientIndex);\n                //Debug.Console(1, this, \"{0}\", discoResult);  \n\n                if (HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                {\n                    HeartbeatTimerDictionary[clientIndex].Stop();\n                    HeartbeatTimerDictionary[clientIndex].Dispose();\n                    HeartbeatTimerDictionary.Remove(clientIndex);\n                }\n            }\n            catch (Exception ex)\n            {\n                ErrorLog.Error(\"{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}\", clientIndex, address, ex.Message, Key);\n            }\n        }\n\n        #endregion\n\n        #region Methods - Socket Status Changed Callbacks\n        /// <summary>\n        /// Secure Server Socket Status Changed Callback\n        /// </summary>\n        /// <param name=\"server\"></param>\n        /// <param name=\"clientIndex\"></param>\n        /// <param name=\"serverSocketStatus\"></param>\n        void TcpServer_SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)\n        {\n            try\n            {\n\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}\", clientIndex, serverSocketStatus, this.myTcpServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.myTcpServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));\n                if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)\n                {\n                    if (ConnectedClientsIndexes.Contains(clientIndex))\n                        ConnectedClientsIndexes.Remove(clientIndex);\n                    if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                    {\n                        HeartbeatTimerDictionary[clientIndex].Stop();\n                        HeartbeatTimerDictionary[clientIndex].Dispose();\n                        HeartbeatTimerDictionary.Remove(clientIndex);\n                    }\n                    if (ClientReadyAfterKeyExchange.Contains(clientIndex))\n                        ClientReadyAfterKeyExchange.Remove(clientIndex);\n\t\t\t\t\tif (WaitingForSharedKey.Contains(clientIndex))\n\t\t\t\t\t\tWaitingForSharedKey.Remove(clientIndex);\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error in Socket Status Change Callback. Error: {0}\", ex);\n            }\n            onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));\n        }\n\n        #endregion\n\n        #region Methods Connected Callbacks\n        /// <summary>\n        /// Secure TCP Client Connected to Secure Server Callback\n        /// </summary>\n        /// <param name=\"server\"></param>\n        /// <param name=\"clientIndex\"></param>\n        void TcpConnectCallback(TCPServer server, uint clientIndex)\n        {\n            try\n            {\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}\",\n                    server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),\n                    clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));\n                if (clientIndex != 0)\n                {\n                    if (server.ClientConnected(clientIndex))\n                    {\n\n                        if (!ConnectedClientsIndexes.Contains(clientIndex))\n                        {\n                            ConnectedClientsIndexes.Add(clientIndex);\n                        }\n                        if (SharedKeyRequired)\n                        {\n                            if (!WaitingForSharedKey.Contains(clientIndex))\n                            {\n                                WaitingForSharedKey.Add(clientIndex);\n                            }\n                            byte[] b = Encoding.GetEncoding(28591).GetBytes(\"SharedKey:\");\n                            server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });\n                            Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Sent Shared Key Request to client at {0}\", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));\n                        }\n                        else\n                        {\n                            OnServerClientReadyForCommunications(clientIndex);\n                        }\n                        if (HeartbeatRequired)\n                        {\n                            if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))\n                            {\n                                HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));\n                            }\n                        }\n\n                        server.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);\n                    }\n                }\n                else\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Error, \"Client attempt faulty.\");\n                    if (!ServerStopped)\n                    {\n                        server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);\n                        return;\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error in Socket Status Connect Callback. Error: {0}\", ex);\n            }\n            //Debug.Console(1, this, Debug.ErrorLogLevel, \"((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))\",\n            //    server.State, \n            //    MaxClients,\n            //    ServerStopped);\n            if ((server.State & ServerState.SERVER_LISTENING) != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)\n            {\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Waiting for next connection\");\n                server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);\n\n            }\n        }\n\n        #endregion\n\n        #region Methods - Send/Receive Callbacks\n        /// <summary>\n        /// Secure Received Data Async Callback\n        /// </summary>\n        /// <param name=\"myTCPServer\"></param>\n        /// <param name=\"clientIndex\"></param>\n        /// <param name=\"numberOfBytesReceived\"></param>\n        void TcpServerReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)\n        {\n\t\t\tif (numberOfBytesReceived > 0)\n\t\t\t{\n\t\t\t\tstring received = \"Nothing\";\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tbyte[] bytes = myTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);\n\t\t\t\t\treceived = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);\n\t\t\t\t\tif (WaitingForSharedKey.Contains(clientIndex))\n\t\t\t\t\t{\n\t\t\t\t\t\treceived = received.Replace(\"\\r\", \"\");\n\t\t\t\t\t\treceived = received.Replace(\"\\n\", \"\");\n\t\t\t\t\t\tif (received != SharedKey)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbyte[] b = Encoding.GetEncoding(28591).GetBytes(\"Shared key did not match server. Disconnecting\");\n\t\t\t\t\t\t\tDebug.Console(1, this, Debug.ErrorLogLevel.Warning, \"Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}\", clientIndex, received);\n\t\t\t\t\t\t\tmyTCPServer.SendData(clientIndex, b, b.Length);\n\t\t\t\t\t\t\tmyTCPServer.Disconnect(clientIndex);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tWaitingForSharedKey.Remove(clientIndex);\n\t\t\t\t\t\tbyte[] success = Encoding.GetEncoding(28591).GetBytes(\"Shared Key Match\");\n\t\t\t\t\t\tmyTCPServer.SendDataAsync(clientIndex, success, success.Length, null);\n\t\t\t\t\t\tOnServerClientReadyForCommunications(clientIndex);\n\t\t\t\t\t\tDebug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Client with index {0} provided the shared key and successfully connected to the server\", clientIndex);\n\t\t\t\t\t}\n\n\t\t\t\t\telse if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))\n\t\t\t\t\t\tonTextReceived(received, clientIndex);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex)\n\t\t\t\t{\n\t\t\t\t\tDebug.Console(2, this, Debug.ErrorLogLevel.Error, \"Error Receiving data: {0}. Error: {1}\", received, ex);\n\t\t\t\t}\n\t\t\t\tif (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)\n\t\t\t\t\tmyTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// If numberOfBytesReceived <= 0\n\t\t\t\tmyTCPServer.Disconnect();\n\t\t\t}\n\n        }\n\n        #endregion\n\n        #region Methods - EventHelpers/Callbacks\n\n        //Private Helper method to call the Connection Change Event\n        void onConnectionChange(uint clientIndex, SocketStatus clientStatus)\n        {\n            if (clientIndex != 0) //0 is error not valid client change\n            {\n                var handler = ClientConnectionChange;\n                if (handler != null)\n                {\n                    handler(this, new GenericTcpServerSocketStatusChangeEventArgs(myTcpServer, clientIndex, clientStatus));\n                }\n            }\n        }\n\n        //Private Helper method to call the Connection Change Event\n        void OnConnectionChange()\n        {\n            if (ProgramIsStopping)\n            {\n                return;\n            }\n            var handler = ClientConnectionChange;\n            if (handler != null)\n            {\n                handler(this, new GenericTcpServerSocketStatusChangeEventArgs());\n            }\n        }\n\n        //Private Helper Method to call the Text Received Event\n        void onTextReceived(string text, uint clientIndex)\n        {\n            var handler = TextReceived;\n            if (handler != null)\n                handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));\n        }\n\n        //Private Helper Method to call the Server State Change Event\n        void OnServerStateChange(ServerState state)\n        {\n            if (ProgramIsStopping)\n            {\n                return;\n            }\n            var handler = ServerStateChange;\n            if (handler != null)\n            {\n                handler(this, new GenericTcpServerStateChangedEventArgs(state));\n            }\n        }\n\n        /// <summary>\n        /// Private Event Handler method to handle the closing of connections when the program stops\n        /// </summary>\n        /// <param name=\"programEventType\"></param>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping)\n            {\n                ProgramIsStopping = true;\n                // kill bandaid things\n                if (MonitorClientTimer != null)\n                    MonitorClientTimer.Stop();\n                if (MonitorClient != null)\n                    MonitorClient.Disconnect();\n\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Program stopping. Closing server\");\n                KillServer();\n            }\n        }\n\n        //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation\n        void OnServerClientReadyForCommunications(uint clientIndex)\n        {\n            ClientReadyAfterKeyExchange.Add(clientIndex);\n            var handler = ServerClientReadyForCommunications;\n            if (handler != null)\n                handler(this, new GenericTcpServerSocketStatusChangeEventArgs(\n                    this, clientIndex, myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex)));\n        }\n        #endregion\n\n        #region Monitor Client\n        /// <summary>\n        /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient\n        /// </summary>\n        void StartMonitorClient()\n        {\n            if (MonitorClientTimer != null)\n            {\n                return;\n            }\n            MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void RunMonitorClient()\n        {\n            MonitorClient = new GenericTcpIpClient_ForServer(Key + \"-MONITOR\", \"127.0.0.1\", Port, 2000);\n            MonitorClient.SharedKeyRequired = this.SharedKeyRequired;\n            MonitorClient.SharedKey = this.SharedKey;\n            MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;\n            //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;\n            MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;\n\n            Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Starting monitor check\");\n\n            MonitorClient.Connect();\n            // From here MonitorCLient either connects or hangs, MonitorClient will call back \n\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void StopMonitorClient()\n        {\n            if (MonitorClient == null)\n                return;\n\n            MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;\n            MonitorClient.Disconnect();\n            MonitorClient = null;\n        }\n\n        /// <summary>\n        /// On monitor connect, restart the operation\n        /// </summary>\n        void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)\n        {\n            if (args.IsReady)\n            {\n                Debug.Console(1, this, Debug.ErrorLogLevel.Notice, \"Monitor client connection success. Disconnecting in 2s\");\n                MonitorClientTimer.Stop();\n                MonitorClientTimer = null;\n                MonitorClientFailureCount = 0;\n                CrestronEnvironment.Sleep(2000);\n                StopMonitorClient();\n                StartMonitorClient();\n            }\n        }\n\n        /// <summary>\n        /// If the client hangs, add to counter and maybe fire the choke event\n        /// </summary>\n        void MonitorClientHasHungCallback()\n        {\n            MonitorClientFailureCount++;\n            MonitorClientTimer.Stop();\n            MonitorClientTimer = null;\n            StopMonitorClient();\n            if (MonitorClientFailureCount < MonitorClientMaxFailureCount)\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Warning, \"Monitor client connection has hung {0} time{1}, maximum {2}\",\n                    MonitorClientFailureCount, MonitorClientFailureCount > 1 ? \"s\" : \"\", MonitorClientMaxFailureCount);\n                StartMonitorClient();\n            }\n            else\n            {\n                Debug.Console(2, this, Debug.ErrorLogLevel.Error,\n                    \"\\r***************************\\rMonitor client connection has hung a maximum of {0} times.\\r***************************\",\n                    MonitorClientMaxFailureCount);\n\n                var handler = ServerHasChoked;\n                if (handler != null)\n                    handler();\n                // Some external thing is in charge here.  Expected reset of program\n            }\n        }\n        #endregion\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericUdpClient.cs",
    "content": "using System;\nusing System.Net.Sockets;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing ThreadingTimeout = System.Threading.Timeout;\nusing NetSocketException = System.Net.Sockets.SocketException;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// A class to handle basic UDP communications to a remote endpoint\n    /// </summary>\n    public class GenericUdpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect\n    {\n        private const string SplusKey = \"Uninitialized UdpClient\";\n\n        private readonly object stateLock = new object();\n        private readonly Timer reconnectTimer;\n\n        private UdpClient client;\n        private CancellationTokenSource receiveCancellationTokenSource;\n        private bool connectEnabled;\n        private bool connectionRefusedLogged;\n        private SocketStatus clientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;\n\n        /// <summary>\n        /// Object to enable stream debugging\n        /// </summary>\n        public CommunicationStreamDebugging StreamDebugging { get; private set; }\n\n        /// <summary>\n        /// Fires when data is received from the remote endpoint and returns it as a byte array\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// Fires when data is received from the remote endpoint and returns it as text\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Fires when the socket status changes\n        /// </summary>\n        public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;\n\n        /// <summary>\n        /// Address of remote endpoint\n        /// </summary>\n        public string Hostname { get; set; }\n\n        /// <summary>\n        /// Port on remote endpoint\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// Another S+ helper because large port numbers can be treated as signed ints\n        /// </summary>\n        public ushort UPort\n        {\n            get { return Convert.ToUInt16(Port); }\n            set { Port = Convert.ToInt32(value); }\n        }\n\n        /// <summary>\n        /// Defaults to 2000\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// True when the local socket is created and associated with the configured remote endpoint\n        /// </summary>\n        public bool IsConnected\n        {\n            get { return ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }\n        }\n\n        /// <summary>\n        /// S+ helper for IsConnected\n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return (ushort)(IsConnected ? 1 : 0); }\n        }\n\n        /// <summary>\n        /// The current socket status of the client\n        /// </summary>\n        public SocketStatus ClientStatus\n        {\n            get\n            {\n                lock (stateLock)\n                {\n                    return clientStatus;\n                }\n            }\n            private set\n            {\n                var shouldFireEvent = false;\n\n                lock (stateLock)\n                {\n                    if (clientStatus != value)\n                    {\n                        clientStatus = value;\n                        shouldFireEvent = true;\n                    }\n                }\n\n                if (shouldFireEvent)\n                    ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));\n            }\n        }\n\n        /// <summary>\n        /// Ushort representation of client status\n        /// </summary>\n        public ushort UStatus\n        {\n            get { return (ushort)ClientStatus; }\n        }\n\n        /// <summary>\n        /// Gets or sets the AutoReconnect\n        /// </summary>\n        public bool AutoReconnect { get; set; }\n\n        /// <summary>\n        /// S+ helper for AutoReconnect\n        /// </summary>\n        public ushort UAutoReconnect\n        {\n            get { return (ushort)(AutoReconnect ? 1 : 0); }\n            set { AutoReconnect = value == 1; }\n        }\n\n        /// <summary>\n        /// Milliseconds to wait before attempting to reconnect. Defaults to 5000\n        /// </summary>\n        public int AutoReconnectIntervalMs { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public GenericUdpClient(string key, string address, int port, int bufferSize)\n            : base(key)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(key);\n            CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;\n            AutoReconnectIntervalMs = 5000;\n            Hostname = address;\n            Port = port;\n            BufferSize = bufferSize;\n\n            reconnectTimer = new Timer(o =>\n            {\n                if (connectEnabled)\n                    Connect();\n            }, null, ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);\n        }\n\n        /// <summary>\n        /// Constructor for S+\n        /// </summary>\n        public GenericUdpClient()\n            : base(SplusKey)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(SplusKey);\n            CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;\n            AutoReconnectIntervalMs = 5000;\n            BufferSize = 2000;\n\n            reconnectTimer = new Timer(o =>\n            {\n                if (connectEnabled)\n                    Connect();\n            }, null, ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);\n        }\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public void Initialize(string key)\n        {\n            Key = key;\n        }\n\n        private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping)\n            {\n                Debug.Console(1, this, \"Program stopping. Closing connection\");\n                Deactivate();\n            }\n        }\n\n        /// <summary>\n        /// Deactivate method\n        /// </summary>\n        public override bool Deactivate()\n        {\n            Disconnect();\n            return true;\n        }\n\n        /// <summary>\n        /// Connect method\n        /// </summary>\n        public void Connect()\n        {\n            if (string.IsNullOrEmpty(Hostname))\n            {\n                Debug.Console(1, Debug.ErrorLogLevel.Warning, \"GenericUdpClient '{0}': No address set\", Key);\n                ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;\n                return;\n            }\n\n            if (Port < 1 || Port > 65535)\n            {\n                Debug.Console(1, Debug.ErrorLogLevel.Warning, \"GenericUdpClient '{0}': Invalid port\", Key);\n                ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;\n                return;\n            }\n\n            var hostname = Hostname;\n            var port = Port;\n            var bufferSize = BufferSize;\n            UdpClient newClient = null;\n            CancellationTokenSource newReceiveCancellationTokenSource = null;\n            CancellationToken startReceiveToken = default(CancellationToken);\n            var shouldStartReceive = false;\n\n            lock (stateLock)\n            {\n                connectEnabled = true;\n\n                if (client != null)\n                    return;\n            }\n\n            try\n            {\n                newReceiveCancellationTokenSource = new CancellationTokenSource();\n                newClient = new UdpClient();\n                newClient.Client.ReceiveBufferSize = bufferSize;\n                newClient.Client.SendBufferSize = bufferSize;\n                newClient.Connect(hostname, port);\n\n                lock (stateLock)\n                {\n                    if (!connectEnabled || client != null)\n                    {\n                        newClient.Close();\n                        newReceiveCancellationTokenSource.Cancel();\n                        newReceiveCancellationTokenSource.Dispose();\n                        return;\n                    }\n\n                    receiveCancellationTokenSource = newReceiveCancellationTokenSource;\n                    client = newClient;\n                    ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;\n                    reconnectTimer.Change(ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);\n                    startReceiveToken = receiveCancellationTokenSource.Token;\n                    shouldStartReceive = true;\n                }\n\n                if (shouldStartReceive)\n                    StartReceive(startReceiveToken);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Error connecting UDP client {0}\", this, Key);\n\n                if (newClient != null)\n                    newClient.Close();\n\n                if (newReceiveCancellationTokenSource != null)\n                {\n                    newReceiveCancellationTokenSource.Cancel();\n                    newReceiveCancellationTokenSource.Dispose();\n                }\n\n                lock (stateLock)\n                {\n                    if (connectEnabled && client == null)\n                    {\n                        ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;\n                        StartReconnectTimer();\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// Disconnect method\n        /// </summary>\n        public void Disconnect()\n        {\n            lock (stateLock)\n            {\n                connectEnabled = false;\n                reconnectTimer.Change(ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);\n                CleanupClient();\n                ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;\n            }\n        }\n\n        /// <summary>\n        /// SendText method\n        /// </summary>\n        public void SendText(string text)\n        {\n            this.PrintSentText(text);\n\n            var bytes = Encoding.GetEncoding(28591).GetBytes(text);\n            SendBytes(bytes);\n        }\n\n        /// <summary>\n        /// SendBytes method\n        /// </summary>\n        public void SendBytes(byte[] bytes)\n        {\n            if (bytes == null)\n                return;\n\n            try\n            {\n                this.PrintSentBytes(bytes);\n\n                if (!IsConnected || client == null)\n                    Connect();\n\n                var udpClient = client;\n                if (!IsConnected || udpClient == null)\n                {\n                    Debug.Console(1, Debug.ErrorLogLevel.Warning, \"GenericUdpClient '{0}': Cannot send bytes because the client is not connected\", Key);\n                    return;\n                }\n\n                udpClient.Send(bytes, bytes.Length);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Error sending UDP bytes for {0}\", this, Key);\n                HandleDisconnected();\n            }\n        }\n\n        private void StartReceive(CancellationToken token)\n        {\n            Task.Run(async () =>\n            {\n                while (!token.IsCancellationRequested)\n                {\n                    try\n                    {\n                        var udpClient = client;\n                        if (udpClient == null)\n                            return;\n\n                        var result = await udpClient.ReceiveAsync().ConfigureAwait(false);\n                        var bytes = result.Buffer;\n                        if (bytes == null || bytes.Length == 0)\n                            continue;\n\n                        connectionRefusedLogged = false;\n\n                        var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);\n\n                        this.PrintReceivedBytes(bytes);\n                        this.PrintReceivedText(text);\n\n                        BytesReceived?.Invoke(this, new GenericCommMethodReceiveBytesArgs(bytes));\n                        TextReceived?.Invoke(this, new GenericCommMethodReceiveTextArgs(text));\n                    }\n                    catch (ObjectDisposedException)\n                    {\n                        return;\n                    }\n                    catch (InvalidOperationException)\n                    {\n                        return;\n                    }\n                    catch (NetSocketException ex)\n                    {\n                        if (ex.SocketErrorCode == SocketError.ConnectionRefused)\n                        {\n                            if (!connectionRefusedLogged)\n                            {\n                                Debug.Console(1, Debug.ErrorLogLevel.Warning,\n                                    \"GenericUdpClient '{0}': Remote endpoint refused UDP traffic or is no longer listening\",\n                                    Key);\n                                connectionRefusedLogged = true;\n                            }\n\n                            HandleDisconnected();\n                            return;\n                        }\n\n                        Debug.LogMessage(ex, \"UDP receive error for {0}\", this, Key);\n\n                        if (AutoReconnect)\n                        {\n                            HandleDisconnected();\n                            return;\n                        }\n\n                        continue;\n                    }\n                    catch (Exception ex)\n                    {\n                        Debug.LogMessage(ex, \"Unexpected UDP receive error for {0}\", this, Key);\n\n                        if (AutoReconnect)\n                        {\n                            HandleDisconnected();\n                            return;\n                        }\n\n                        continue;\n                    }\n                }\n            }, token);\n        }\n\n        private void HandleDisconnected()\n        {\n            lock (stateLock)\n            {\n                CleanupClient();\n                ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;\n                StartReconnectTimer();\n            }\n        }\n\n        private void StartReconnectTimer()\n        {\n            if (AutoReconnect && connectEnabled)\n                reconnectTimer.Change(AutoReconnectIntervalMs, ThreadingTimeout.Infinite);\n        }\n\n        private void CleanupClient()\n        {\n            if (receiveCancellationTokenSource != null)\n            {\n                receiveCancellationTokenSource.Cancel();\n                receiveCancellationTokenSource.Dispose();\n                receiveCancellationTokenSource = null;\n            }\n\n            if (client != null)\n            {\n                client.Close();\n                client = null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Comm/GenericUdpServer.cs",
    "content": "﻿\nusing System;\nusing System.Linq;\nusing System.Text;\n\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Logging;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Generic UDP Server device\n    /// </summary>\n    public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging\n    {\n        private const string SplusKey = \"Uninitialized Udp Server\";\n        /// <summary>\n        /// Object to enable stream debugging\n        /// </summary>\n        public CommunicationStreamDebugging StreamDebugging { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// \n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// This event will fire when a message is dequeued that includes the source IP and Port info if needed to determine the source of the received data.\n        /// </summary>\n\t\tpublic event EventHandler<GenericUdpReceiveTextExtraArgs> DataRecievedExtra;\n\n        /// <summary>\n        /// \n        /// </summary>\n        public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;\n\n        /// <summary>\n        /// \n        /// </summary>\n        public event EventHandler<GenericUdpConnectedEventArgs> UpdateConnectionStatus;\n\n        /// <summary>\n        /// \n        /// </summary>\n        public SocketStatus ClientStatus\n        {\n            get\n            {\n                return Server.ServerStatus;\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public ushort UStatus\n        {\n            get { return (ushort)Server.ServerStatus; }\n        }\n\n        /// <summary>\n        /// Address of server\n        /// </summary>\n        public string Hostname { get; set; }\n\n\n        /// <summary>\n        /// Port on server\n        /// </summary>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// Another damn S+ helper because S+ seems to treat large port nums as signed ints\n        /// which screws up things\n        /// </summary>\n        public ushort UPort\n        {\n            get { return Convert.ToUInt16(Port); }\n            set { Port = Convert.ToInt32(value); }\n        }\n\n        /// <summary>\n        /// Indicates that the UDP Server is enabled\n        /// </summary>\n        public bool IsConnected\n        {\n            get;\n            private set;\n        }\n\n        /// <summary>\n        /// Numeric value indicating \n        /// </summary>\n        public ushort UIsConnected\n        {\n            get { return IsConnected ? (ushort)1 : (ushort)0; }\n        }\n\n        /// <summary>\n        /// Defaults to 2000\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// The server\n        /// </summary>\n        public UDPServer Server { get; private set; }\n\n        /// <summary>\n        /// Constructor for S+. Make sure to set key, address, port, and buffersize using init method\n        /// </summary>\n        public GenericUdpServer()\n            : base(SplusKey)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(SplusKey);\n            BufferSize = 5000;\n\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"address\"></param>\n        /// <param name=\"port\"></param>\n        /// <param name=\"bufferSize\"></param>\n        public GenericUdpServer(string key, string address, int port, int bufferSize)\n            : base(key)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(key);\n            Hostname = address;\n            Port = port;\n            BufferSize = bufferSize;\n\n            CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);\n            CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);\n        }\n\n        /// <summary>\n        /// Call from S+ to initialize values\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"address\"></param>\n        /// <param name=\"port\"></param>\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public void Initialize(string key, string address, ushort port)\n        {\n            Key = key;\n            Hostname = address;\n            UPort = port;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"ethernetEventArgs\"></param>\n        void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)\n        {\n            // Re-enable the server if the link comes back up and the status should be connected\n            if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp\n                && IsConnected)\n            {\n                Connect();\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"programEventType\"></param>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType != eProgramStatusEventType.Stopping)\n                return;\n\n            Debug.Console(1, this, \"Program stopping. Disabling Server\");\n            Disconnect();\n        }\n\n        /// <summary>\n        /// Connect method\n        /// </summary>\n        public void Connect()\n        {\n            if (Server == null)\n            {\n                try\n                {\n                    var address = IPAddress.Parse(Hostname);\n\n                    Server = new UDPServer(address, Port, BufferSize);\n\n                }\n                catch (Exception ex)\n                {\n                    this.LogError(\"Error parsing IP Address '{ipAddress}': message: {message}\", Hostname, ex.Message);\n                    this.LogInformation(\"Creating UDPServer with default buffersize\");\n\n                    Server = new UDPServer();\n                }\n\n            }\n\n            if (string.IsNullOrEmpty(Hostname))\n            {\n                Debug.Console(1, Debug.ErrorLogLevel.Warning, \"GenericUdpServer '{0}': No address set\", Key);\n                return;\n            }\n            if (Port < 1 || Port > 65535)\n            {\n                {\n                    Debug.Console(1, Debug.ErrorLogLevel.Warning, \"GenericUdpServer '{0}': Invalid port\", Key);\n                    return;\n                }\n            }\n\n            var status = Server.EnableUDPServer(Hostname, Port);\n\n            Debug.Console(2, this, \"SocketErrorCode: {0}\", status);\n            if (status == SocketErrorCodes.SOCKET_OK)\n                IsConnected = true;\n\n            var handler = UpdateConnectionStatus;\n            if (handler != null)\n                handler(this, new GenericUdpConnectedEventArgs(UIsConnected));\n\n            // Start receiving data\n            Server.ReceiveDataAsync(Receive);\n        }\n\n        /// <summary>\n        /// Disconnect method\n        /// </summary>\n        public void Disconnect()\n        {\n            if (Server != null)\n                Server.DisableUDPServer();\n\n            IsConnected = false;\n\n            var handler = UpdateConnectionStatus;\n            if (handler != null)\n                handler(this, new GenericUdpConnectedEventArgs(UIsConnected));\n        }\n\n\n        /// <summary>\n        /// Recursive method to receive data\n        /// </summary>\n        /// <param name=\"server\"></param>\n        /// <param name=\"numBytes\"></param>\n        void Receive(UDPServer server, int numBytes)\n        {\n            Debug.Console(2, this, \"Received {0} bytes\", numBytes);\n\n            try\n            {\n                if (numBytes <= 0)\n                    return;\n\n                var sourceIp = Server.IPAddressLastMessageReceivedFrom;\n                var sourcePort = Server.IPPortLastMessageReceivedFrom;\n                var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray();\n                var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);\n\n                var dataRecivedExtra = DataRecievedExtra;\n                if (dataRecivedExtra != null)\n                    dataRecivedExtra(this, new GenericUdpReceiveTextExtraArgs(str, sourceIp, sourcePort, bytes));\n\n                Debug.Console(2, this, \"Bytes: {0}\", bytes.ToString());\n                var bytesHandler = BytesReceived;\n                if (bytesHandler != null)\n                {\n                    this.PrintReceivedBytes(bytes);\n                    bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));\n                }\n                var textHandler = TextReceived;\n                if (textHandler != null)\n                {\n                    this.PrintReceivedText(str);\n                    textHandler(this, new GenericCommMethodReceiveTextArgs(str));\n                }\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex, \"GenericUdpServer Receive error\");\n            }\n            finally\n            {\n                server.ReceiveDataAsync(Receive);\n            }\n        }\n\n        /// <summary>\n        /// General send method\n        /// </summary>\n        /// <param name=\"text\"></param>\n        /// <summary>\n        /// SendText method\n        /// </summary>\n        public void SendText(string text)\n        {\n            var bytes = Encoding.GetEncoding(28591).GetBytes(text);\n\n            if (IsConnected && Server != null)\n            {\n                this.PrintSentText(text);\n\n                Server.SendData(bytes, bytes.Length);\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"bytes\"></param>\n        /// <summary>\n        /// SendBytes method\n        /// </summary>\n        public void SendBytes(byte[] bytes)\n        {\n            this.PrintSentBytes(bytes);\n\n            if (IsConnected && Server != null)\n                Server.SendData(bytes, bytes.Length);\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a GenericUdpReceiveTextExtraArgs\n    /// </summary>\n    public class GenericUdpReceiveTextExtraArgs : EventArgs\n    {\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Text { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string IpAddress { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int Port { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic byte[] Bytes { get; private set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"text\"></param>\n        /// <param name=\"ipAddress\"></param>\n        /// <param name=\"port\"></param>\n        /// <param name=\"bytes\"></param>\n\t\tpublic GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes)\n        {\n            Text = text;\n            IpAddress = ipAddress;\n            Port = port;\n            Bytes = bytes;\n        }\n\n        /// <summary>\n        /// Stupid S+ Constructor\n        /// </summary>\n        public GenericUdpReceiveTextExtraArgs() { }\n    }\n\n    /// <summary>\n    /// \n    /// </summary>\n    public class UdpServerPropertiesConfig\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        [JsonProperty(Required = Required.Always)]\n        public string Address { get; set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        [JsonProperty(Required = Required.Always)]\n        public int Port { get; set; }\n\n        /// <summary>\n        /// Defaults to 32768\n        /// </summary>\n        public int BufferSize { get; set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public UdpServerPropertiesConfig()\n        {\n            BufferSize = 32768;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/StreamDebuggingExtensions.cs",
    "content": "using System;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core\n{\n  /// <summary>\n  /// Extension methods for stream debugging\n  /// </summary>\n  public static class StreamDebuggingExtensions\n  {\n    private static readonly string app = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? $\"App {InitialParametersClass.ApplicationNumber}\" : $\"{InitialParametersClass.RoomId}\";\n\n    /// <summary>\n    /// Print the sent bytes to the console\n    /// </summary>\n    /// <param name=\"comms\">comms device</param>\n    /// <param name=\"bytes\">bytes to print</param>\n    public static void PrintSentBytes(this IStreamDebugging comms, byte[] bytes)\n    {\n      if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;\n\n      var timestamp = DateTime.Now.ToString(\"yyyy-MM-dd HH:mm:ss.fff\");\n\n      CrestronConsole.PrintLine($\"[{timestamp}][{app}][{comms.Key}] Sending {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'\");\n    }\n\n    /// <summary>\n    /// Print the received bytes to the console\n    /// </summary>\n    /// <param name=\"comms\">comms device</param>\n    /// <param name=\"bytes\">bytes to print</param>\n    public static void PrintReceivedBytes(this IStreamDebugging comms, byte[] bytes)\n    {\n      if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;\n\n      var timestamp = DateTime.Now.ToString(\"yyyy-MM-dd HH:mm:ss.fff\");\n\n      CrestronConsole.PrintLine($\"[{timestamp}][{app}][{comms.Key}] Received {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'\");\n    }\n\n    /// <summary>\n    /// Print the sent text to the console\n    /// </summary>\n    /// <param name=\"comms\">comms device</param>\n    /// <param name=\"text\">text to print</param>\n    public static void PrintSentText(this IStreamDebugging comms, string text)\n    {\n      if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;\n\n      var timestamp = DateTime.Now.ToString(\"yyyy-MM-dd HH:mm:ss.fff\");\n\n      CrestronConsole.PrintLine($\"[{timestamp}][{app}][{comms.Key}] Sending Text: '{ComTextHelper.GetDebugText(text)}'\");\n    }\n\n    /// <summary>\n    /// Print the received text to the console\n    /// </summary>\n    /// <param name=\"comms\">comms device</param>\n    /// <param name=\"text\">text to print</param>\n    public static void PrintReceivedText(this IStreamDebugging comms, string text)\n    {\n      if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;\n\n      var timestamp = DateTime.Now.ToString(\"yyyy-MM-dd HH:mm:ss.fff\");\n\n      CrestronConsole.PrintLine($\"[{timestamp}][{app}][{comms.Key}] Received Text: '{ComTextHelper.GetDebugText(text)}'\");\n    }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Comm/TcpClientConfigObject.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Represents a TcpClientConfigObject\n    /// </summary>\n    public class TcpClientConfigObject\n    {\n        /// <summary>\n        /// TcpSsh Properties \n        /// </summary>\n        [JsonProperty(\"control\")]\n        public ControlPropertiesConfig Control { get; set; }\n\n        /// <summary>\n        /// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic\n        /// </summary>\n        [JsonProperty(\"secure\")]\n        public bool Secure { get; set; }\n\n        /// <summary>\n        /// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client\n        /// </summary>\n        [JsonProperty(\"sharedKeyRequired\")]\n        public bool SharedKeyRequired { get; set; }\n\n        /// <summary>\n        /// The shared key that must match on the server and client\n        /// </summary>\n        [JsonProperty(\"sharedKey\")]\n        public string SharedKey { get; set; }\n\n        /// <summary>\n        /// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received. \n        /// heartbeats do not raise received events. \n        /// </summary>\n        [JsonProperty(\"heartbeatRequired\")]\n        public bool HeartbeatRequired { get; set; }\n\n        /// <summary>\n        /// The interval in seconds for the heartbeat from the client. If not received client is disconnected\n        /// </summary>\n        [JsonProperty(\"heartbeatRequiredIntervalInSeconds\")]\n        public ushort HeartbeatRequiredIntervalInSeconds { get; set; }\n\n        /// <summary>\n        /// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided. \n        /// </summary>\n        [JsonProperty(\"heartbeatStringToMatch\")]\n        public string HeartbeatStringToMatch { get; set; }\n\n        /// <summary>\n        /// Receive Queue size must be greater than 20 or defaults to 20\n        /// </summary>\n        [JsonProperty(\"receiveQueueSize\")]\n        public int ReceiveQueueSize { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/TcpServerConfigObject.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities\n    /// </summary>\n    public class TcpServerConfigObject\n    {\n        /// <summary>\n        /// Uique key\n        /// </summary>\n        public string Key { get; set; }\n        /// <summary>\n        /// Max Clients that the server will allow to connect. \n        /// </summary>\n        public ushort MaxClients { get; set; }\n        /// <summary>\n        /// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic\n        /// </summary>\n        public bool Secure { get; set; }\n        /// <summary>\n        /// Port for the server to listen on\n        /// </summary>\n        public int Port { get; set; }\n        /// <summary>\n        /// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client\n        /// </summary>\n        public bool SharedKeyRequired { get; set; }\n        /// <summary>\n        /// The shared key that must match on the server and client\n        /// </summary>\n        public string SharedKey { get; set; }\n        /// <summary>\n        /// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received. \n        /// heartbeats do not raise received events. \n        /// </summary>\n        public bool HeartbeatRequired { get; set; }\n        /// <summary>\n        /// The interval in seconds for the heartbeat from the client. If not received client is disconnected\n        /// </summary>\n        public ushort HeartbeatRequiredIntervalInSeconds { get; set; }\n        /// <summary>\n        /// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided. \n        /// </summary>\n        public string HeartbeatStringToMatch { get; set; }\n        /// <summary>\n        /// Client buffer size. See Crestron help. defaults to 2000 if not greater than 2000\n        /// </summary>\n        public int BufferSize { get; set; }\n        /// <summary>\n        /// Receive Queue size must be greater than 20 or defaults to 20\n        /// </summary>\n        public int ReceiveQueueSize { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/eControlMethods.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Crestron Control Methods for a comm object\n    /// </summary>\n    public enum eControlMethod\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        None = 0,\n        /// <summary>\n        /// RS232/422/485\n        /// </summary>\n        Com,\n        /// <summary>\n        /// Crestron IpId (most Crestron ethernet devices)\n        /// </summary>\n        IpId,\n        /// <summary>\n        /// Crestron IpIdTcp (HD-MD series, etc.)\n        /// </summary>\n        IpidTcp,\n        /// <summary>\n        /// Crestron IR control\n        /// </summary>\n        IR,\n        /// <summary>\n        /// SSH client\n        /// </summary>\n        Ssh,\n        /// <summary>\n        /// TCP/IP client\n        /// </summary>\n        Tcpip,\n        /// <summary>\n        /// Telnet\n        /// </summary>\n        Telnet,\n        /// <summary>\n        /// Crestnet device\n        /// </summary>\n        Cresnet,\n        /// <summary>\n        /// CEC Control, via a DM HDMI port\n        /// </summary>\n        Cec,\n        /// <summary>\n        /// UDP Server\n        /// </summary>\n        Udp,\n        /// <summary>\n        /// UDP client\n        /// </summary>\n        UdpClient,\n        /// <summary>\n        /// HTTP client\n        /// </summary>\n        Http,\n        /// <summary>\n        /// HTTPS client\n        /// </summary>\n        Https,\n        /// <summary>\n        /// Websocket client\n        /// </summary>\n        Ws,\n        /// <summary>\n        /// Secure Websocket client \n        /// </summary>\n        Wss,\n        /// <summary>\n        /// Secure TCP/IP\n        /// </summary>\n        SecureTcpIp,\n        /// <summary>\n        /// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction\n        /// </summary>\n        ComBridge,\n        /// <summary>\n        /// InfinetEX control\n        /// </summary>\n        InfinetEx\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Comm/eStreamDebuggingDataTypeSettings.cs",
    "content": "using System;\n\nnamespace PepperDash.Core\n{\n  /// <summary>\n  /// The available settings for stream debugging data format types\n  /// </summary>\n  [Flags]\n  public enum eStreamDebuggingDataTypeSettings\n  {\n    /// <summary>\n    /// Debug data in byte format\n    /// </summary>\n    Bytes = 0,\n    /// <summary>\n    /// Debug data in text format\n    /// </summary>\n    Text = 1,\n    /// <summary>\n    /// Debug data in both byte and text formats\n    /// </summary>\n    Both = Bytes | Text\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs",
    "content": "using System;\n\nnamespace PepperDash.Core\n{\n  /// <summary>\n  /// The available settings for stream debugging\n  /// </summary>\n  [Flags]\n  public enum eStreamDebuggingSetting\n  {\n    /// <summary>\n    /// Debug off\n    /// </summary>\n    Off = 0,\n    /// <summary>\n    /// Debug received data\n    /// </summary>\n    Rx = 1,\n    /// <summary>\n    /// Debug transmitted data\n    /// </summary>\n    Tx = 2,\n    /// <summary>\n    /// Debug both received and transmitted data\n    /// </summary>\n    Both = Rx | Tx\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/CommunicationExtras.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronSockets;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// An incoming communication stream\n    /// </summary>\n    public interface ICommunicationReceiver : IKeyed\n    {\n        /// <summary>\n        /// Notifies of bytes received\n        /// </summary>\n        event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n        /// <summary>\n        /// Notifies of text received\n        /// </summary>\n        event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Indicates connection status\n        /// </summary>\n        [JsonProperty(\"isConnected\")]\n        bool IsConnected { get; }\n        /// <summary>\n        /// Connect to the device\n        /// </summary>\n        void Connect();\n        /// <summary>\n        /// Disconnect from the device\n        /// </summary>\n        void Disconnect();\n    }\n\n    /// <summary>\n    /// Defines the contract for IBasicCommunication\n    /// </summary>\n    public interface IBasicCommunication : ICommunicationReceiver\n    {\n        /// <summary>\n        /// Send text to the device\n        /// </summary>\n        /// <param name=\"text\"></param>\n\t\tvoid SendText(string text);\n\n        /// <summary>\n        /// Send bytes to the device\n        /// </summary>\n        /// <param name=\"bytes\"></param>\n\t\tvoid SendBytes(byte[] bytes);\n    }\n\n    /// <summary>\n    /// Represents a device that implements IBasicCommunication and IStreamDebugging\n    /// </summary>\n    public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging\n    {\n\n    }\n\n    /// <summary>\n    /// Represents a device with stream debugging capablities\n    /// </summary>\n    public interface IStreamDebugging : IKeyed\n    {\n        /// <summary>\n        /// Object to enable stream debugging\n        /// </summary>\n        [JsonProperty(\"streamDebugging\")]\n        CommunicationStreamDebugging StreamDebugging { get; }\n    }\n\n    /// <summary>\n    /// For IBasicCommunication classes that have SocketStatus. GenericSshClient,\n    /// GenericTcpIpClient\n    /// </summary>\n    public interface ISocketStatus : IBasicCommunication\n    {\n        /// <summary>\n        /// Notifies of socket status changes\n        /// </summary>\n\t\tevent EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;\n\n        /// <summary>\n        /// The current socket status of the client\n        /// </summary>\n        [JsonProperty(\"clientStatus\")]\n        [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]\n        SocketStatus ClientStatus { get; }\n    }\n\n    /// <summary>\n    /// Describes a device that implements ISocketStatus and IStreamDebugging\n    /// </summary>\n    public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging\n    {\n\n    }\n\n    /// <summary>\n    /// Describes a device that can automatically attempt to reconnect\n    /// </summary>\n\tpublic interface IAutoReconnect\n    {\n        /// <summary>\n        /// Enable automatic recconnect\n        /// </summary>\n        [JsonProperty(\"autoReconnect\")]\n        bool AutoReconnect { get; set; }\n        /// <summary>\n        /// Interval in ms to attempt automatic recconnections\n        /// </summary>\n        [JsonProperty(\"autoReconnectIntervalMs\")]\n        int AutoReconnectIntervalMs { get; set; }\n    }\n\n    /// <summary>\n    /// \n    /// </summary>\n    public enum eGenericCommMethodStatusChangeType\n    {\n        /// <summary>\n        /// Connected\n        /// </summary>\n\t\tConnected,\n        /// <summary>\n        /// Disconnected\n        /// </summary>\n        Disconnected\n    }\n\n    /// <summary>\n    /// This delegate defines handler for IBasicCommunication status changes\n    /// </summary>\n    /// <param name=\"comm\">Device firing the status change</param>\n    /// <param name=\"status\"></param>\n    public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);\n\n    /// <summary>\n    /// \n    /// </summary>\n    public class GenericCommMethodReceiveBytesArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the Bytes\n        /// </summary>\n        public byte[] Bytes { get; private set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"bytes\"></param>\n\t\tpublic GenericCommMethodReceiveBytesArgs(byte[] bytes)\n        {\n            Bytes = bytes;\n        }\n\n        /// <summary>\n        /// S+ Constructor\n        /// </summary>\n        public GenericCommMethodReceiveBytesArgs() { }\n    }\n\n    /// <summary>\n    /// \n    /// </summary>\n    public class GenericCommMethodReceiveTextArgs : EventArgs\n    {\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Text { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n        public string Delimiter { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"text\"></param>\n\t\tpublic GenericCommMethodReceiveTextArgs(string text)\n        {\n            Text = text;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"text\"></param>\n        /// <param name=\"delimiter\"></param>\n        public GenericCommMethodReceiveTextArgs(string text, string delimiter)\n            : this(text)\n        {\n            Delimiter = delimiter;\n        }\n\n        /// <summary>\n        /// S+ Constructor\n        /// </summary>\n        public GenericCommMethodReceiveTextArgs() { }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Config/PortalConfigReader.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Core.Config\n{\n\n\n    /// <summary>\n    /// Reads a Portal formatted config file\n    /// </summary>\n\tpublic class PortalConfigReader\n\t{\n        const string template = \"template\";\n        const string system = \"system\";\n\t\tconst string systemUrl = \"system_url\";\n        const string templateUrl = \"template_url\";\n\t\tconst string info = \"info\";\n        const string devices = \"devices\";\n        const string rooms = \"rooms\";\n        const string sourceLists = \"sourceLists\";\n        const string destinationLists = \"destinationLists\";\n        const string cameraLists = \"cameraLists\";\n        const string audioControlPointLists = \"audioControlPointLists\";\n\n\t\tconst string tieLines = \"tieLines\";\n        const string joinMaps = \"joinMaps\";\n\t\tconst string global = \"global\";\n\n\n        /// <summary>\n        /// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object.\n        /// </summary>\n        /// <returns>JObject of config file</returns>\n        public static void ReadAndMergeFileIfNecessary(string filePath, string savePath)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (!File.Exists(filePath))\n\t\t\t\t{\n\t\t\t\t\tDebug.LogError(\n\t\t\t\t\t\t\"ERROR: Configuration file not present. Please load file to {0} and reset program\", filePath);\n\t\t\t\t}\n\n\t\t\t\tusing (StreamReader fs = new StreamReader(filePath))\n\t\t\t\t{\n\t\t\t\t\tvar jsonObj = JObject.Parse(fs.ReadToEnd());\n\t\t\t\t\tif(jsonObj[template] != null && jsonObj[system] != null)\n\t\t\t\t\t{\n\t\t\t\t\t\t// it's a double-config, merge it.\n\t\t\t\t\t\tvar merged = MergeConfigs(jsonObj);\n\t\t\t\t\t\tif (jsonObj[systemUrl] != null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmerged[systemUrl] = jsonObj[systemUrl].Value<string>();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (jsonObj[templateUrl] != null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmerged[templateUrl] = jsonObj[templateUrl].Value<string>();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tjsonObj = merged;\n\t\t\t\t\t}\n\n\t\t\t\t\tusing (StreamWriter fw = new StreamWriter(savePath))\n\t\t\t\t\t{\n\t\t\t\t\t\tfw.Write(jsonObj.ToString(Formatting.Indented));\n\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"JSON config merged and saved to {0}\", savePath);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(e, \"ERROR: Config load failed\");\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"doubleConfig\"></param>\n\t\t/// <returns></returns>\n  /// <summary>\n  /// MergeConfigs method\n  /// </summary>\n\t\tpublic static JObject MergeConfigs(JObject doubleConfig)\n\t\t{\n\t\t\tvar system = JObject.FromObject(doubleConfig[\"system\"]);\n\t\t\tvar template = JObject.FromObject(doubleConfig[\"template\"]);\n\t\t\tvar merged = new JObject();\n\n\t\t\t// Put together top-level objects\n\t\t\tif (system[info] != null)\n\t\t\t\tmerged.Add(info, Merge(template[info], system[info], info));\n\t\t\telse\n\t\t\t\tmerged.Add(info, template[info]);\n\n\t\t\tmerged.Add(devices, MergeArraysOnTopLevelProperty(template[devices] as JArray,\n\t\t\t\tsystem[devices] as JArray, \"key\", devices));\n\n\t\t\tif (system[rooms] == null)\n\t\t\t\tmerged.Add(rooms, template[rooms]);\n\t\t\telse\n\t\t\t\tmerged.Add(rooms, MergeArraysOnTopLevelProperty(template[rooms] as JArray,\n\t\t\t\t\tsystem[rooms] as JArray, \"key\", rooms));\n\n\t\t\tif (system[sourceLists] == null)\n\t\t\t\tmerged.Add(sourceLists, template[sourceLists]);\n\t\t\telse\n\t\t\t\tmerged.Add(sourceLists, Merge(template[sourceLists], system[sourceLists], sourceLists));\n\n\t\t    if (system[destinationLists] == null)\n\t\t        merged.Add(destinationLists, template[destinationLists]);\n\t\t    else\n\t\t        merged.Add(destinationLists,\n\t\t            Merge(template[destinationLists], system[destinationLists], destinationLists));\n\n\n            if (system[cameraLists] == null)\n                merged.Add(cameraLists, template[cameraLists]);\n            else\n                merged.Add(cameraLists, Merge(template[cameraLists], system[cameraLists], cameraLists));\n\n            if (system[audioControlPointLists] == null)\n                merged.Add(audioControlPointLists, template[audioControlPointLists]);\n            else\n                merged.Add(audioControlPointLists,\n                    Merge(template[audioControlPointLists], system[audioControlPointLists], audioControlPointLists));\n\n\n            // Template tie lines take precedence.  Config tool doesn't do them at system\n            // level anyway...\n            if (template[tieLines] != null)\n\t\t\t\tmerged.Add(tieLines, template[tieLines]);\n\t\t\telse if (system[tieLines] != null)\n\t\t\t\tmerged.Add(tieLines, system[tieLines]);\n\t\t\telse\n\t\t\t\tmerged.Add(tieLines, new JArray());\n\n            if (template[joinMaps] != null)\n                merged.Add(joinMaps, template[joinMaps]);\n            else\n                merged.Add(joinMaps, new JObject());\n\n\t\t\tif (system[global] != null)\n\t\t\t\tmerged.Add(global, Merge(template[global], system[global], global));\n\t\t\telse\n\t\t\t\tmerged.Add(global, template[global]);\n\n\t\t\t//Debug.Console(2, \"MERGED CONFIG RESULT: \\x0d\\x0a{0}\", merged);\n\t\t\treturn merged;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Merges the contents of a base and a delta array, matching the entries on a top-level property\n\t\t/// given by propertyName.  Returns a merge of them. Items in the delta array that do not have\n\t\t/// a matched item in base array will not be merged. Non keyed system items will replace the template items.\n\t\t/// </summary>\n\t\tstatic JArray MergeArraysOnTopLevelProperty(JArray a1, JArray a2, string propertyName, string path)\n\t\t{\n\t\t\tvar result = new JArray();\n\t\t\tif (a2 == null || a2.Count == 0) // If the system array is null or empty, return the template array\n\t\t\t\treturn a1;\n\t\t\telse if (a1 != null)\n\t\t\t{\n                if (a2[0][\"key\"] == null) // If the first item in the system array has no key, overwrite the template array\n                {                                                       // with the system array\n                    return a2;\n                }\n                else    // The arrays are keyed, merge them by key\n                {\n                    for (int i = 0; i < a1.Count(); i++)\n                    {\n                        var a1Dev = a1[i];\n                        // Try to get a system device and if found, merge it onto template\n                        var a2Match = a2.FirstOrDefault(t => t[propertyName].Equals(a1Dev[propertyName]));// t.Value<int>(\"uid\") == tmplDev.Value<int>(\"uid\"));\n                        if (a2Match != null)\n                        {\n                            var mergedItem = Merge(a1Dev, a2Match, string.Format(\"{0}[{1}].\", path, i));// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match));\n                            result.Add(mergedItem);\n                        }\n                        else\n                            result.Add(a1Dev);\n                    }\n                }\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\n\t\t/// <summary>\n\t\t/// Helper for using with JTokens.  Converts to JObject \n\t\t/// </summary>\n\t\tstatic JObject Merge(JToken t1, JToken t2, string path)\n\t\t{\n\t\t\treturn Merge(JObject.FromObject(t1), JObject.FromObject(t2), path);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Merge o2 onto o1\n\t\t/// </summary>\n        /// <param name=\"o1\"></param>\n        /// <param name=\"o2\"></param>\n        /// <param name=\"path\"></param>\n\t\tstatic JObject Merge(JObject o1, JObject o2, string path)\n\t\t{\n\t\t\tforeach (var o2Prop in o2)\n\t\t\t{\n\t\t\t\tvar propKey = o2Prop.Key;\n\t\t\t\tvar o1Value = o1[propKey];\n\t\t\t\tvar o2Value = o2[propKey];\n\n\t\t\t\t// if the property doesn't exist on o1, then add it.\n\t\t\t\tif (o1Value == null)\n\t\t\t\t{\n\t\t\t\t\to1.Add(propKey, o2Value);\n\t\t\t\t}\n\t\t\t\t// otherwise merge them\n\t\t\t\telse\n\t\t\t\t{\t\n\t\t\t\t\t// Drill down\n\t\t\t\t\tvar propPath = String.Format(\"{0}.{1}\", path, propKey);\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\n\t\t\t\t\t\tif (o1Value is JArray)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (o2Value is JArray)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\to1Value.Replace(MergeArraysOnTopLevelProperty(o1Value as JArray, o2Value as JArray, \"key\", propPath));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (o2Prop.Value.HasValues && o1Value.HasValues)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\to1Value.Replace(Merge(JObject.FromObject(o1Value), JObject.FromObject(o2Value), propPath));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\to1Value.Replace(o2Prop.Value);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e)\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.LogError($\"Cannot merge items at path {propPath}: \\r{e}\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn o1;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/Conversion/Convert.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Represents a EncodingHelper\n    /// </summary>\n    public class EncodingHelper\n    {\n        /// <summary>\n        /// ConvertUtf8ToAscii method\n        /// </summary>\n        public static string ConvertUtf8ToAscii(string utf8String)\n        {\n            return Encoding.ASCII.GetString(Encoding.UTF8.GetBytes(utf8String), 0, utf8String.Length);\n        }\n\n        /// <summary>\n        /// ConvertUtf8ToUtf16 method\n        /// </summary>\n        public static string ConvertUtf8ToUtf16(string utf8String)\n        {\n            return Encoding.Unicode.GetString(Encoding.UTF8.GetBytes(utf8String), 0, utf8String.Length);\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/CoreInterfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing Serilog;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Unique key interface to require a unique key for the class\n    /// </summary>\n\tpublic interface IKeyed\n\t{\n        /// <summary>\n        /// Unique Key\n        /// </summary>\n        [JsonProperty(\"key\")]\n\t\tstring Key { get; }\n    }\n\n    /// <summary>\n    /// Named Keyed device interface. Forces the device to have a Unique Key and a name. \n    /// </summary>\n\tpublic interface IKeyName : IKeyed\n    {\n        /// <summary>\n        /// Isn't it obvious :)\n        /// </summary>\n        [JsonProperty(\"name\")]\n\t\tstring Name { get; }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Core/Device.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Serilog.Events;\n\nnamespace PepperDash.Core\n{\n\t//*********************************************************************************************************\n\t/// <summary>\n\t/// Represents a Device\n\t/// </summary>\n\tpublic class Device : IKeyName\n\t{\n\n\t\t/// <summary>\n\t\t/// Unique Key\n\t\t/// </summary>\n\t\tpublic string Key { get; protected set; }\n\t\t/// <summary>\n\t\t/// Gets or sets the Name\n\t\t/// </summary>\n\t\tpublic string Name { get; protected set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\tpublic bool Enabled { get; protected set; }\n\n\t\tList<Action> _PreActivationActions;\n\t\tList<Action> _PostActivationActions;\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\tpublic static Device DefaultDevice { get { return _DefaultDevice; } }\n\t\tstatic Device _DefaultDevice = new Device(\"Default\", \"Default\");\n\n\t\t/// <summary>\n\t\t/// Base constructor for all Devices.\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\tpublic Device(string key)\n\t\t{\n\t\t\tKey = key;\n\t\t\tif (key.Contains(\".\")) Debug.LogMessage(LogEventLevel.Information, \"WARNING: Device key should not include '.'\", this);\n\t\t\tName = \"\";\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor with key and name\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"name\"></param>\n\t\tpublic Device(string key, string name) : this(key)\n\t\t{\n\t\t\tName = name;\n\n\t\t}\n\n\t\t//public Device(DeviceConfig config)\n\t\t//    : this(config.Key, config.Name)\n\t\t//{\n\t\t//    Config = config;\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Adds a pre activation action\n\t\t/// </summary>\n\t\t/// <param name=\"act\"></param>\n\t\tpublic void AddPreActivationAction(Action act)\n\t\t{\n\t\t\tif (_PreActivationActions == null)\n\t\t\t\t_PreActivationActions = new List<Action>();\n\t\t\t_PreActivationActions.Add(act);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds a post activation action\n\t\t/// </summary>\n\t\t/// <param name=\"act\"></param>\n\t\t/// <summary>\n\t\t/// AddPostActivationAction method\n\t\t/// </summary>\n\t\tpublic void AddPostActivationAction(Action act)\n\t\t{\n\t\t\tif (_PostActivationActions == null)\n\t\t\t\t_PostActivationActions = new List<Action>();\n\t\t\t_PostActivationActions.Add(act);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// PreActivate method\n\t\t/// </summary>\n\t\tpublic void PreActivate()\n\t\t{\n\t\t\tif (_PreActivationActions != null)\n\t\t\t\t_PreActivationActions.ForEach(a =>\n\t\t\t\t{\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\ta.Invoke();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e)\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.LogMessage(e, \"Error in PreActivationAction: \" + e.Message, this);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Activate method\n\t\t/// </summary>\n\t\tpublic bool Activate()\n\t\t{\n\t\t\t//if (_PreActivationActions != null)\n\t\t\t//    _PreActivationActions.ForEach(a => a.Invoke());\n\t\t\tvar result = CustomActivate();\n\t\t\t//if(result && _PostActivationActions != null)\n\t\t\t//    _PostActivationActions.ForEach(a => a.Invoke());\n\t\t\treturn result;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// PostActivate method\n\t\t/// </summary>\n\t\tpublic void PostActivate()\n\t\t{\n\t\t\tif (_PostActivationActions != null)\n\t\t\t\t_PostActivationActions.ForEach(a =>\n\t\t\t\t{\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\ta.Invoke();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e)\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.LogMessage(e, \"Error in PostActivationAction: \" + e.Message, this);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Called in between Pre and PostActivationActions when Activate() is called. \n\t\t/// Override to provide addtitional setup when calling activation.  Overriding classes \n\t\t/// do not need to call base.CustomActivate()\n\t\t/// </summary>\n\t\t/// <returns>true if device activated successfully.</returns>\n\t\t/// <summary>\n\t\t/// CustomActivate method\n\t\t/// </summary>\n\t\tpublic virtual bool CustomActivate() { return true; }\n\n\t\t/// <summary>\n\t\t/// Call to deactivate device - unlink events, etc.  Overriding classes do not\n\t\t/// need to call base.Deactivate()\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic virtual bool Deactivate() { return true; }\n\n\t\t/// <summary>\n\t\t/// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()\n\t\t/// </summary>\n\t\tpublic virtual void Initialize()\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Helper method to check object for bool value false and fire an Action method\n\t\t/// </summary>\n\t\t/// <param name=\"o\">Should be of type bool, others will be ignored</param>\n\t\t/// <param name=\"a\">Action to be run when o is false</param>\n\t\tpublic void OnFalse(object o, Action a)\n\t\t{\n\t\t\tif (o is bool && !(bool)o) a();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns a string representation of the object, including its key and name.\n\t\t/// </summary>\n\t\t/// <remarks>The returned string is formatted as \"{Key} - {Name}\". If the <c>Name</c> property is\n\t\t/// null or empty,  \"---\" is used in place of the name.</remarks>\n\t\t/// <returns>A string that represents the object, containing the key and name in the format \"{Key} - {Name}\".</returns>\n\t\t/// <summary>\n\t\t/// ToString method\n\t\t/// </summary>\n\t\tpublic override string ToString()\n\t\t{\n\t\t\treturn string.Format(\"{0} - {1}\", Key, string.IsNullOrEmpty(Name) ? \"---\" : Name);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/EthernetHelper.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing Serilog.Events;\n\nnamespace PepperDash.Core\n{\n /// <summary>\n /// Represents a EthernetHelper\n /// </summary>\n\tpublic class EthernetHelper\n\t{\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\tpublic static EthernetHelper LanHelper\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\tif (_LanHelper == null) _LanHelper = new EthernetHelper(0);\n\t\t\t\treturn _LanHelper;\n\t\t\t}\n\t\t}\n\t\tstatic EthernetHelper _LanHelper;\n\n\t\t// ADD OTHER HELPERS HERE\n\n  /// <summary>\n  /// Gets or sets the PortNumber\n  /// </summary>\n\t\tpublic int PortNumber { get; private set; }\n\n\t\tprivate EthernetHelper(int portNumber)\n\t\t{\n\t\t\tPortNumber = portNumber;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t[JsonProperty(\"linkActive\")]\n\t\tpublic bool LinkActive\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\tvar status = CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_LINK_STATUS, 0);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"LinkActive = {0}\", status);\n\t\t\t\treturn status == \"\";\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t[JsonProperty(\"dchpActive\")]\n\t\tpublic bool DhcpActive\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, 0) == \"ON\";\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t[JsonProperty(\"hostname\")]\n\t\tpublic string Hostname\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t[JsonProperty(\"ipAddress\")]\n\t\tpublic string IPAddress\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t[JsonProperty(\"subnetMask\")]\n\t\tpublic string SubnetMask\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t[JsonProperty(\"defaultGateway\")]\n\t\tpublic string DefaultGateway\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/EventArgs.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core\n{\n\t/// <summary>\n\t/// Bool change event args\n\t/// </summary>\n\tpublic class BoolChangeEventArgs : EventArgs\n\t{\n\t\t/// <summary>\n\t\t/// Boolean state property\n\t\t/// </summary>\n\t\tpublic bool State { get; set; }\n\t\t\n  /// <summary>\n  /// Gets or sets the IntValue\n  /// </summary>\n\t\tpublic ushort IntValue { get { return (ushort)(State ? 1 : 0); } }\n\t\t\n  /// <summary>\n  /// Gets or sets the Type\n  /// </summary>\n\t\tpublic ushort Type { get; set; }\n\t\t\n  /// <summary>\n  /// Gets or sets the Index\n  /// </summary>\n\t\tpublic ushort Index { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic BoolChangeEventArgs()\n\t\t{\n\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tpublic BoolChangeEventArgs(bool state, ushort type)\n\t\t{\n\t\t\tState = state;\n\t\t\tType = type;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\tpublic BoolChangeEventArgs(bool state, ushort type, ushort index)\n\t\t{\n\t\t\tState = state;\n\t\t\tType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a UshrtChangeEventArgs\n /// </summary>\n\tpublic class UshrtChangeEventArgs : EventArgs\n\t{\n\t\t/// <summary>\n\t\t/// Ushort change event args integer value\n\t\t/// </summary>\n\t\tpublic ushort IntValue { get; set; }\n\t\t\n  /// <summary>\n  /// Gets or sets the Type\n  /// </summary>\n\t\tpublic ushort Type { get; set; }\n\t\t\n  /// <summary>\n  /// Gets or sets the Index\n  /// </summary>\n\t\tpublic ushort Index { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic UshrtChangeEventArgs()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\t/// <param name=\"intValue\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tpublic UshrtChangeEventArgs(ushort intValue, ushort type)\n\t\t{\n\t\t\tIntValue = intValue;\n\t\t\tType = type;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\t/// <param name=\"intValue\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\tpublic UshrtChangeEventArgs(ushort intValue, ushort type, ushort index)\n\t\t{\n\t\t\tIntValue = intValue;\n\t\t\tType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a StringChangeEventArgs\n /// </summary>\n\tpublic class StringChangeEventArgs : EventArgs\n\t{\n\t\t/// <summary>\n\t\t/// String change event args value\n\t\t/// </summary>\n\t\tpublic string StringValue { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Type\n  /// </summary>\n\t\tpublic ushort Type { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Index\n  /// </summary>\n\t\tpublic ushort Index { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic StringChangeEventArgs()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\t/// <param name=\"stringValue\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tpublic StringChangeEventArgs(string stringValue, ushort type)\n\t\t{\n\t\t\tStringValue = stringValue;\n\t\t\tType = type;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\t/// <param name=\"stringValue\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\tpublic StringChangeEventArgs(string stringValue, ushort type, ushort index)\n\t\t{\n\t\t\tStringValue = stringValue;\n\t\t\tType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\t\n}"
  },
  {
    "path": "src/PepperDash.Core/GenericRESTfulCommunications/Constants.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.GenericRESTfulCommunications\n{\n\t/// <summary>\n\t/// Constants\n\t/// </summary>\n    public class GenericRESTfulConstants\n    {\n\t\t/// <summary>\n\t\t/// Generic boolean change\n\t\t/// </summary>\n\t\tpublic const ushort BoolValueChange = 1;\n\t\t/// <summary>\n\t\t/// Generic Ushort change\n\t\t/// </summary>\n\t\tpublic const ushort UshrtValueChange = 101;\n\t\t/// <summary>\n\t\t/// Response Code Ushort change\n\t\t/// </summary>\n\t\tpublic const ushort ResponseCodeChange = 102;\n\t\t/// <summary>\n\t\t/// Generic String chagne \n\t\t/// </summary>\n\t\tpublic const ushort StringValueChange = 201;\n\t\t/// <summary>\n\t\t/// Response string change\n\t\t/// </summary>\n\t\tpublic const ushort ResponseStringChange = 202;\n\t\t/// <summary>\n\t\t/// Error string change\n\t\t/// </summary>\n\t\tpublic const ushort ErrorStringChange = 203;\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/GenericRESTfulCommunications/GenericRESTfulClient.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.Net.Http;\nusing Crestron.SimplSharp.Net.Https;\n\nnamespace PepperDash.Core.GenericRESTfulCommunications\n{\n\t/// <summary>\n\t/// Generic RESTful communication class\n\t/// </summary>\n\tpublic class GenericRESTfulClient\n\t{\n\t\t/// <summary>\n\t\t/// Boolean event handler\n\t\t/// </summary>\n\t\tpublic event EventHandler<BoolChangeEventArgs> BoolChange;\n\t\t/// <summary>\n\t\t/// Ushort event handler\n\t\t/// </summary>\n\t\tpublic event EventHandler<UshrtChangeEventArgs> UshrtChange;\n\t\t/// <summary>\n\t\t/// String event handler\n\t\t/// </summary>\n\t\tpublic event EventHandler<StringChangeEventArgs> StringChange;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic GenericRESTfulClient()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Generic RESTful submit request\n\t\t/// </summary>\n\t\t/// <param name=\"url\"></param>\n\t\t/// <param name=\"port\"></param>\n\t\t/// <param name=\"requestType\"></param>\n\t\t/// <param name=\"username\"></param>\n\t\t/// <param name=\"password\"></param>\n        /// <param name=\"contentType\"></param>\n\t\tpublic void SubmitRequest(string url, ushort port, ushort requestType, string contentType, string username, string password)\n\t\t{\n\t\t\tif (url.StartsWith(\"https:\", StringComparison.OrdinalIgnoreCase))\n\t\t\t{\n\t\t\t\tSubmitRequestHttps(url, port, requestType, contentType, username, password);\n\t\t\t}\n\t\t\telse if (url.StartsWith(\"http:\", StringComparison.OrdinalIgnoreCase))\n\t\t\t{\n\t\t\t\tSubmitRequestHttp(url, port, requestType, contentType, username, password);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tOnStringChange(string.Format(\"Invalid URL {0}\", url), 0, GenericRESTfulConstants.ErrorStringChange);\n\t\t\t}\n\t\t}\n\n\t\t\t/// <summary>\n\t\t/// Private HTTP submit request\n\t\t/// </summary>\n\t\t/// <param name=\"url\"></param>\n\t\t/// <param name=\"port\"></param>\n\t\t/// <param name=\"requestType\"></param>\n        /// <param name=\"contentType\"></param>\n\t\t/// <param name=\"username\"></param>\n\t\t/// <param name=\"password\"></param>\n\t\tprivate void SubmitRequestHttp(string url, ushort port, ushort requestType, string contentType, string username, string password)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tHttpClient client = new HttpClient();\n\t\t\t\tHttpClientRequest request = new HttpClientRequest();\n\t\t\t\tHttpClientResponse response;\n\n\t\t\t\tclient.KeepAlive = false;\n\t\t\t\t\n\t\t\t\tif(port >= 1 || port <= 65535)\n\t\t\t\t\tclient.Port = port;\n\t\t\t\telse\n\t\t\t\t\tclient.Port = 80;\n\n\t\t\t\tvar authorization = \"\";\n\t\t\t\tif (!string.IsNullOrEmpty(username))\n\t\t\t\t\tauthorization = EncodeBase64(username, password);\n\n\t\t\t\tif (!string.IsNullOrEmpty(authorization))\n\t\t\t\t\trequest.Header.SetHeaderValue(\"Authorization\", authorization);\n\n\t\t\t\tif (!string.IsNullOrEmpty(contentType))\n\t\t\t\t\trequest.Header.ContentType = contentType;\n\n\t\t\t\trequest.Url.Parse(url);\n\t\t\t\trequest.RequestType = (Crestron.SimplSharp.Net.Http.RequestType)requestType;\n\t\t\t\t\n\t\t\t\tresponse = client.Dispatch(request);\n\n\t\t\t\tCrestronConsole.PrintLine(string.Format(\"SubmitRequestHttp Response[{0}]: {1}\", response.Code, response.ContentString.ToString()));\t\t\t\t\n\n\t\t\t\tif (!string.IsNullOrEmpty(response.ContentString.ToString()))\n\t\t\t\t\tOnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);\n\n\t\t\t\tif (response.Code > 0)\n\t\t\t\t\tOnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\t//var msg = string.Format(\"SubmitRequestHttp({0}, {1}, {2}) failed:{3}\", url, port, requestType, e.Message);\n\t\t\t\t//CrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\n\t\t\t\tCrestronConsole.PrintLine(e.Message);\n\t\t\t\tOnStringChange(e.Message, 0, GenericRESTfulConstants.ErrorStringChange);\t\t\t\t\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Private HTTPS submit request\n\t\t/// </summary>\n\t\t/// <param name=\"url\"></param>\n\t\t/// <param name=\"port\"></param>\n\t\t/// <param name=\"requestType\"></param>\n        /// <param name=\"contentType\"></param>\n\t\t/// <param name=\"username\"></param>\n\t\t/// <param name=\"password\"></param>\n\t\tprivate void SubmitRequestHttps(string url, ushort port, ushort requestType, string contentType, string username, string password)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tHttpsClient client = new HttpsClient();\n\t\t\t\tHttpsClientRequest request = new HttpsClientRequest();\n\t\t\t\tHttpsClientResponse response;\n\n\t\t\t\tclient.KeepAlive = false;\n\t\t\t\tclient.HostVerification = false;\n\t\t\t\tclient.PeerVerification = false;\t\t\t\t\n\n\t\t\t\tvar authorization = \"\";\n\t\t\t\tif (!string.IsNullOrEmpty(username))\n\t\t\t\t\tauthorization = EncodeBase64(username, password);\n\n\t\t\t\tif (!string.IsNullOrEmpty(authorization))\n\t\t\t\t\trequest.Header.SetHeaderValue(\"Authorization\", authorization);\n\n\t\t\t\tif (!string.IsNullOrEmpty(contentType))\n\t\t\t\t\trequest.Header.ContentType = contentType;\n\n\t\t\t\trequest.Url.Parse(url);\n\t\t\t\trequest.RequestType = (Crestron.SimplSharp.Net.Https.RequestType)requestType;\n\t\t\t\t\n\t\t\t\tresponse = client.Dispatch(request);\n\n\t\t\t\tCrestronConsole.PrintLine(string.Format(\"SubmitRequestHttp Response[{0}]: {1}\", response.Code, response.ContentString.ToString()));\n\n\t\t\t\tif(!string.IsNullOrEmpty(response.ContentString.ToString()))\n\t\t\t\t\tOnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);\n\n\t\t\t\tif(response.Code > 0)\n\t\t\t\t\tOnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);\n\t\t\t\t\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\t//var msg = string.Format(\"SubmitRequestHttps({0}, {1}, {2}, {3}, {4}) failed:{5}\", url, port, requestType, username, password, e.Message);\n\t\t\t\t//CrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\n\t\t\t\tCrestronConsole.PrintLine(e.Message);\n\t\t\t\tOnStringChange(e.Message, 0, GenericRESTfulConstants.ErrorStringChange);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Private method to encode username and password to Base64 string\n\t\t/// </summary>\n\t\t/// <param name=\"username\"></param>\n\t\t/// <param name=\"password\"></param>\n\t\t/// <returns>authorization</returns>\n\t\tprivate string EncodeBase64(string username, string password)\n\t\t{\n\t\t\tvar authorization = \"\";\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (!string.IsNullOrEmpty(username))\n\t\t\t\t{\n\t\t\t\t\tstring base64String = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding(\"ISO-8859-1\").GetBytes(string.Format(\"{0}:{1}\", username, password)));\n\t\t\t\t\tauthorization = string.Format(\"Basic {0}\", base64String);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"EncodeBase64({0}, {1}) failed:\\r{2}\", username, password, e);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\tErrorLog.Error(msg);\n\t\t\t\treturn \"\" ;\n\t\t\t}\n\n\t\t\treturn authorization;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected method to handle boolean change events\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnBoolChange(bool state, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = BoolChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new BoolChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tBoolChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected mehtod to handle ushort change events\n\t\t/// </summary>\n\t\t/// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnUshrtChange(ushort value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = UshrtChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new UshrtChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tUshrtChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected method to handle string change events\n\t\t/// </summary>\n\t\t/// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnStringChange(string value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = StringChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new StringChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tStringChange(this, args);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonStandardObjects/EventArgs and Constants.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.JsonStandardObjects\n{\n\t/// <summary>\n\t/// Constants for simpl modules\n\t/// </summary>\n\tpublic class JsonStandardDeviceConstants\n\t{\n\t\t/// <summary>\n\t\t/// Json object evaluated constant\n\t\t/// </summary>\n\t\tpublic const ushort JsonObjectEvaluated = 2;\n\n\t\t/// <summary>\n\t\t/// Json object changed constant\n\t\t/// </summary>\n\t\tpublic const ushort JsonObjectChanged = 104;\n\t}\n\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic class DeviceChangeEventArgs : EventArgs\n\t{\n\t\t/// <summary>\n\t\t/// Device change event args object\n\t\t/// </summary>\n\t\tpublic DeviceConfig Device { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Type\n  /// </summary>\n\t\tpublic ushort Type { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Index\n  /// </summary>\n\t\tpublic ushort Index { get; set; }\n\n\t\t/// <summary>\n\t\t/// Default constructor\n\t\t/// </summary>\n\t\tpublic DeviceChangeEventArgs()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\t/// <param name=\"device\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tpublic DeviceChangeEventArgs(DeviceConfig device, ushort type)\n\t\t{\n\t\t\tDevice = device;\n\t\t\tType = type;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\t/// <param name=\"device\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\tpublic DeviceChangeEventArgs(DeviceConfig device, ushort type, ushort index)\n\t\t{\n\t\t\tDevice = device;\n\t\t\tType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonStandardObjects/JsonToSimplDevice.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing PepperDash.Core.JsonToSimpl;\nusing Serilog.Events;\n\nnamespace PepperDash.Core.JsonStandardObjects\n{\n\t/// <summary>\n\t/// Device class\n\t/// </summary>\n\tpublic class DeviceConfig\n\t{\t\t\n\t\t/// <summary>\n\t\t/// JSON config key property\n\t\t/// </summary>\n\t\tpublic string key { get; set; }\n\t\t/// <summary>\n\t\t/// JSON config name property\n\t\t/// </summary>\n\t\tpublic string name { get; set; }\n\t\t/// <summary>\n\t\t/// JSON config type property\n\t\t/// </summary>\n\t\tpublic string type { get; set; }\n\t\t/// <summary>\n\t\t/// JSON config properties \n\t\t/// </summary>\n\t\tpublic PropertiesConfig properties { get; set; }\n\n\t\t/// <summary>\n\t\t/// Bool change event handler\n\t\t/// </summary>\n\t\tpublic event EventHandler<BoolChangeEventArgs> BoolChange;\n\t\t/// <summary>\n\t\t/// Ushort change event handler\n\t\t/// </summary>\n\t\tpublic event EventHandler<UshrtChangeEventArgs> UshrtChange;\n\t\t/// <summary>\n\t\t/// String change event handler\n\t\t/// </summary>\n\t\tpublic event EventHandler<StringChangeEventArgs> StringChange;\n\t\t/// <summary>\n\t\t/// Object change event handler\n\t\t/// </summary>\n\t\tpublic event EventHandler<DeviceChangeEventArgs> DeviceChange;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic DeviceConfig()\n\t\t{\n\t\t\tproperties = new PropertiesConfig();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Initialize method\n\t\t/// </summary>\n\t\t/// <param name=\"uniqueID\"></param>\n\t\t/// <param name=\"deviceKey\"></param>\n  /// <summary>\n  /// Initialize method\n  /// </summary>\n\t\tpublic void Initialize(string uniqueID, string deviceKey)\n\t\t{\n\t\t\t// S+ set EvaluateFb low\n\t\t\tOnBoolChange(false, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);\n\t\t\t// validate parameters\n\t\t\tif (string.IsNullOrEmpty(uniqueID) || string.IsNullOrEmpty(deviceKey))\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"UniqueID ({0} or key ({1} is null or empty\", uniqueID, deviceKey);\n\t\t\t\t// S+ set EvaluteFb high\n\t\t\t\tOnBoolChange(true, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tkey = deviceKey;\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\t// get the file using the unique ID\n\t\t\t\tJsonToSimplMaster jsonMaster = J2SGlobal.GetMasterByFile(uniqueID);\n\t\t\t\tif (jsonMaster == null)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"Could not find JSON file with uniqueID {0}\", uniqueID);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// get the device configuration using the key\n\t\t\t\tvar devices = jsonMaster.JsonObject.ToObject<RootObject>().devices;\n\t\t\t\tvar device = devices.FirstOrDefault(d => d.key.Equals(key));\n\t\t\t\tif (device == null)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"Could not find device with key {0}\", key);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tOnObjectChange(device, 0, JsonStandardDeviceConstants.JsonObjectChanged);\n\n\t\t\t\tvar index = devices.IndexOf(device);\n\t\t\t\tOnStringChange(string.Format(\"devices[{0}]\", index), 0, JsonToSimplConstants.FullPathToArrayChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"Device {0} lookup failed:\\r{1}\", key, e);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\tErrorLog.Error(msg);\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\t// S+ set EvaluteFb high\n\t\t\t\tOnBoolChange(true, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);\n\t\t\t}\n\t\t}\n\n\t\t#region EventHandler Helpers\n\n\t\t/// <summary>\n\t\t/// BoolChange event handler helper\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnBoolChange(bool state, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = BoolChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new BoolChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tBoolChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// UshrtChange event handler helper\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnUshrtChange(ushort state, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = UshrtChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new UshrtChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tUshrtChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// StringChange event handler helper\n\t\t/// </summary>\n\t\t/// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnStringChange(string value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = StringChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new StringChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tStringChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ObjectChange event handler helper\n\t\t/// </summary>\n\t\t/// <param name=\"device\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnObjectChange(DeviceConfig device, ushort index, ushort type)\n\t\t{\n\t\t\tif (DeviceChange != null)\n\t\t\t{\n\t\t\t\tvar args = new DeviceChangeEventArgs(device, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tDeviceChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t#endregion EventHandler Helpers\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.JsonStandardObjects\n{\n\t/*\n\tConvert JSON snippt to C#: http://json2csharp.com/#\n\t\n\tJSON Snippet:\n\t{\n\t\t\"devices\": [\n\t\t\t{\n\t\t\t\t\"key\": \"deviceKey\",\n\t\t\t\t\"name\": \"deviceName\",\n\t\t\t\t\"type\": \"deviceType\",\n\t\t\t\t\"properties\": {\n\t\t\t\t\t\"deviceId\": 1,\n\t\t\t\t\t\"enabled\": true,\n\t\t\t\t\t\"control\": {\n\t\t\t\t\t\t\"method\": \"methodName\",\n\t\t\t\t\t\t\"controlPortDevKey\": \"deviceControlPortDevKey\",\n\t\t\t\t\t\t\"controlPortNumber\": 1,\n\t\t\t\t\t\t\"comParams\": {\n\t\t\t\t\t\t\t\"baudRate\": 9600,\n\t\t\t\t\t\t\t\"dataBits\": 8,\n\t\t\t\t\t\t\t\"stopBits\": 1,\n\t\t\t\t\t\t\t\"parity\": \"None\",\n\t\t\t\t\t\t\t\"protocol\": \"RS232\",\n\t\t\t\t\t\t\t\"hardwareHandshake\": \"None\",\n\t\t\t\t\t\t\t\"softwareHandshake\": \"None\",\n\t\t\t\t\t\t\t\"pacing\": 0\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"tcpSshProperties\": {\n\t\t\t\t\t\t\t\"address\": \"172.22.1.101\",\n\t\t\t\t\t\t\t\"port\": 23,\n\t\t\t\t\t\t\t\"username\": \"user01\",\n\t\t\t\t\t\t\t\"password\": \"password01\",\n\t\t\t\t\t\t\t\"autoReconnect\": false,\n\t\t\t\t\t\t\t\"autoReconnectIntervalMs\": 10000\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t]\n\t}\n\t*/\n /// <summary>\n /// Represents a ComParamsConfig\n /// </summary>\n\tpublic class ComParamsConfig\n\t{\n  /// <summary>\n  /// Gets or sets the baudRate\n  /// </summary>\n\t\tpublic int baudRate { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int dataBits { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int stopBits { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string parity { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string protocol { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string hardwareHandshake { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string softwareHandshake { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int pacing { get; set; }\n\n\t\t// convert properties for simpl\n  /// <summary>\n  /// Gets or sets the simplBaudRate\n  /// </summary>\n\t\tpublic ushort simplBaudRate { get { return Convert.ToUInt16(baudRate); } }\n  /// <summary>\n  /// Gets or sets the simplDataBits\n  /// </summary>\n\t\tpublic ushort simplDataBits { get { return Convert.ToUInt16(dataBits); } }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort simplStopBits { get { return Convert.ToUInt16(stopBits); } }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort simplPacing { get { return Convert.ToUInt16(pacing); } }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ComParamsConfig()\n\t\t{\n\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Device TCP/SSH properties class\n\t/// </summary>\n\tpublic class TcpSshPropertiesConfig\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string address { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int port { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string username { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string password { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic bool autoReconnect { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int autoReconnectIntervalMs { get; set; }\n\n\t\t// convert properties for simpl\n  /// <summary>\n  /// Gets or sets the simplPort\n  /// </summary>\n\t\tpublic ushort simplPort { get { return Convert.ToUInt16(port); } }\n  /// <summary>\n  /// Gets or sets the simplAutoReconnect\n  /// </summary>\n\t\tpublic ushort simplAutoReconnect { get { return (ushort)(autoReconnect ? 1 : 0); } }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort simplAutoReconnectIntervalMs { get { return Convert.ToUInt16(autoReconnectIntervalMs); } }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic TcpSshPropertiesConfig()\n\t\t{\n\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Device control class\n\t/// </summary>\n\tpublic class ControlConfig\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string method { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string controlPortDevKey { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int controlPortNumber { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ComParamsConfig comParams { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic TcpSshPropertiesConfig tcpSshProperties { get; set; }\n\n\t\t// convert properties for simpl\n  /// <summary>\n  /// Gets or sets the simplControlPortNumber\n  /// </summary>\n\t\tpublic ushort simplControlPortNumber { get { return Convert.ToUInt16(controlPortNumber); } }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ControlConfig()\n\t\t{\n\t\t\tcomParams = new ComParamsConfig();\n\t\t\ttcpSshProperties = new TcpSshPropertiesConfig();\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a PropertiesConfig\n /// </summary>\n\tpublic class PropertiesConfig\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int deviceId { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic bool enabled { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ControlConfig control { get; set; }\n\n\t\t// convert properties for simpl\n  /// <summary>\n  /// Gets or sets the simplDeviceId\n  /// </summary>\n\t\tpublic ushort simplDeviceId { get { return Convert.ToUInt16(deviceId); } }\n  /// <summary>\n  /// Gets or sets the simplEnabled\n  /// </summary>\n\t\tpublic ushort simplEnabled { get { return (ushort)(enabled ? 1 : 0); } }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic PropertiesConfig()\n\t\t{\n\t\t\tcontrol = new ControlConfig();\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Root device class\n\t/// </summary>\n\tpublic class RootObject\n\t{\n        /// <summary>\n        /// The collection of devices\n        /// </summary>\n\t\tpublic List<DeviceConfig> devices { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/Constants.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n\t/// <summary>\n\t/// Constants for Simpl modules\n\t/// </summary>\n\tpublic class JsonToSimplConstants\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort BoolValueChange = 1;\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort JsonIsValidBoolChange = 2;\n\n        /// <summary>\n        /// Reports the if the device is 3-series compatible\n        /// </summary>\n        public const ushort ProgramCompatibility3SeriesChange = 3;\n\n        /// <summary>\n        /// Reports the if the device is 4-series compatible\n        /// </summary>\n        public const ushort ProgramCompatibility4SeriesChange = 4;\n\n        /// <summary>\n        /// Reports the device platform enum value\n        /// </summary>\n        public const ushort DevicePlatformValueChange = 5;\n\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort UshortValueChange = 101;        \n\t\t\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort StringValueChange = 201;\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort FullPathToArrayChange = 202;\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort ActualFilePathChange = 203;\n\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort FilenameResolvedChange = 204;\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort FilePathResolvedChange = 205;\n\n        /// <summary>\n        /// Reports the root directory change\n        /// </summary>\n        public const ushort RootDirectoryChange = 206;\n\n        /// <summary>\n        /// Reports the room ID change\n        /// </summary>\n        public const ushort RoomIdChange = 207;\n\n        /// <summary>\n        /// Reports the room name change\n        /// </summary>\n        public const ushort RoomNameChange = 208;\n\t}\n\n\t/// <summary>\n\t/// S+ values delegate\n\t/// </summary>\n\tpublic delegate void SPlusValuesDelegate();\n\n\t/// <summary>\n\t/// S+ values wrapper\n\t/// </summary>\n\tpublic class SPlusValueWrapper\n\t{\n  /// <summary>\n  /// Gets or sets the ValueType\n  /// </summary>\n\t\tpublic SPlusType ValueType { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort Index { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort BoolUShortValue { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string StringValue { get; set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic SPlusValueWrapper() {}\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"type\"></param>\n        /// <param name=\"index\"></param>\n\t\tpublic SPlusValueWrapper(SPlusType type, ushort index)\n\t\t{\n\t\t\tValueType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\n\n /// <summary>\n /// Enumeration of SPlusType values\n /// </summary>\n\tpublic enum SPlusType\n\t{\n        /// <summary>\n        /// Digital\n        /// </summary>\n\t\tDigital, \n        /// <summary>\n        /// Analog\n        /// </summary>\n        Analog, \n        /// <summary>\n        /// String\n        /// </summary>\n        String\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/Global.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Serilog.Events;\n\n//using PepperDash.Core;\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n    /// <summary>\n    /// The global class to manage all the instances of JsonToSimplMaster \n    /// </summary>\n\tpublic class J2SGlobal\n\t{\n\t\tstatic List<JsonToSimplMaster> Masters = new List<JsonToSimplMaster>();\n\n\n\t\t/// <summary>\n\t\t/// Adds a file master.  If the master's key or filename is equivalent to any existing \n\t\t/// master, this will fail\n\t\t/// </summary>\n\t\t/// <param name=\"master\">New master to add</param>\n        /// \n  /// <summary>\n  /// AddMaster method\n  /// </summary>\n\t\tpublic static void AddMaster(JsonToSimplMaster master)\n\t\t{\n\t\t\tif (master == null)\n\t\t\t\tthrow new ArgumentNullException(\"master\");\n\n\t\t\tif (string.IsNullOrEmpty(master.UniqueID))\n\t\t\t\tthrow new InvalidOperationException(\"JSON Master cannot be added with a null UniqueId\");\n\t\t\t\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"JSON Global adding master {0}\", master.UniqueID);\n\n\t\t\tif (Masters.Contains(master)) return;\n\n\t\t\tvar existing = Masters.FirstOrDefault(m =>\n\t\t\t\tm.UniqueID.Equals(master.UniqueID, StringComparison.OrdinalIgnoreCase));\n\t\t\tif (existing == null)\n\t\t\t{\n\t\t\t\tMasters.Add(master);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"Cannot add JSON Master with unique ID '{0}'.\\rID is already in use on another master.\", master.UniqueID);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\tErrorLog.Warn(msg);\n\t\t\t}\n\t\t}\n\n  /// <summary>\n  /// GetMasterByFile method\n  /// </summary>\n\t\tpublic static JsonToSimplMaster GetMasterByFile(string file)\n\t\t{\n\t\t\treturn Masters.FirstOrDefault(m => m.UniqueID.Equals(file, StringComparison.OrdinalIgnoreCase));\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Newtonsoft.Json.Linq;\nusing Serilog.Events;\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n /// <summary>\n /// Represents a JsonToSimplArrayLookupChild\n /// </summary>\n\tpublic class JsonToSimplArrayLookupChild : JsonToSimplChildObjectBase\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string SearchPropertyName { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string SearchPropertyValue { get; set; }\n\n\t\tint ArrayIndex;\n\n\t\t/// <summary>\n\t\t/// For gt2.4.1 array lookups\n\t\t/// </summary>\n\t\t/// <param name=\"file\"></param>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"pathPrefix\"></param>\n\t\t/// <param name=\"pathSuffix\"></param>\n\t\t/// <param name=\"searchPropertyName\"></param>\n\t\t/// <param name=\"searchPropertyValue\"></param>\n\t\tpublic void Initialize(string file, string key, string pathPrefix, string pathSuffix,\n\t\t\tstring searchPropertyName, string searchPropertyValue)\n\t\t{\n\t\t\tbase.Initialize(file, key, pathPrefix, pathSuffix);\n\t\t\tSearchPropertyName = searchPropertyName;\n\t\t\tSearchPropertyValue = searchPropertyValue;\n\t\t}\n\n\n\t\t/// <summary>\n\t\t/// For newer >=2.4.1 array lookups. \n\t\t/// </summary>\n\t\t/// <param name=\"file\"></param>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"pathPrefix\"></param>\n\t\t/// <param name=\"pathAppend\"></param>\n\t\t/// <param name=\"pathSuffix\"></param>\n\t\t/// <param name=\"searchPropertyName\"></param>\n\t\t/// <param name=\"searchPropertyValue\"></param>\n\t\tpublic void InitializeWithAppend(string file, string key, string pathPrefix, string pathAppend,\n\t\t\tstring pathSuffix, string searchPropertyName, string searchPropertyValue)\n\t\t{\n\t\t\tstring pathPrefixWithAppend = (pathPrefix != null ? pathPrefix : \"\") + GetPathAppend(pathAppend);\n\t\t\tbase.Initialize(file, key, pathPrefixWithAppend, pathSuffix);\n\n\t\t\tSearchPropertyName = searchPropertyName;\n\t\t\tSearchPropertyValue = searchPropertyValue;\n\t\t}\n\n\n\n\t\t//PathPrefix+ArrayName+[x]+path+PathSuffix\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"path\"></param>\n\t\t/// <returns></returns>\n\t\tprotected override string GetFullPath(string path)\n\t\t{\n\t\t\treturn string.Format(\"{0}[{1}].{2}{3}\",\n\t\t\t\tPathPrefix == null ? \"\" : PathPrefix,\n\t\t\t\tArrayIndex,\n\t\t\t\tpath,\n\t\t\t\tPathSuffix == null ? \"\" : PathSuffix);\n\t\t}\n\n  /// <summary>\n  /// ProcessAll method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic override void ProcessAll()\n\t\t{\n\t\t\tif (FindInArray())\n\t\t\t\tbase.ProcessAll();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Provides the path append for GetFullPath\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tstring GetPathAppend(string a)\n\t\t{\n\t\t\tif (string.IsNullOrEmpty(a))\n\t\t\t{\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\tif (a.StartsWith(\".\"))\n\t\t\t{\n\t\t\t\treturn a;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn \".\" + a;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tbool FindInArray()\n\t\t{\n\t\t\tif (Master == null)\n\t\t\t\tthrow new InvalidOperationException(\"Cannot do operations before master is linked\");\n\t\t\tif (Master.JsonObject == null)\n\t\t\t\tthrow new InvalidOperationException(\"Cannot do operations before master JSON has read\");\n\t\t\tif (PathPrefix == null)\n\t\t\t\tthrow new InvalidOperationException(\"Cannot do operations before PathPrefix is set\");\n\n\n\t\t\tvar token = Master.JsonObject.SelectToken(PathPrefix);\n\t\t\tif (token is JArray)\n\t\t\t{\n\t\t\t\tvar array = token as JArray;\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tvar item = array.FirstOrDefault(o =>\n\t\t\t\t\t{\n\t\t\t\t\t\tvar prop = o[SearchPropertyName];\n\t\t\t\t\t\treturn prop != null && prop.Value<string>()\n\t\t\t\t\t\t.Equals(SearchPropertyValue, StringComparison.OrdinalIgnoreCase);\n\t\t\t\t\t});\n\t\t\t\t\tif (item == null)\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Debug,\"JSON Child[{0}] Array '{1}' '{2}={3}' not found: \", Key,\n\t\t\t\t\t\t\tPathPrefix, SearchPropertyName, SearchPropertyValue);\n\t\t\t\t\t\tthis.LinkedToObject = false;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.LinkedToObject = true;\n\t\t\t\t\tArrayIndex = array.IndexOf(item);\n\t\t\t\t\tOnStringChange(string.Format(\"{0}[{1}]\", PathPrefix, ArrayIndex), 0, JsonToSimplConstants.FullPathToArrayChange);\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"JSON Child[{0}] Found array match at index {1}\", Key, ArrayIndex);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tcatch (Exception e)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(e, \"JSON Child[{key}] Array '{pathPrefix}' lookup error: '{searchPropertyName}={searchPropertyValue}'\", null, Key,\n\t\t\t\t\t\tPathPrefix, SearchPropertyName, SearchPropertyValue, e);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"JSON Child[{0}] Path '{1}' is not an array\", Key, PathPrefix);\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/JsonToSimplChildObjectBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n    /// <summary>\n    /// Base class for JSON objects\n    /// </summary>\n\tpublic abstract class JsonToSimplChildObjectBase : IKeyed\n\t{\n        /// <summary>\n        /// Notifies of bool change\n        /// </summary>\n\t\tpublic event EventHandler<BoolChangeEventArgs> BoolChange;\n        /// <summary>\n        /// Notifies of ushort change\n        /// </summary>\n\t\tpublic event EventHandler<UshrtChangeEventArgs> UShortChange;\n        /// <summary>\n        /// Notifies of string change\n        /// </summary>\n\t\tpublic event EventHandler<StringChangeEventArgs> StringChange;\n\n        /// <summary>\n        /// Delegate to get all values\n        /// </summary>\n\t\tpublic SPlusValuesDelegate GetAllValuesDelegate { get; set; }\n\n  /// <summary>\n  /// Gets or sets the SetAllPathsDelegate\n  /// </summary>\n\t\tpublic SPlusValuesDelegate SetAllPathsDelegate { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Key\n  /// </summary>\n\t\tpublic string Key { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// This will be prepended to all paths to allow path swapping or for more organized\n\t\t/// sub-paths\n\t\t/// </summary>\n\t\tpublic string PathPrefix { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// This is added to the end of all paths\n\t\t/// </summary>\n\t\tpublic string PathSuffix { get; protected set; }\n\n  /// <summary>\n  /// Gets or sets the LinkedToObject\n  /// </summary>\n\t\tpublic bool LinkedToObject { get; protected set; }\n\n        /// <summary>\n        /// Reference to Master instance\n        /// </summary>\n\t\tprotected JsonToSimplMaster Master;\n\n        /// <summary>\n        /// Paths to boolean values in JSON structure\n        /// </summary>\n        protected Dictionary<ushort, string> BoolPaths = new Dictionary<ushort, string>();\n        /// <summary>\n        /// Paths to numeric values in JSON structure\n        /// </summary>\n\t\tprotected Dictionary<ushort, string> UshortPaths = new Dictionary<ushort, string>();\n        /// <summary>\n        /// Paths to string values in JSON structure\n        /// </summary>\n\t\tprotected Dictionary<ushort, string> StringPaths = new Dictionary<ushort, string>();\n\n\t\t/// <summary>\n\t\t/// Call this before doing anything else\n\t\t/// </summary>\n        /// <param name=\"masterUniqueId\"></param>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"pathPrefix\"></param>\n\t\t/// <param name=\"pathSuffix\"></param>\n\t\tpublic void Initialize(string masterUniqueId, string key, string pathPrefix, string pathSuffix)\n\t\t{\n\t\t\tKey = key;\n\t\t\tPathPrefix = pathPrefix;\n\t\t\tPathSuffix = pathSuffix;\n\n\t\t\tMaster = J2SGlobal.GetMasterByFile(masterUniqueId);\n\t\t\tif (Master != null)\n\t\t\t\tMaster.AddChild(this);\n\t\t\telse\n\t\t\t\tDebug.Console(1, \"JSON Child [{0}] cannot link to master {1}\", key, masterUniqueId);\n\t\t}\n\n        /// <summary>\n        /// Sets the path prefix for the object\n        /// </summary>\n        /// <param name=\"pathPrefix\"></param>\n  /// <summary>\n  /// SetPathPrefix method\n  /// </summary>\n\t\tpublic void SetPathPrefix(string pathPrefix)\n\t\t{\n\t\t\tPathPrefix = pathPrefix;\n\t\t}\n\t\t/// <summary>\n\t\t/// Set the JPath to evaluate for a given bool out index.\n\t\t/// </summary>\n\t\tpublic void SetBoolPath(ushort index, string path)\n\t\t{\n\t\t\tDebug.Console(1, \"JSON Child[{0}] SetBoolPath {1}={2}\", Key, index, path);\n\t\t\tif (path == null || path.Trim() == string.Empty) return;\n\t\t\tBoolPaths[index] = path;\n\t\t}\n\n  /// <summary>\n  /// SetUshortPath method\n  /// </summary>\n\t\tpublic void SetUshortPath(ushort index, string path)\n\t\t{\n\t\t\tDebug.Console(1, \"JSON Child[{0}] SetUshortPath {1}={2}\", Key, index, path);\n\t\t\tif (path == null || path.Trim() == string.Empty) return;\n\t\t\tUshortPaths[index] = path;\n\t\t}\n\n  /// <summary>\n  /// SetStringPath method\n  /// </summary>\n\t\tpublic void SetStringPath(ushort index, string path)\n\t\t{\n\t\t\tDebug.Console(1, \"JSON Child[{0}] SetStringPath {1}={2}\", Key, index, path);\n\t\t\tif (path == null || path.Trim() == string.Empty) return;\n\t\t\tStringPaths[index] = path;\n\t\t}\n\n  /// <summary>\n  /// ProcessAll method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic virtual void ProcessAll()\n\t\t{\n\t\t\tif (!LinkedToObject)\n\t\t\t{\n\t\t\t\tDebug.Console(1, this, \"Not linked to object in file.  Skipping\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (SetAllPathsDelegate == null)\n\t\t\t{\n\t\t\t\tDebug.Console(1, this, \"No SetAllPathsDelegate set. Ignoring ProcessAll\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSetAllPathsDelegate();\n\t\t\tforeach (var kvp in BoolPaths)\n\t\t\t\tProcessBoolPath(kvp.Key);\n\t\t\tforeach (var kvp in UshortPaths)\n\t\t\t\tProcessUshortPath(kvp.Key);\n\t\t\tforeach (var kvp in StringPaths)\n\t\t\t\tProcessStringPath(kvp.Key);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Processes a bool property, converting to bool, firing off a BoolChange event\n\t\t/// </summary>\n\t\tvoid ProcessBoolPath(ushort index)\n\t\t{\n\t\t\tstring response;\n\t\t\tif (Process(BoolPaths[index], out response))\n\t\t\t\tOnBoolChange(response.Equals(\"true\", StringComparison.OrdinalIgnoreCase),\n\t\t\t\t\tindex, JsonToSimplConstants.BoolValueChange);\n\t\t\telse { }\n\t\t\t// OnBoolChange(false, index, JsonToSimplConstants.BoolValueChange);\n\t\t}\n\n\t\t// Processes the path to a ushort, converting to ushort if able, twos complement if necessary, firing off UshrtChange event\n        void ProcessUshortPath(ushort index) {\n            string response;\n            if (Process(UshortPaths[index], out response)) {\n                ushort val;\n                try { val = Convert.ToInt32(response) < 0 ? (ushort)(Convert.ToInt16(response) + 65536) : Convert.ToUInt16(response); }\n                catch { val = 0; }\n\n                OnUShortChange(val, index, JsonToSimplConstants.UshortValueChange);\n            }\n            else { }\n            // OnUShortChange(0, index, JsonToSimplConstants.UshortValueChange);\n        }\n\n\t\t// Processes the path to a string property and fires of a StringChange event.\n\t\tvoid ProcessStringPath(ushort index)\n\t\t{\n\t\t\tstring response;\n\t\t\tif (Process(StringPaths[index], out response))\n\t\t\t\tOnStringChange(response, index, JsonToSimplConstants.StringValueChange);\n\t\t\telse { }\n\t\t\t// OnStringChange(\"\", index, JsonToSimplConstants.StringValueChange);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Processes the given path. \n\t\t/// </summary>\n\t\t/// <param name=\"path\">JPath formatted path to the desired property</param>\n\t\t/// <param name=\"response\">The string value of the property, or a default value if it \n\t\t/// doesn't exist</param>\n\t\t/// <returns> This will return false in the case that EvaulateAllOnJsonChange\n\t\t/// is false and the path does not evaluate to a property in the incoming JSON. </returns>\n\t\tbool Process(string path, out string response)\n\t\t{\n\t\t\tpath = GetFullPath(path);\n\t\t\tDebug.Console(1, \"JSON Child[{0}] Processing {1}\", Key, path);\n\t\t\tresponse = \"\";\n\t\t\tif (Master == null)\n\t\t\t{\n\t\t\t\tDebug.Console(1, \"JSONChild[{0}] cannot process without Master attached\", Key);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (Master.JsonObject != null && path != string.Empty)\n\t\t\t{\n\t\t\t\tbool isCount = false;\n\t\t\t\tpath = path.Trim();\n\t\t\t\tif (path.EndsWith(\".Count\"))\n\t\t\t\t{\n\t\t\t\t\tpath = path.Remove(path.Length - 6, 6);\n\t\t\t\t\tisCount = true;\n\t\t\t\t}\n\t\t\t\ttry // Catch a strange cast error on a bad path\n\t\t\t\t{\n\t\t\t\t\tvar t = Master.JsonObject.SelectToken(path);\n\t\t\t\t\tif (t != null)\n\t\t\t\t\t{\n\t\t\t\t\t\t// return the count of children objects - if any\n\t\t\t\t\t\tif (isCount)\n\t\t\t\t\t\t\tresponse = (t.HasValues ? t.Children().Count() : 0).ToString();\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tresponse = t.Value<string>();\n\t\t\t\t\t\tDebug.Console(1, \"   ='{0}'\", response);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch\n\t\t\t\t{\n\t\t\t\t\tresponse = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If the path isn't found, return this to determine whether to pass out the non-value or not.\n\t\t\treturn false;\n\t\t}\n\n\n\t\t//************************************************************************************************\n\t\t// Save-related functions\n\n\n\t\t/// <summary>\n\t\t/// Called from Master to read inputs and update their values in master JObject\n\t\t/// Callback should hit one of the following four methods\n\t\t/// </summary>\n\t\tpublic void UpdateInputsForMaster()\n\t\t{\n\t\t\tif (!LinkedToObject)\n\t\t\t{\n\t\t\t\tDebug.Console(1, this, \"Not linked to object in file.  Skipping\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (SetAllPathsDelegate == null)\n\t\t\t{\n\t\t\t\tDebug.Console(1, this, \"No SetAllPathsDelegate set. Ignoring UpdateInputsForMaster\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSetAllPathsDelegate();\n\t\t\tvar del = GetAllValuesDelegate;\n\t\t\tif (del != null)\n\t\t\t\tGetAllValuesDelegate();\n\t\t}\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"theValue\"></param>\n  /// <summary>\n  /// USetBoolValue method\n  /// </summary>\n\t\tpublic void USetBoolValue(ushort key, ushort theValue)\n\t\t{\n\t\t\tSetBoolValue(key, theValue == 1);\n\t\t}\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"theValue\"></param>\n  /// <summary>\n  /// SetBoolValue method\n  /// </summary>\n\t\tpublic void SetBoolValue(ushort key, bool theValue)\n\t\t{\n\t\t\tif (BoolPaths.ContainsKey(key))\n\t\t\t\tSetValueOnMaster(BoolPaths[key], new JValue(theValue));\n\t\t}\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"theValue\"></param>\n  /// <summary>\n  /// SetUShortValue method\n  /// </summary>\n\t\tpublic void SetUShortValue(ushort key, ushort theValue)\n\t\t{\n\t\t\tif (UshortPaths.ContainsKey(key))\n\t\t\t\tSetValueOnMaster(UshortPaths[key], new JValue(theValue));\n\t\t}\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"theValue\"></param>\n  /// <summary>\n  /// SetStringValue method\n  /// </summary>\n\t\tpublic void SetStringValue(ushort key, string theValue)\n\t\t{\n\t\t\tif (StringPaths.ContainsKey(key))\n\t\t\t\tSetValueOnMaster(StringPaths[key], new JValue(theValue));\n\t\t}\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"keyPath\"></param>\n        /// <param name=\"valueToSave\"></param>\n  /// <summary>\n  /// SetValueOnMaster method\n  /// </summary>\n\t\tpublic void SetValueOnMaster(string keyPath, JValue valueToSave)\n\t\t{\n\t\t\tvar path = GetFullPath(keyPath);\n\t\t\ttry\n\t\t\t{\n\t\t\t\tDebug.Console(1, \"JSON Child[{0}] Queueing value on master {1}='{2}'\", Key, path, valueToSave);\n\n\t\t\t\t//var token = Master.JsonObject.SelectToken(path);\n\t\t\t\t//if (token != null) // The path exists in the file\n\t\t\t\tMaster.AddUnsavedValue(path, valueToSave);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.Console(1, \"JSON Child[{0}] Failed setting value for path '{1}'\\r{2}\", Key, path, e);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Called during Process(...) to get the path to a given property. By default, \n\t\t/// returns PathPrefix+path+PathSuffix.  Override to change the way path is built.\n\t\t/// </summary>\n\t\tprotected virtual string GetFullPath(string path)\n\t\t{\n\t\t\treturn (PathPrefix != null ? PathPrefix : \"\") +\n\t\t\t\tpath + (PathSuffix != null ? PathSuffix : \"\");\n\t\t}\n\n\t\t// Helpers for events\n\t\t//******************************************************************************************\n        /// <summary>\n        /// Event helper\n        /// </summary>\n        /// <param name=\"state\"></param>\n        /// <param name=\"index\"></param>\n        /// <param name=\"type\"></param>\n\t\tprotected void OnBoolChange(bool state, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = BoolChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new BoolChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tBoolChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t//******************************************************************************************\n        /// <summary>\n        /// Event helper\n        /// </summary>\n        /// <param name=\"state\"></param>\n        /// <param name=\"index\"></param>\n        /// <param name=\"type\"></param>\n\t\tprotected void OnUShortChange(ushort state, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = UShortChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new UshrtChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tUShortChange(this, args);\n\t\t\t}\n\t\t}\n\n        /// <summary>\n        /// Event helper\n        /// </summary>\n        /// <param name=\"value\"></param>\n        /// <param name=\"index\"></param>\n        /// <param name=\"type\"></param>\n\t\tprotected void OnStringChange(string value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = StringChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new StringChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tStringChange(this, args);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n    /// <summary>\n    /// Represents a JSON file that can be read and written to\n    /// </summary>\n    public class JsonToSimplFileMaster : JsonToSimplMaster\n    {\n        /// <summary>\n        ///  Sets the filepath as well as registers this with the Global.Masters list\n        /// </summary>\n        public string Filepath { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the ActualFilePath\n        /// </summary>\n        public string ActualFilePath { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Filename\n        /// </summary>\n        public string Filename { get; private set; }\n        /// <summary>\n        /// \n        /// </summary>\n        public string FilePathName { get; private set; }\n\n        /*****************************************************************************************/\n        /** Privates **/\n\n\n        // The JSON file in JObject form\n        // For gathering the incoming data\n        object StringBuilderLock = new object();\n        // To prevent multiple same-file access\n        static object FileLock = new object();\n\n        /*****************************************************************************************/\n\n        /// <summary>\n        /// SIMPL+ default constructor.\n        /// </summary>\n        public JsonToSimplFileMaster()\n        {\n        }\n\n        /// <summary>\n        /// Read, evaluate and udpate status\n        /// </summary>\n        public void EvaluateFile(string filepath)\n        {\n            try\n            {\n                OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);\n\n                var dirSeparator = Path.DirectorySeparatorChar;\n                var dirSeparatorAlt = Path.AltDirectorySeparatorChar;\n\n                var series = CrestronEnvironment.ProgramCompatibility;\n\n                var is3Series = (eCrestronSeries.Series3 == (series & eCrestronSeries.Series3));\n                OnBoolChange(is3Series, 0,\n                    JsonToSimplConstants.ProgramCompatibility3SeriesChange);\n\n                var is4Series = (eCrestronSeries.Series4 == (series & eCrestronSeries.Series4));\n                OnBoolChange(is4Series, 0,\n                    JsonToSimplConstants.ProgramCompatibility4SeriesChange);\n\n                var isServer = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server;\n                OnBoolChange(isServer, 0,\n                    JsonToSimplConstants.DevicePlatformValueChange);\n\n                // get the roomID\n                var roomId = Crestron.SimplSharp.InitialParametersClass.RoomId;                \n                if (!string.IsNullOrEmpty(roomId))\n                {\n                    OnStringChange(roomId, 0, JsonToSimplConstants.RoomIdChange);\n                }\n\n                // get the roomName\n                var roomName = Crestron.SimplSharp.InitialParametersClass.RoomName;\n                if (!string.IsNullOrEmpty(roomName))\n                {\n                    OnStringChange(roomName, 0, JsonToSimplConstants.RoomNameChange);\n                }\n\n                var rootDirectory = Directory.GetApplicationRootDirectory();\n                OnStringChange(rootDirectory, 0, JsonToSimplConstants.RootDirectoryChange);                \n                \n                var splusPath = string.Empty;\n                if (Regex.IsMatch(filepath, @\"user\", RegexOptions.IgnoreCase))\n                {\n                    if (is4Series) \n                        splusPath = Regex.Replace(filepath, \"user\", \"user\", RegexOptions.IgnoreCase);\n                    else if (isServer)\n                        splusPath = Regex.Replace(filepath, \"user\", \"User\", RegexOptions.IgnoreCase);\n                    else\n                        splusPath = filepath;\n                }\n\n                filepath = splusPath.Replace(dirSeparatorAlt, dirSeparator);\n                \n                Filepath = string.Format(\"{1}{0}{2}\", dirSeparator, rootDirectory,\n                    filepath.TrimStart(dirSeparator, dirSeparatorAlt));\n                \n                OnStringChange(string.Format(\"Attempting to evaluate {0}\", Filepath), 0, JsonToSimplConstants.StringValueChange);\n\n                if (string.IsNullOrEmpty(Filepath))\n                {\n                    OnStringChange(string.Format(\"Cannot evaluate file. JSON file path not set\"), 0, JsonToSimplConstants.StringValueChange);\n                    CrestronConsole.PrintLine(\"Cannot evaluate file. JSON file path not set\");\n                    return;\n                }\n\n                // get file directory and name to search\n                var fileDirectory = Path.GetDirectoryName(Filepath);\n                var fileName = Path.GetFileName(Filepath);\n\n                OnStringChange(string.Format(\"Checking '{0}' for '{1}'\", fileDirectory, fileName), 0, JsonToSimplConstants.StringValueChange);\n                Debug.Console(1, \"Checking '{0}' for '{1}'\", fileDirectory, fileName);\n\n                if (Directory.Exists(fileDirectory))\n                {\n                    // get the directory info                    \n                    var directoryInfo = new DirectoryInfo(fileDirectory);                                       \n\n                    // get the file to be read\n                    var actualFile = directoryInfo.GetFiles(fileName).FirstOrDefault();                    \n                    if (actualFile == null)\n                    {\n                        var msg = string.Format(\"JSON file not found: {0}\", Filepath);\n                        OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);\n                        CrestronConsole.PrintLine(msg);\n                        ErrorLog.Error(msg);\n                        return;\n                    }\n\n                    // \\xSE\\xR\\PDT000-Template_Main_Config-Combined_DSP_v00.02.json\n                    // \\USER\\PDT000-Template_Main_Config-Combined_DSP_v00.02.json\n                    ActualFilePath = actualFile.FullName;                    \n                    OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);\n                    OnStringChange(string.Format(\"Actual JSON file is {0}\", ActualFilePath), 0, JsonToSimplConstants.StringValueChange);\n                    Debug.Console(1, \"Actual JSON file is {0}\", ActualFilePath);\n\n                    Filename = actualFile.Name;\n                    OnStringChange(Filename, 0, JsonToSimplConstants.FilenameResolvedChange);\n                    OnStringChange(string.Format(\"JSON Filename is {0}\", Filename), 0, JsonToSimplConstants.StringValueChange);\n                    Debug.Console(1, \"JSON Filename is {0}\", Filename);\n\n\n                    FilePathName = string.Format(@\"{0}{1}\", actualFile.DirectoryName, dirSeparator);\n                    OnStringChange(string.Format(@\"{0}\", actualFile.DirectoryName), 0, JsonToSimplConstants.FilePathResolvedChange);\n                    OnStringChange(string.Format(@\"JSON File Path is {0}\", actualFile.DirectoryName), 0, JsonToSimplConstants.StringValueChange);\n                    Debug.Console(1, \"JSON File Path is {0}\", FilePathName);                    \n\n                    var json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);\n\n                    JsonObject = JObject.Parse(json);\n                    foreach (var child in Children)\n                        child.ProcessAll();\n\n                    OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);\n                }\n                else\n                {\n                    OnStringChange(string.Format(\"'{0}' not found\", fileDirectory), 0, JsonToSimplConstants.StringValueChange);\n                    Debug.Console(1, \"'{0}' not found\", fileDirectory);\n                }\n            }\n            catch (Exception e)\n            {\n                var msg = string.Format(\"EvaluateFile Exception: Message\\r{0}\", e.Message);\n                OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);\n                CrestronConsole.PrintLine(msg);\n                ErrorLog.Error(msg);\n\n                var stackTrace = string.Format(\"EvaluateFile: Stack Trace\\r{0}\", e.StackTrace);\n                OnStringChange(stackTrace, 0, JsonToSimplConstants.StringValueChange);\n                CrestronConsole.PrintLine(stackTrace);\n                ErrorLog.Error(stackTrace);\n            }\n        }\n\n\n        /// <summary>\n        /// Sets the debug level\n        /// </summary>\n        /// <param name=\"level\"></param>\n        /// <summary>\n        /// setDebugLevel method\n        /// </summary>\n        public void setDebugLevel(uint level)\n        {\n            Debug.SetDebugLevel(level);\n        }\n\n        /// <summary>\n        /// Saves the values to the file\n        /// </summary>\n        public override void Save()\n        {\n            // this code is duplicated in the other masters!!!!!!!!!!!!!\n            UnsavedValues = new Dictionary<string, JValue>();\n            // Make each child update their values into master object\n            foreach (var child in Children)\n            {\n                Debug.Console(1, \"Master [{0}] checking child [{1}] for updates to save\", UniqueID, child.Key);\n                child.UpdateInputsForMaster();\n            }\n\n            if (UnsavedValues == null || UnsavedValues.Count == 0)\n            {\n                Debug.Console(1, \"Master [{0}] No updated values to save. Skipping\", UniqueID);\n                return;\n            }\n            lock (FileLock)\n            {\n                Debug.Console(1, \"Saving\");\n                foreach (var path in UnsavedValues.Keys)\n                {\n                    var tokenToReplace = JsonObject.SelectToken(path);\n                    if (tokenToReplace != null)\n                    {// It's found\n                        tokenToReplace.Replace(UnsavedValues[path]);\n                        Debug.Console(1, \"JSON Master[{0}] Updating '{1}'\", UniqueID, path);\n                    }\n                    else // No token.  Let's make one \n                    {\n                        //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net\n                        Debug.Console(1, \"JSON Master[{0}] Cannot write value onto missing property: '{1}'\", UniqueID, path);\n\n                        //                        JContainer jpart = JsonObject;\n                        //                        // walk down the path and find where it goes\n                        //#warning Does not handle arrays.\n                        //                        foreach (var part in path.Split('.'))\n                        //                        {\n\n                        //                            var openPos = part.IndexOf('[');\n                        //                            if (openPos > -1)\n                        //                            {\n                        //                                openPos++; // move to number\n                        //                                var closePos = part.IndexOf(']');\n                        //                                var arrayName = part.Substring(0, openPos - 1); // get the name\n                        //                                var index = Convert.ToInt32(part.Substring(openPos, closePos - openPos));\n\n                        //                                // Check if the array itself exists and add the item if so\n                        //                                if (jpart[arrayName] != null)\n                        //                                {\n                        //                                    var arrayObj = jpart[arrayName] as JArray;\n                        //                                    var item = arrayObj[index];\n                        //                                    if (item == null)\n                        //                                        arrayObj.Add(new JObject());\n                        //                                }\n\n                        //                                Debug.Console(0, \"IGNORING MISSING ARRAY VALUE FOR NOW\");\n                        //                                continue;\n                        //                            }\n                        //                            // Build the \n                        //                            if (jpart[part] == null)\n                        //                                jpart.Add(new JProperty(part, new JObject()));\n                        //                            jpart = jpart[part] as JContainer;\n                        //                        }\n                        //                        jpart.Replace(UnsavedValues[path]);\n                    }\n                }\n                using (StreamWriter sw = new StreamWriter(ActualFilePath))\n                {\n                    try\n                    {\n                        sw.Write(JsonObject.ToString());\n                        sw.Flush();\n                    }\n                    catch (Exception e)\n                    {\n                        string err = string.Format(\"Error writing JSON file:\\r{0}\", e);\n                        Debug.Console(0, err);\n                        ErrorLog.Warn(err);\n                        return;\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/JsonToSimplFixedPathObject.cs",
    "content": "﻿\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n /// <summary>\n /// Represents a JsonToSimplFixedPathObject\n /// </summary>\n\tpublic class JsonToSimplFixedPathObject : JsonToSimplChildObjectBase\n\t{\n        /// <summary>\n        /// Constructor\n        /// </summary>\n\t\tpublic JsonToSimplFixedPathObject()\n\t\t{\n\t\t\tthis.LinkedToObject = true;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/JsonToSimplGenericMaster.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n /// <summary>\n /// Represents a JsonToSimplGenericMaster\n /// </summary>\n\tpublic class JsonToSimplGenericMaster : JsonToSimplMaster\n    {\n\t\t/*****************************************************************************************/\n\t\t/** Privates **/\n\n        \n\t\t// The JSON file in JObject form\n\t\t// For gathering the incoming data\n\t\tobject StringBuilderLock = new object();\n\t\t// To prevent multiple same-file access\n\t\tstatic object WriteLock = new object();\n\n  /// <summary>\n  /// Gets or sets the SaveCallback\n  /// </summary>\n\t\tpublic Action<string> SaveCallback { get; set; }\n\n\t\t/*****************************************************************************************/\n\n\t\t/// <summary>\n        /// SIMPL+ default constructor.\n        /// </summary>\n\t\tpublic JsonToSimplGenericMaster()\n        {\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Loads in JSON and triggers evaluation on all children\n\t\t/// </summary>\n\t\t/// <param name=\"json\"></param>\n\t\tpublic void LoadWithJson(string json)\n\t\t{\n\t\t\tOnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);\n\t\t\ttry\n\t\t\t{\n\t\t\t\tJsonObject = JObject.Parse(json);\n\t\t\t\tforeach (var child in Children)\n\t\t\t\t\tchild.ProcessAll();\n\t\t\t\tOnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"JSON parsing failed:\\r{0}\", e);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\tErrorLog.Error(msg);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Loads JSON into JsonObject, but does not trigger evaluation by children\n\t\t/// </summary>\n\t\t/// <param name=\"json\"></param>\n  /// <summary>\n  /// SetJsonWithoutEvaluating method\n  /// </summary>\n\t\tpublic void SetJsonWithoutEvaluating(string json)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tJsonObject = JObject.Parse(json);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.Console(0, this, \"JSON parsing failed:\\r{0}\", e);\n\t\t\t}\n\t\t}\n\n  /// <summary>\n  /// Save method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic override void Save()\n\t\t{\n\t\t\t// this code is duplicated in the other masters!!!!!!!!!!!!!\n \t\t\tUnsavedValues = new Dictionary<string, JValue>();\n\t\t\t// Make each child update their values into master object\n\t\t\tforeach (var child in Children)\n\t\t\t{\n\t\t\t\tDebug.Console(1, this, \"Master. checking child [{0}] for updates to save\",  child.Key);\n\t\t\t\tchild.UpdateInputsForMaster();\n\t\t\t}\n\n\t\t\tif (UnsavedValues == null || UnsavedValues.Count == 0)\n\t\t\t{\n\t\t\t\tDebug.Console(1, this, \"Master. No updated values to save. Skipping\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlock (WriteLock)\n\t\t\t{\n\t\t\t\tDebug.Console(1, this, \"Saving\");\n\t\t\t\tforeach (var path in UnsavedValues.Keys)\n\t\t\t\t{\n\t\t\t\t\tvar tokenToReplace = JsonObject.SelectToken(path);\n\t\t\t\t\tif (tokenToReplace != null)\n\t\t\t\t\t{// It's found\n\t\t\t\t\t\ttokenToReplace.Replace(UnsavedValues[path]);\n\t\t\t\t\t\tDebug.Console(1, this, \"Master Updating '{0}'\", path);\n\t\t\t\t\t}\n\t\t\t\t\telse // No token.  Let's make one \n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.Console(1, \"Master Cannot write value onto missing property: '{0}'\", path);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (SaveCallback != null)\n\t\t\t\tSaveCallback(JsonObject.ToString());\n\t\t\telse\n\t\t\t\tDebug.Console(0, this, \"WARNING: No save callback defined.\");\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/JsonToSimplMaster.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n    /// <summary>\n    /// Abstract base class for JsonToSimpl interactions\n    /// </summary>\n\tpublic abstract class JsonToSimplMaster : IKeyed\n\t{\n        /// <summary>\n        /// Notifies of bool change\n        /// </summary>\n\t\tpublic event EventHandler<BoolChangeEventArgs> BoolChange;\n        /// <summary>\n        /// Notifies of ushort change\n        /// </summary>\n\t\tpublic event EventHandler<UshrtChangeEventArgs> UshrtChange;\n        /// <summary>\n        /// Notifies of string change\n        /// </summary>\n\t\tpublic event EventHandler<StringChangeEventArgs> StringChange;\n\n        /// <summary>\n        /// A collection of associated child modules\n        /// </summary>\n\t\tprotected List<JsonToSimplChildObjectBase> Children = new List<JsonToSimplChildObjectBase>();\n\n\t\t/*****************************************************************************************/\n\n\t\t/// <summary>\n\t\t/// Mirrors the Unique ID for now.\n\t\t/// </summary>\n\t\tpublic string Key { get { return UniqueID; } }\n\n  /// <summary>\n  /// Gets or sets the UniqueID\n  /// </summary>\n\t\tpublic string UniqueID { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Merely for use in debug messages\n\t\t/// </summary>\n\t\tpublic string DebugName\n\t\t{\n\t\t\tget { return _DebugName; }\n\t\t\tset { if (DebugName == null) _DebugName = \"\"; else _DebugName = value; }\n\t\t}\n\t\tstring _DebugName = \"\";\n\n  /// <summary>\n  /// Gets or sets the PathPrefix\n  /// </summary>\n\t\tpublic string PathPrefix { get; set; }\n\n\t\t/// <summary>\n\t\t/// This is added to the end of all paths\n\t\t/// </summary>\n\t\tpublic string PathSuffix { get; set; }\n\n\t\t/// <summary>\n\t\t/// Enables debugging output to the console.  Certain error messages will be logged to the \n\t\t/// system's error log regardless of this setting\n\t\t/// </summary>\n\t\tpublic bool DebugOn { get; set; }\n\n\t\t/// <summary>\n\t\t/// Ushort helper for Debug property\n\t\t/// </summary>\n\t\tpublic ushort UDebug\n\t\t{\n\t\t\tget { return (ushort)(DebugOn ? 1 : 0); }\n\t\t\tset\n\t\t\t{\n\t\t\t\tDebugOn = (value == 1);\n\t\t\t\tCrestronConsole.PrintLine(\"JsonToSimpl debug={0}\", DebugOn);\n\t\t\t}\n\t\t}\n\n  /// <summary>\n  /// Gets or sets the JsonObject\n  /// </summary>\n\t\tpublic JObject JsonObject { get; protected set; }\n\n\t\t/*****************************************************************************************/\n\t\t/** Privates **/\n\n\n\t\t// The JSON file in JObject form\n\t\t// For gathering the incoming data\n\t\tprotected Dictionary<string, JValue> UnsavedValues = new Dictionary<string, JValue>();\n\n\t\t/*****************************************************************************************/\n\n\t\t/// <summary>\n\t\t/// SIMPL+ default constructor.\n\t\t/// </summary>\n\t\tpublic JsonToSimplMaster()\n\t\t{\n\t\t}\n\n\n\t\t/// <summary>\n\t\t/// Sets up class - overriding methods should always call this.\n\t\t/// </summary>\n\t\t/// <param name=\"uniqueId\"></param>\n\t\tpublic virtual void Initialize(string uniqueId)\n\t\t{\n\t\t\tUniqueID = uniqueId;\n\t\t\tJ2SGlobal.AddMaster(this); // Should not re-add\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds a child \"module\" to this master\n\t\t/// </summary>\n\t\t/// <param name=\"child\"></param>\n  /// <summary>\n  /// AddChild method\n  /// </summary>\n\t\tpublic void AddChild(JsonToSimplChildObjectBase child)\n\t\t{\n\t\t\tif (!Children.Contains(child))\n\t\t\t{\n\t\t\t\tChildren.Add(child);\n\t\t\t}\n\t\t}\n\n  /// <summary>\n  /// AddUnsavedValue method\n  /// </summary>\n\t\tpublic void AddUnsavedValue(string path, JValue value)\n\t\t{\n\t\t\tif (UnsavedValues.ContainsKey(path))\n\t\t\t{\n\t\t\t\tDebug.Console(0, \"Master[{0}] WARNING - Attempt to add duplicate value for path '{1}'.\\r Ingoring. Please ensure that path does not exist on multiple modules.\", UniqueID, path);\n\t\t\t}\n\t\t\telse\n\t\t\t\tUnsavedValues.Add(path, value);\n\t\t\t//Debug.Console(0, \"Master[{0}] Unsaved size={1}\", UniqueID, UnsavedValues.Count);\n\t\t}\n\n        /// <summary>\n        /// Saves the file\n        /// </summary>\n\t\tpublic abstract void Save();\n\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\tpublic static class JsonFixes\n\t\t{\n            /// <summary>\n            /// Deserializes a string into a JObject\n            /// </summary>\n            /// <param name=\"json\"></param>\n            /// <returns></returns>\n\t\t\tpublic static JObject ParseObject(string json)\n\t\t\t{\n\t\t\t\t#if NET6_0\n                using (var reader = new JsonTextReader(new System.IO.StringReader(json)))\n#else\n                using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))\n#endif\n\t\t\t\t{\n\t\t\t\t\tvar startDepth = reader.Depth;\n\t\t\t\t\tvar obj = JObject.Load(reader);\n\t\t\t\t\tif (startDepth != reader.Depth)\n\t\t\t\t\t\tthrow new JsonSerializationException(\"Unenclosed json found\");\n\t\t\t\t\treturn obj;\n\t\t\t\t}\n\t\t\t}\n\n            /// <summary>\n            /// Deserializes a string into a JArray\n            /// </summary>\n            /// <param name=\"json\"></param>\n            /// <returns></returns>\n   /// <summary>\n   /// ParseArray method\n   /// </summary>\n\t\t\tpublic static JArray ParseArray(string json)\n\t\t\t{\n\t\t\t\t#if NET6_0\n                using (var reader = new JsonTextReader(new System.IO.StringReader(json)))\n#else\n                using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))\n#endif\n\t\t\t\t{\n\t\t\t\t\tvar startDepth = reader.Depth;\n\t\t\t\t\tvar obj = JArray.Load(reader);\n\t\t\t\t\tif (startDepth != reader.Depth)\n\t\t\t\t\t\tthrow new JsonSerializationException(\"Unenclosed json found\");\n\t\t\t\t\treturn obj;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Helper event\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnBoolChange(bool state, ushort index, ushort type)\n\t\t{\n\t\t\tif (BoolChange != null)\n\t\t\t{\n\t\t\t\tvar args = new BoolChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tBoolChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Helper event\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnUshrtChange(ushort state, ushort index, ushort type)\n\t\t{\n\t\t\tif (UshrtChange != null)\n\t\t\t{\n\t\t\t\tvar args = new UshrtChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tUshrtChange(this, args);\n\t\t\t}\n\t\t}\n\n        /// <summary>\n        /// Helper event\n        /// </summary>\n        /// <param name=\"value\"></param>\n        /// <param name=\"index\"></param>\n        /// <param name=\"type\"></param>\n\t\tprotected void OnStringChange(string value, ushort index, ushort type)\n\t\t{\n\t\t\tif (StringChange != null)\n\t\t\t{\n\t\t\t\tvar args = new StringChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tStringChange(this, args);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core.Config;\n\nnamespace PepperDash.Core.JsonToSimpl\n{\n    /// <summary>\n    /// Portal File Master\n    /// </summary>\n    public class JsonToSimplPortalFileMaster : JsonToSimplMaster\n    {\n\t\t/// <summary>\n\t\t///  Sets the filepath as well as registers this with the Global.Masters list\n\t\t/// </summary>\n\t\tpublic string PortalFilepath { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the ActualFilePath\n        /// </summary>\n        public string ActualFilePath { get; private set; }\n\n\t\t/*****************************************************************************************/\n\t\t/** Privates **/\n\n\t\t// To prevent multiple same-file access\n\t\tobject StringBuilderLock = new object();\n\t\tstatic object FileLock = new object();\n\n\t\t/*****************************************************************************************/\n\n\t\t/// <summary>\n        /// SIMPL+ default constructor.\n        /// </summary>\n\t\tpublic JsonToSimplPortalFileMaster()\n        {\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Read, evaluate and udpate status\n\t\t/// </summary>\n\t\tpublic void EvaluateFile(string portalFilepath)\n\t\t{\n\t\t\tPortalFilepath = portalFilepath;\n\n\t\t\tOnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);\n\t\t\tif (string.IsNullOrEmpty(PortalFilepath))\n\t\t\t{\n\t\t\t\tCrestronConsole.PrintLine(\"Cannot evaluate file. JSON file path not set\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Resolve possible wildcarded filename\n\n\t\t\t// If the portal file is xyz.json, then \n\t\t\t// the file we want to check for first will be called xyz.local.json\n\t\t\tvar localFilepath = Path.ChangeExtension(PortalFilepath, \"local.json\");\n\t\t\tDebug.Console(0, this, \"Checking for local file {0}\", localFilepath);\n\t\t\tvar actualLocalFile = GetActualFileInfoFromPath(localFilepath);\n\n\t\t\tif (actualLocalFile != null)\n\t\t\t{\n\t\t\t\tActualFilePath = actualLocalFile.FullName;\n                OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);\n\t\t\t}\n\t\t\t// If the local file does not exist, then read the portal file xyz.json\n\t\t\t// and create the local.\n\t\t\telse\n\t\t\t{\n\t\t\t\tDebug.Console(1, this, \"Local JSON file not found {0}\\rLoading portal JSON file\", localFilepath);\n\t\t\t\tvar actualPortalFile = GetActualFileInfoFromPath(portalFilepath);\n\t\t\t\tif (actualPortalFile != null)\n\t\t\t\t{\n\t\t\t\t\tvar newLocalPath = Path.ChangeExtension(actualPortalFile.FullName, \"local.json\");\n\t\t\t\t\t// got the portal file, hand off to the merge / save method\t\t\t\t\n\t\t\t\t\tPortalConfigReader.ReadAndMergeFileIfNecessary(actualPortalFile.FullName, newLocalPath);\n\t\t\t\t\tActualFilePath = newLocalPath;\n                    OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvar msg = string.Format(\"Portal JSON file not found: {0}\", PortalFilepath);\n\t\t\t\t\tDebug.Console(1, this, msg);\n\t\t\t\t\tErrorLog.Error(msg);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// At this point we should have a local file.  Do it.\n\t\t\tDebug.Console(1, \"Reading local JSON file {0}\", ActualFilePath);\n\n\t\t\tstring json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tJsonObject = JObject.Parse(json);\n\t\t\t\tforeach (var child in Children)\n\t\t\t\t\tchild.ProcessAll();\n\t\t\t\tOnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"JSON parsing failed:\\r{0}\", e);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\tErrorLog.Error(msg);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns the FileInfo object for a given path, with possible wildcards\n\t\t/// </summary>\n\t\t/// <param name=\"path\"></param>\n\t\t/// <returns></returns>\n\t\tFileInfo GetActualFileInfoFromPath(string path)\n\t\t{\n\t\t\tvar dir = Path.GetDirectoryName(path);\n\t\t\tvar localFilename = Path.GetFileName(path);\n\t\t\tvar directory = new DirectoryInfo(dir);\n\t\t\t// search the directory for the file w/ wildcards\n\t\t\treturn directory.GetFiles(localFilename).FirstOrDefault();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"level\"></param>\n  /// <summary>\n  /// setDebugLevel method\n  /// </summary>\n\t\tpublic void setDebugLevel(uint level)\n\t\t{\n\t\t\tDebug.SetDebugLevel(level);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\tpublic override void Save()\n\t\t{\n\t\t\t// this code is duplicated in the other masters!!!!!!!!!!!!!\n\t\t\tUnsavedValues = new Dictionary<string, JValue>();\n\t\t\t// Make each child update their values into master object\n\t\t\tforeach (var child in Children)\n\t\t\t{\n\t\t\t\tDebug.Console(1, \"Master [{0}] checking child [{1}] for updates to save\", UniqueID, child.Key);\n\t\t\t\tchild.UpdateInputsForMaster();\n\t\t\t}\n\n\t\t\tif (UnsavedValues == null || UnsavedValues.Count == 0)\n\t\t\t{\n\t\t\t\tDebug.Console(1, \"Master [{0}] No updated values to save. Skipping\", UniqueID);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlock (FileLock)\n\t\t\t{\n\t\t\t\tDebug.Console(1, \"Saving\");\n\t\t\t\tforeach (var path in UnsavedValues.Keys)\n\t\t\t\t{\n\t\t\t\t\tvar tokenToReplace = JsonObject.SelectToken(path);\n\t\t\t\t\tif (tokenToReplace != null)\n\t\t\t\t\t{// It's found\n\t\t\t\t\t\ttokenToReplace.Replace(UnsavedValues[path]);\n\t\t\t\t\t\tDebug.Console(1, \"JSON Master[{0}] Updating '{1}'\", UniqueID, path);\n\t\t\t\t\t}\n\t\t\t\t\telse // No token.  Let's make one \n\t\t\t\t\t{\n\t\t\t\t\t\t//http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net\n\t\t\t\t\t\tDebug.Console(1, \"JSON Master[{0}] Cannot write value onto missing property: '{1}'\", UniqueID, path);\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tusing (StreamWriter sw = new StreamWriter(ActualFilePath))\n\t\t\t\t{\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tsw.Write(JsonObject.ToString());\n\t\t\t\t\t\tsw.Flush();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e)\n\t\t\t\t\t{\n\t\t\t\t\t\tstring err = string.Format(\"Error writing JSON file:\\r{0}\", e);\n\t\t\t\t\t\tDebug.Console(0, err);\n\t\t\t\t\t\tErrorLog.Warn(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Logging/CrestronEnricher.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Serilog.Core;\nusing Serilog.Events;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Core.Logging\n{\n    /// <summary>\n    /// Represents a CrestronEnricher\n    /// </summary>\n    public class CrestronEnricher : ILogEventEnricher\n    {\n        static readonly string _appName;\n\n        static CrestronEnricher()\n        {\n            switch (CrestronEnvironment.DevicePlatform)\n            {\n                case eDevicePlatform.Appliance:\n                    _appName = $\"App {InitialParametersClass.ApplicationNumber}\";\n                    break;\n                case eDevicePlatform.Server:\n                    _appName = $\"{InitialParametersClass.RoomId}\";\n                    break;\n            }\n        }\n            \n\n        /// <summary>\n        /// Enrich method\n        /// </summary>\n        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)\n        {\n            var property = propertyFactory.CreateProperty(\"App\", _appName);\n\n            logEvent.AddOrUpdateProperty(property);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Logging/Debug.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronDataStore;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharp.CrestronLogger;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Logging;\nusing Serilog;\nusing Serilog.Context;\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Formatting.Compact;\nusing Serilog.Formatting.Json;\nusing Serilog.Templates;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// </summary>\n    public static class Debug\n    {\n        private static readonly string LevelStoreKey = \"ConsoleDebugLevel\";\n        private static readonly string WebSocketLevelStoreKey = \"WebsocketDebugLevel\";\n        private static readonly string ErrorLogLevelStoreKey = \"ErrorLogDebugLevel\";\n        private static readonly string FileLevelStoreKey = \"FileDebugLevel\";\n\n        private static readonly Dictionary<uint, LogEventLevel> _logLevels = new Dictionary<uint, LogEventLevel>()\n        {\n            {0, LogEventLevel.Information },\n            {3, LogEventLevel.Warning },\n            {4, LogEventLevel.Error },\n            {5, LogEventLevel.Fatal },\n            {1, LogEventLevel.Debug },\n            {2, LogEventLevel.Verbose },\n        };\n\n        private static ILogger _logger;\n\n        private static readonly LoggingLevelSwitch _consoleLogLevelSwitch;\n\n        private static readonly LoggingLevelSwitch _websocketLogLevelSwitch;\n\n        private static readonly LoggingLevelSwitch _errorLogLevelSwitch;\n\n        private static readonly LoggingLevelSwitch _fileLogLevelSwitch;\n\n        /// <summary>\n        /// Gets the minimum log level for the websocket sink.\n        /// </summary>\n        public static LogEventLevel WebsocketMinimumLogLevel\n        {\n            get { return _websocketLogLevelSwitch.MinimumLevel; }\n        }\n\n        private static readonly DebugWebsocketSink _websocketSink;\n\n        /// <summary>\n        /// Gets the websocket sink for debug logging.\n        /// </summary>\n        public static DebugWebsocketSink WebsocketSink\n        {\n            get { return _websocketSink; }\n        }\n\n        /// <summary>\n        /// Describes the folder location where a given program stores it's debug level memory. By default, the\n        /// file written will be named appNdebug where N is 1-10.\n        /// </summary>\n        public static string OldFilePathPrefix = @\"\\nvram\\debug\\\";\n\n        /// <summary>\n        /// Describes the new folder location where a given program stores it's debug level memory. By default, the\n        /// file written will be named appNdebug where N is 1-10.\n        /// </summary>\n        public static string NewFilePathPrefix = @\"\\user\\debug\\\";\n\n        /// <summary>\n        /// The name of the file containing the current debug settings.\n        /// </summary>\n        public static string FileName = string.Format(@\"app{0}Debug.json\", InitialParametersClass.ApplicationNumber);\n\n        /// <summary>\n        /// Gets or sets the Level\n        /// </summary>\n        public static int Level { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the DoNotLoadConfigOnNextBoot\n        /// </summary>\n        public static bool DoNotLoadConfigOnNextBoot { get; private set; }\n\n        private static DebugContextCollection _contexts;\n\n        private const int SaveTimeoutMs = 30000;\n\n        /// <summary>\n        /// Indicates whether the system is running on an appliance.\n        /// </summary>\n        public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance;\n\n        /// <summary>\n        /// Gets or sets the PepperDashCoreVersion\n        /// </summary>\n        public static string PepperDashCoreVersion { get; private set; }\n\n        private static CTimer _saveTimer;\n\n        /// <summary>\n        /// When true, the IncludedExcludedKeys dict will contain keys to include. \n        /// When false (default), IncludedExcludedKeys will contain keys to exclude.\n        /// </summary>\n        private static bool _excludeAllMode;\n\n        private static readonly Dictionary<string, object> IncludedExcludedKeys;\n\n        private static readonly LoggerConfiguration _defaultLoggerConfiguration;\n\n        private static LoggerConfiguration _loggerConfiguration;\n\n        /// <summary>\n        /// Gets the logger configuration for the debug logging.\n        /// </summary>\n        public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;\n\n        static Debug()\n        {\n            CrestronDataStoreStatic.InitCrestronDataStore();\n\n            var defaultConsoleLevel = GetStoredLogEventLevel(LevelStoreKey);\n\n            var defaultWebsocketLevel = GetStoredLogEventLevel(WebSocketLevelStoreKey);\n\n            var defaultErrorLogLevel = GetStoredLogEventLevel(ErrorLogLevelStoreKey);\n\n            var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);\n\n            _consoleLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);\n\n            _websocketLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);\n\n            _errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);\n\n            _fileLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);\n\n            _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));\n\n            var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ?\n                $@\"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log\" :\n                $@\"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log\";\n\n            CrestronConsole.PrintLine($\"Saving log files to {logFilePath}\");\n\n            var errorLogTemplate = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance\n                ? \"{@t:fff}ms [{@l:u4}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\\r\\n{@x}{#end}\"\n                : \"[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\\r\\n{@x}{#end}\";\n\n            _defaultLoggerConfiguration = new LoggerConfiguration()\n                .MinimumLevel.Verbose()\n                .Enrich.FromLogContext()\n                .Enrich.With(new CrestronEnricher())\n                .WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate(\"[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\\r\\n{@x}{#end}\")), levelSwitch: _consoleLogLevelSwitch)\n                .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLogLevelSwitch)\n                .WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch)\n                .WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,\n                    rollingInterval: RollingInterval.Day,\n                    restrictedToMinimumLevel: LogEventLevel.Debug,\n                    retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 7 : 14,\n                    levelSwitch: _fileLogLevelSwitch\n                );\n\n            try\n            {\n                if (InitialParametersClass.NumberOfRemovableDrives > 0)\n                {\n                    CrestronConsole.PrintLine(\"{0} RM Drive(s) Present. Initializing CrestronLogger\", InitialParametersClass.NumberOfRemovableDrives);\n                    _defaultLoggerConfiguration.WriteTo.Sink(new DebugCrestronLoggerSink());\n                }\n                else\n                    CrestronConsole.PrintLine(\"No RM Drive(s) Present. Not using Crestron Logger\");\n            }\n            catch (Exception e)\n            {\n                CrestronConsole.PrintLine(\"Initializing of CrestronLogger failed: {0}\", e);\n            }\n\n            // Instantiate the root logger\n            _loggerConfiguration = _defaultLoggerConfiguration;\n\n            _logger = _loggerConfiguration.CreateLogger();\n            // Get the assembly version and print it to console and the log\n            GetVersion();\n\n            string msg = $\"[App {InitialParametersClass.ApplicationNumber}] Using PepperDash_Core v{PepperDashCoreVersion}\";\n\n            if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)\n            {\n                msg = $\"[Room {InitialParametersClass.RoomId}] Using PepperDash_Core v{PepperDashCoreVersion}\";\n            }\n\n            CrestronConsole.PrintLine(msg);\n\n            LogMessage(LogEventLevel.Information, msg);\n\n            IncludedExcludedKeys = new Dictionary<string, object>();\n\n            if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)\n            {\n                // Add command to console\n                CrestronConsole.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, \"donotloadonnextboot\",\n                    \"donotloadonnextboot:P [true/false]: Should the application load on next boot\", ConsoleAccessLevelEnum.AccessOperator);\n\n                CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, \"appdebug\",\n                    \"appdebug:P [0-5]: Sets the application's console debug message level\",\n                    ConsoleAccessLevelEnum.AccessOperator);\n                CrestronConsole.AddNewConsoleCommand(ShowDebugLog, \"appdebuglog\",\n                    \"appdebuglog:P [all] Use \\\"all\\\" for full log.\",\n                    ConsoleAccessLevelEnum.AccessOperator);\n                CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), \"appdebugclear\",\n                    \"appdebugclear:P Clears the current custom log\",\n                    ConsoleAccessLevelEnum.AccessOperator);\n                CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, \"appdebugfilter\",\n                    \"appdebugfilter [params]\", ConsoleAccessLevelEnum.AccessOperator);\n            }\n\n            CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;\n\n            LoadMemory();\n\n            var context = _contexts.GetOrCreateItem(\"DEFAULT\");\n            Level = context.Level;\n            DoNotLoadConfigOnNextBoot = context.DoNotLoadOnNextBoot;\n\n            if (DoNotLoadConfigOnNextBoot)\n                CrestronConsole.PrintLine(string.Format(\"Program {0} will not load config after next boot.  Use console command go:{0} to load the config manually\", InitialParametersClass.ApplicationNumber));\n\n            _errorLogLevelSwitch.MinimumLevelChanged += (sender, args) =>\n            {\n                LogMessage(LogEventLevel.Information, \"Error log debug level set to {minimumLevel}\", _errorLogLevelSwitch.MinimumLevel);\n            };\n\n            // Set initial error log level based on platform && stored level. If appliance, use stored level, otherwise default to verbose\n            SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? _errorLogLevelSwitch.MinimumLevel : LogEventLevel.Verbose);\n        }\n\n        /// <summary>\n        /// UpdateLoggerConfiguration method\n        /// </summary>\n        public static void UpdateLoggerConfiguration(LoggerConfiguration config)\n        {\n            _loggerConfiguration = config;\n\n            _logger = config.CreateLogger();\n        }\n\n        /// <summary>\n        /// ResetLoggerConfiguration method\n        /// </summary>\n        public static void ResetLoggerConfiguration()\n        {\n            _loggerConfiguration = _defaultLoggerConfiguration;\n\n            _logger = _loggerConfiguration.CreateLogger();\n        }\n\n        private static LogEventLevel GetStoredLogEventLevel(string levelStoreKey)\n        {\n            try\n            {\n                var result = CrestronDataStoreStatic.GetLocalIntValue(levelStoreKey, out int logLevel);\n\n                if (result != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                {\n                    CrestronConsole.Print($\"Unable to retrieve stored log level for {levelStoreKey}.\\r\\nError: {result}.\\r\\nSetting level to {LogEventLevel.Information}\\r\\n\");\n\n                    CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, levelStoreKey == ErrorLogLevelStoreKey ? (int)LogEventLevel.Warning : (int)LogEventLevel.Information);\n\n                    return levelStoreKey == ErrorLogLevelStoreKey ? LogEventLevel.Warning : LogEventLevel.Information;\n                }\n\n                if (logLevel < 0 || logLevel > 5)\n                {\n                    CrestronConsole.PrintLine($\"Stored Log level not valid for {levelStoreKey}: {logLevel}. Setting level to {LogEventLevel.Information}\");\n                    return LogEventLevel.Information;\n                }\n\n                CrestronConsole.PrintLine($\"Stored log level for {levelStoreKey} is {logLevel}\");\n\n                return (LogEventLevel)logLevel;\n            }\n            catch (Exception ex)\n            {\n                CrestronConsole.PrintLine($\"Exception retrieving log level for {levelStoreKey}: {ex.Message}\");\n                return LogEventLevel.Information;\n            }\n        }\n\n        private static void GetVersion()\n        {\n            var assembly = Assembly.GetExecutingAssembly();\n            var ver =\n                assembly\n                    .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);\n\n            if (ver != null && ver.Length > 0)\n            {\n                if (ver[0] is AssemblyInformationalVersionAttribute verAttribute)\n                {\n                    PepperDashCoreVersion = verAttribute.InformationalVersion;\n                }\n            }\n            else\n            {\n                var version = assembly.GetName().Version;\n                PepperDashCoreVersion = version.ToString();\n            }\n        }\n\n        /// <summary>\n        /// Used to save memory when shutting down\n        /// </summary>\n        /// <param name=\"programEventType\"></param>\n        static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n\n            if (programEventType == eProgramStatusEventType.Stopping)\n            {\n                Log.CloseAndFlush();\n\n                if (_saveTimer != null)\n                {\n                    _saveTimer.Stop();\n                    _saveTimer = null;\n                }\n                LogMessage(LogEventLevel.Information, \"Saving debug settings\");\n                SaveMemory();\n            }\n        }\n\n        /// <summary>\n        /// Callback for console command\n        /// </summary>\n        /// <param name=\"levelString\"></param>\n        /// <summary>\n        /// SetDebugFromConsole method\n        /// </summary>\n        public static void SetDebugFromConsole(string levelString)\n        {\n            try\n            {\n                if (levelString.Trim() == \"?\")\n                {\n                    CrestronConsole.ConsoleCommandResponse(\n                    \"Used to set the minimum level of debug messages:\\r\\n\" +\n                    \"Usage: appdebug:P [sink] [level]\\r\\n\" +\n                    \"  sink: console (default), errorlog, file, all\\r\\n\" +\n                    \"  all: sets all sinks to the specified level\\r\\n\" +\n                    \"  level: 0-5 or LogEventLevel name\\r\\n\" +\n                    $\"{_logLevels[0]} = 0\\r\\n\" +\n                    $\"{_logLevels[1]} = 1\\r\\n\" +\n                    $\"{_logLevels[2]} = 2\\r\\n\" +\n                    $\"{_logLevels[3]} = 3\\r\\n\" +\n                    $\"{_logLevels[4]} = 4\\r\\n\" +\n                    $\"{_logLevels[5]} = 5\");\n                    return;\n                }\n\n                if (string.IsNullOrEmpty(levelString.Trim()))\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"Console log level = {0}\\r\\n\", _consoleLogLevelSwitch.MinimumLevel);\n                    CrestronConsole.ConsoleCommandResponse(\"File log level = {0}\\r\\n\", _fileLogLevelSwitch.MinimumLevel);\n                    CrestronConsole.ConsoleCommandResponse(\"Error log level = {0}\\r\\n\", _errorLogLevelSwitch.MinimumLevel);\n                    return;\n                }\n\n                // Parse tokens: first token is sink (defaults to console), second token is level\n                var tokens = levelString.Trim().Split(new char[] { ' ', '\\t' }, StringSplitOptions.RemoveEmptyEntries);\n\n                string sinkName;\n                string levelToken;\n\n                if (tokens.Length == 1)\n                {\n                    // Single token - assume it's a level for console sink\n                    sinkName = \"console\";\n                    levelToken = tokens[0];\n                }\n                else if (tokens.Length == 2)\n                {\n                    // Two tokens - first is sink, second is level\n                    sinkName = tokens[0].ToLowerInvariant();\n                    levelToken = tokens[1];\n                }\n                else\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"Usage: appdebug:P [sink] [level]\");\n                    return;\n                }\n\n                // Parse the level using the same logic as before\n                LogEventLevel level;\n\n                if (int.TryParse(levelToken, out var levelInt))\n                {\n                    if (levelInt < 0 || levelInt > 5)\n                    {\n                        CrestronConsole.ConsoleCommandResponse($\"Error: Unable to parse {levelToken} to valid log level. If using a number, value must be between 0-5\");\n                        return;\n                    }\n\n                    if (!_logLevels.TryGetValue((uint)levelInt, out level))\n                    {\n                        level = LogEventLevel.Information;\n                        CrestronConsole.ConsoleCommandResponse($\"{levelInt} not valid. Setting level to {level}\");\n                    }\n                }\n                else if (Enum.TryParse(levelToken, true, out level))\n                {\n                    // Successfully parsed as LogEventLevel enum\n                }\n                else\n                {\n                    CrestronConsole.ConsoleCommandResponse($\"Error: Unable to parse {levelToken} to valid log level\");\n                    return;\n                }\n\n                // Set the level for the specified sink\n                switch (sinkName)\n                {\n                    case \"console\":\n                        SetDebugLevel(level);\n                        break;\n                    case \"errorlog\":\n                        SetErrorLogMinimumDebugLevel(level);\n                        break;\n                    case \"file\":\n                        SetFileMinimumDebugLevel(level);\n                        break;\n                    case \"all\":\n                        SetDebugLevel(level);\n                        SetErrorLogMinimumDebugLevel(level);\n                        SetFileMinimumDebugLevel(level);\n                        break;\n                    default:\n                        CrestronConsole.ConsoleCommandResponse($\"Error: Unknown sink '{sinkName}'. Valid sinks: console, errorlog, file\");\n                        break;\n                }\n            }\n            catch\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Usage: appdebug:P [sink] [level]\");\n            }\n        }\n\n        /// <summary>\n        /// Sets the debug level\n        /// </summary>\n        /// <param name=\"level\"> Valid values 0-5</param>\n        /// <summary>\n        /// SetDebugLevel method\n        /// </summary>\n        public static void SetDebugLevel(uint level)\n        {\n            if (!_logLevels.TryGetValue(level, out var logLevel))\n            {\n                logLevel = LogEventLevel.Information;\n\n                CrestronConsole.ConsoleCommandResponse($\"{level} not valid. Setting level to {logLevel}\");\n\n                SetDebugLevel(logLevel);\n            }\n\n            SetDebugLevel(logLevel);\n        }\n\n        /// <summary>\n        /// SetDebugLevel method\n        /// </summary>\n        public static void SetDebugLevel(LogEventLevel level)\n        {\n            _consoleLogLevelSwitch.MinimumLevel = level;\n\n            CrestronConsole.ConsoleCommandResponse(\"[Application {0}] Debug level set to {1}\\r\\n\",\n                InitialParametersClass.ApplicationNumber, _consoleLogLevelSwitch.MinimumLevel);\n\n            CrestronConsole.ConsoleCommandResponse($\"Storing level {level}:{(int)level}\");\n\n            var err = CrestronDataStoreStatic.SetLocalIntValue(LevelStoreKey, (int)level);\n\n            CrestronConsole.ConsoleCommandResponse($\"Store result: {err}:{(int)level}\");\n\n            if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                CrestronConsole.PrintLine($\"Error saving console debug level setting: {err}\");\n        }\n\n        /// <summary>\n        /// SetWebSocketMinimumDebugLevel method\n        /// </summary>\n        public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)\n        {\n            _websocketLogLevelSwitch.MinimumLevel = level;\n\n            var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level);\n\n            if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                LogMessage(LogEventLevel.Information, \"Error saving websocket debug level setting: {erro}\", err);\n\n            LogMessage(LogEventLevel.Information, \"Websocket debug level set to {0}\", _websocketLogLevelSwitch.MinimumLevel);\n        }\n\n        /// <summary>\n        /// SetErrorLogMinimumDebugLevel method\n        /// </summary>\n        public static void SetErrorLogMinimumDebugLevel(LogEventLevel level)\n        {\n            _errorLogLevelSwitch.MinimumLevel = level;\n\n            CrestronConsole.ConsoleCommandResponse(\"[Application {0}] Error log level set to {1}\\r\\n\",\n                InitialParametersClass.ApplicationNumber, _errorLogLevelSwitch.MinimumLevel);\n\n            CrestronConsole.ConsoleCommandResponse($\"Storing level {level}:{(int)level}\");\n\n            var err = CrestronDataStoreStatic.SetLocalIntValue(ErrorLogLevelStoreKey, (int)level);\n\n            CrestronConsole.ConsoleCommandResponse($\"Store result: {err}:{(int)level}\");\n\n            if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                CrestronConsole.PrintLine($\"Error saving error log debug level setting: {err}\");\n        }\n\n        /// <summary>\n        /// SetFileMinimumDebugLevel method\n        /// </summary>\n        public static void SetFileMinimumDebugLevel(LogEventLevel level)\n        {\n            _fileLogLevelSwitch.MinimumLevel = level;\n\n            CrestronConsole.ConsoleCommandResponse(\"[Application {0}] File log level set to {1}\\r\\n\",\n                InitialParametersClass.ApplicationNumber, _fileLogLevelSwitch.MinimumLevel);\n\n            CrestronConsole.ConsoleCommandResponse($\"Storing level {level}:{(int)level}\");\n\n            var err = CrestronDataStoreStatic.SetLocalIntValue(FileLevelStoreKey, (int)level);\n\n            CrestronConsole.ConsoleCommandResponse($\"Store result: {err}:{(int)level}\");\n\n            if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                CrestronConsole.PrintLine($\"Error saving file debug level setting: {err}\");\n        }\n\n        /// <summary>\n        /// Callback for console command\n        /// </summary>\n        /// <param name=\"stateString\"></param>\n        /// <summary>\n        /// SetDoNotLoadOnNextBootFromConsole method\n        /// </summary>\n        public static void SetDoNotLoadOnNextBootFromConsole(string stateString)\n        {\n            try\n            {\n                if (string.IsNullOrEmpty(stateString.Trim()))\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"DoNotLoadOnNextBoot = {0}\", DoNotLoadConfigOnNextBoot);\n                    return;\n                }\n\n                SetDoNotLoadConfigOnNextBoot(bool.Parse(stateString));\n            }\n            catch\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Usage: donotloadonnextboot:P [true/false]\");\n            }\n        }\n\n        /// <summary>\n        /// Callback for console command\n        /// </summary>\n        /// <param name=\"items\"></param>\n        /// <summary>\n        /// SetDebugFilterFromConsole method\n        /// </summary>\n        public static void SetDebugFilterFromConsole(string items)\n        {\n            var str = items.Trim();\n            if (str == \"?\")\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Usage:\\r APPDEBUGFILTER key1 key2 key3....\\r \" +\n                    \"+all: at beginning puts filter into 'default include' mode\\r\" +\n                    \"      All keys that follow will be excluded from output.\\r\" +\n                    \"-all: at beginning puts filter into 'default exclude all' mode.\\r\" +\n                    \"      All keys that follow will be the only keys that are shown\\r\" +\n                    \"+nokey: Enables messages with no key (default)\\r\" +\n                    \"-nokey: Disables messages with no key.\\r\" +\n                    \"(nokey settings are independent of all other settings)\");\n                return;\n            }\n            var keys = Regex.Split(str, @\"\\s*\");\n            foreach (var keyToken in keys)\n            {\n                var lkey = keyToken.ToLower();\n                if (lkey == \"+all\")\n                {\n                    IncludedExcludedKeys.Clear();\n                    _excludeAllMode = false;\n                }\n                else if (lkey == \"-all\")\n                {\n                    IncludedExcludedKeys.Clear();\n                    _excludeAllMode = true;\n                }\n                //else if (lkey == \"+nokey\")\n                //{\n                //    ExcludeNoKeyMessages = false;\n                //}\n                //else if (lkey == \"-nokey\")\n                //{\n                //    ExcludeNoKeyMessages = true;\n                //}\n                else\n                {\n                    string key;\n                    if (lkey.StartsWith(\"-\"))\n                    {\n                        key = lkey.Substring(1);\n                        // if in exclude all mode, we need to remove this from the inclusions\n                        if (_excludeAllMode)\n                        {\n                            if (IncludedExcludedKeys.ContainsKey(key))\n                                IncludedExcludedKeys.Remove(key);\n                        }\n                        // otherwise include all mode, add to the exclusions\n                        else\n                        {\n                            IncludedExcludedKeys[key] = new object();\n                        }\n                    }\n                    else if (lkey.StartsWith(\"+\"))\n                    {\n                        key = lkey.Substring(1);\n                        // if in exclude all mode, we need to add this as inclusion\n                        if (_excludeAllMode)\n                        {\n\n                            IncludedExcludedKeys[key] = new object();\n                        }\n                        // otherwise include all mode, remove this from exclusions\n                        else\n                        {\n                            if (IncludedExcludedKeys.ContainsKey(key))\n                                IncludedExcludedKeys.Remove(key);\n                        }\n                    }\n                }\n            }\n        }\n\n\n\n\n        /// <summary>\n        /// sets the settings for a device or creates a new entry\n        /// </summary>\n        /// <param name=\"deviceKey\"></param>\n        /// <param name=\"settings\"></param>\n        /// <returns></returns>\n        public static void SetDeviceDebugSettings(string deviceKey, object settings)\n        {\n            _contexts.SetDebugSettingsForKey(deviceKey, settings);\n            SaveMemoryOnTimeout();\n        }\n\n        /// <summary>\n        /// Gets the device settings for a device by key or returns null\n        /// </summary>\n        /// <param name=\"deviceKey\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetDeviceDebugSettingsForKey method\n        /// </summary>\n        public static object GetDeviceDebugSettingsForKey(string deviceKey)\n        {\n            return _contexts.GetDebugSettingsForKey(deviceKey);\n        }\n\n        /// <summary>\n        /// Sets the flag to prevent application starting on next boot\n        /// </summary>\n        /// <param name=\"state\"></param>\n        public static void SetDoNotLoadConfigOnNextBoot(bool state)\n        {\n            DoNotLoadConfigOnNextBoot = state;\n            _contexts.GetOrCreateItem(\"DEFAULT\").DoNotLoadOnNextBoot = state;\n            SaveMemoryOnTimeout();\n\n            CrestronConsole.ConsoleCommandResponse(\"[Application {0}], Do Not Load Config on Next Boot set to {1}\",\n                InitialParametersClass.ApplicationNumber, DoNotLoadConfigOnNextBoot);\n        }\n\n        /// <summary>\n        /// ShowDebugLog method\n        /// </summary>\n        public static void ShowDebugLog(string s)\n        {\n            var loglist = CrestronLogger.PrintTheLog(s.ToLower() == \"all\");\n            foreach (var l in loglist)\n                CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);\n        }\n\n        /// <summary>\n        /// Log an Exception as an Error\n        /// </summary>\n        /// <param name=\"ex\">Exception to log</param>\n        /// <param name=\"message\">Message template</param>\n        /// <param name=\"device\">Optional IKeyed device. If provided, the Key of the device will be added to the log message</param>\n        /// <param name=\"args\">Args to put into message template</param>\n        /// <summary>\n        /// LogMessage method\n        /// </summary>\n        public static void LogMessage(Exception ex, string message, IKeyed device = null, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", device?.Key))\n            {\n                _logger.Error(ex, message, args);\n            }\n        }\n\n        /// <summary>\n        /// Log a message\n        /// </summary>\n        /// <param name=\"level\">Level to log at</param>\n        /// <param name=\"message\">Message template</param>\n        /// <param name=\"device\">Optional IKeyed device. If provided, the Key of the device will be added to the log message</param>\n        /// <param name=\"args\">Args to put into message template</param>\n        public static void LogMessage(LogEventLevel level, string message, IKeyed device = null, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", device?.Key))\n            {\n                _logger.Write(level, message, args);\n            }\n        }\n\n        /// <summary>\n        /// Logs a message at the specified log level.\n        /// </summary>\n        /// <param name=\"level\">Level to log at</param>\n        /// <param name=\"message\">Message template</param>\n        /// <param name=\"args\">Args to put into message template</param>\n        public static void LogMessage(LogEventLevel level, string message, params object[] args)\n        {\n            _logger.Write(level, message, args);\n        }\n\n        /// <summary>\n        /// LogMessage method\n        /// </summary>\n        public static void LogMessage(LogEventLevel level, Exception ex, string message, params object[] args)\n        {\n            _logger.Write(level, ex, message, args);\n        }\n\n        /// <summary>\n        /// LogMessage method\n        /// </summary>\n        public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args)\n        {\n            LogMessage(level, message, keyed, args);\n        }\n\n        /// <summary>\n        /// LogMessage method\n        /// </summary>\n        public static void LogMessage(LogEventLevel level, Exception ex, IKeyed device, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", device?.Key))\n            {\n                _logger.Write(level, ex, message, args);\n            }\n        }\n\n        #region Explicit methods for logging levels\n        /// <summary>\n        /// LogVerbose method\n        /// </summary>\n        public static void LogVerbose(IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Verbose, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogVerbose method\n        /// </summary>\n        public static void LogVerbose(Exception ex, IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Verbose, ex, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogVerbose method\n        /// </summary>\n        public static void LogVerbose(string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Verbose, message, args);\n        }\n\n        /// <summary>\n        /// LogVerbose method\n        /// </summary>\n        public static void LogVerbose(Exception ex, string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Verbose, ex, message, args);\n        }\n\n        /// <summary>\n        /// LogDebug method\n        /// </summary>\n        public static void LogDebug(IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Debug, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogDebug method\n        /// </summary>\n        public static void LogDebug(Exception ex, IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Debug, ex, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogDebug method\n        /// </summary>\n        public static void LogDebug(string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Debug, message, args);\n        }\n\n        /// <summary>\n        /// LogDebug method\n        /// </summary>\n        public static void LogDebug(Exception ex, string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Debug, ex, null, message, args);\n        }\n\n        /// <summary>\n        /// LogInformation method\n        /// </summary>\n        public static void LogInformation(IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Information, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogInformation method\n        /// </summary>\n        public static void LogInformation(Exception ex, IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Information, ex, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogInformation method\n        /// </summary>\n        public static void LogInformation(string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Information, message, args);\n        }\n\n        /// <summary>\n        /// LogInformation method\n        /// </summary>\n        public static void LogInformation(Exception ex, string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Information, ex, message, args);\n        }\n\n        /// <summary>\n        /// LogWarning method\n        /// </summary>\n        public static void LogWarning(IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Warning, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogWarning method\n        /// </summary>\n        public static void LogWarning(Exception ex, IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Warning, ex, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogWarning method\n        /// </summary>\n        public static void LogWarning(string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Warning, message, args);\n        }\n\n        /// <summary>\n        /// LogWarning method\n        /// </summary>\n        public static void LogWarning(Exception ex, string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Warning, ex, message, args);\n        }\n\n        /// <summary>\n        /// LogError method\n        /// </summary>\n        public static void LogError(IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Error, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogError method\n        /// </summary>\n        public static void LogError(Exception ex, IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Error, ex, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogError method\n        /// </summary>\n        public static void LogError(string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Error, message, args);\n        }\n\n        /// <summary>\n        /// LogError method\n        /// </summary>\n        public static void LogError(Exception ex, string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Error, ex, message, args);\n        }\n\n        /// <summary>\n        /// LogFatal method\n        /// </summary>\n        public static void LogFatal(IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Fatal, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogFatal method\n        /// </summary>\n        public static void LogFatal(Exception ex, IKeyed keyed, string message, params object[] args)\n        {\n            using (LogContext.PushProperty(\"Key\", keyed?.Key))\n            {\n                _logger.Write(LogEventLevel.Fatal, ex, message, args);\n            }\n        }\n\n        /// <summary>\n        /// LogFatal method\n        /// </summary>\n        public static void LogFatal(string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Fatal, message, args);\n        }\n\n        /// <summary>\n        /// LogFatal method\n        /// </summary>\n        public static void LogFatal(Exception ex, string message, params object[] args)\n        {\n            _logger.Write(LogEventLevel.Fatal, ex, message, args);\n        }\n\n        #endregion\n\n\n        private static void LogMessage(uint level, string format, params object[] items)\n        {\n            if (!_logLevels.ContainsKey(level)) return;\n\n            var logLevel = _logLevels[level];\n\n            LogMessage(logLevel, format, items);\n        }\n\n        private static void LogMessage(uint level, IKeyed keyed, string format, params object[] items)\n        {\n            if (!_logLevels.ContainsKey(level)) return;\n\n            var logLevel = _logLevels[level];\n\n            LogMessage(logLevel, keyed, format, items);\n        }\n\n\n        /// <summary>\n        /// Prints message to console if current debug level is equal to or higher than the level of this message.\n        /// Uses CrestronConsole.PrintLine.\n        /// </summary>\n        /// <param name=\"level\"></param>\n        /// <param name=\"format\">Console format string</param>\n        /// <param name=\"items\">Object parameters</param>\n        [Obsolete(\"Use LogMessage methods. Will be removed in 2.2.0 and later versions\")]\n        public static void Console(uint level, string format, params object[] items)\n        {\n\n            LogMessage(level, format, items);\n\n            //if (IsRunningOnAppliance)\n            //{\n            //    CrestronConsole.PrintLine(\"[{0}]App {1} Lvl {2}:{3}\", DateTime.Now.ToString(\"HH:mm:ss.fff\"),\n            //        InitialParametersClass.ApplicationNumber,\n            //        level,\n            //        string.Format(format, items));\n            //}\n        }\n\n        /// <summary>\n\t\t/// Logs to Console when at-level, and all messages to error log, including device key\t\t\t\n        /// </summary>\n        [Obsolete(\"Use LogMessage methods, Will be removed in 2.2.0 and later versions\")]\n        public static void Console(uint level, IKeyed dev, string format, params object[] items)\n        {\n            LogMessage(level, dev, format, items);\n\n            //if (Level >= level)\n            //    Console(level, \"[{0}] {1}\", dev.Key, message);\n        }\n\n        /// <summary>\n        /// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log.\n        /// Uses CrestronConsole.PrintLine.\n        /// </summary>\n        [Obsolete(\"Use LogMessage methods, Will be removed in 2.2.0 and later versions\")]\n        public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,\n            string format, params object[] items)\n        {\n            LogMessage(level, dev, format, items);\n        }\n\n        /// <summary>\n        /// Logs to Console when at-level, and all messages to error log\n        /// </summary>\n        [Obsolete(\"Use LogMessage methods, Will be removed in 2.2.0 and later versions\")]\n        public static void Console(uint level, ErrorLogLevel errorLogLevel,\n            string format, params object[] items)\n        {\n            LogMessage(level, format, items);\n        }\n\n        /// <summary>\n        /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at\n        /// or above the level provided, then the output will be written to both console and the log. Otherwise\n        /// it will only be written to the log.\n        /// </summary>\n        [Obsolete(\"Use LogMessage methods, Will be removed in 2.2.0 and later versions\")]\n        public static void ConsoleWithLog(uint level, string format, params object[] items)\n        {\n            LogMessage(level, format, items);\n\n            // var str = string.Format(format, items);\n            //if (Level >= level)\n            //    CrestronConsole.PrintLine(\"App {0}:{1}\", InitialParametersClass.ApplicationNumber, str);\n            // CrestronLogger.WriteToLog(str, level);\n        }\n\n        /// <summary>\n        /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at\n        /// or above the level provided, then the output will be written to both console and the log. Otherwise\n        /// it will only be written to the log.\n        /// </summary>\n        [Obsolete(\"Use LogMessage methods, Will be removed in 2.2.0 and later versions\")]\n        public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)\n        {\n            LogMessage(level, dev, format, items);\n\n            // var str = string.Format(format, items);\n            // CrestronLogger.WriteToLog(string.Format(\"[{0}] {1}\", dev.Key, str), level);\n        }\n\n        /// <summary>\n        /// Prints to log and error log\n        /// </summary>\n        /// <param name=\"errorLogLevel\"></param>\n        /// <param name=\"str\"></param>\n        [Obsolete(\"Use LogMessage methods, Will be removed in 2.2.0 and later versions\")]\n        public static void LogError(ErrorLogLevel errorLogLevel, string str)\n        {\n            switch (errorLogLevel)\n            {\n                case ErrorLogLevel.Error:\n                    LogMessage(LogEventLevel.Error, str);\n                    break;\n                case ErrorLogLevel.Warning:\n                    LogMessage(LogEventLevel.Warning, str);\n                    break;\n                case ErrorLogLevel.Notice:\n                    LogMessage(LogEventLevel.Information, str);\n                    break;\n            }\n        }\n\n        /// <summary>\n        /// Writes the memory object after timeout\n        /// </summary>\n        static void SaveMemoryOnTimeout()\n        {\n            Console(0, \"Saving debug settings\");\n            if (_saveTimer == null)\n                _saveTimer = new CTimer(o =>\n                {\n                    _saveTimer = null;\n                    SaveMemory();\n                }, SaveTimeoutMs);\n            else\n                _saveTimer.Reset(SaveTimeoutMs);\n        }\n\n        /// <summary>\n        /// Writes the memory - use SaveMemoryOnTimeout\n        /// </summary>\n        static void SaveMemory()\n        {\n            //var dir = @\"\\NVRAM\\debug\";\n            //if (!Directory.Exists(dir))\n            //    Directory.Create(dir);\n\n            var fileName = GetMemoryFileName();\n\n            LogMessage(LogEventLevel.Information, \"Loading debug settings file from {fileName}\", fileName);\n\n            using (var sw = new StreamWriter(fileName))\n            {\n                var json = JsonConvert.SerializeObject(_contexts);\n                sw.Write(json);\n                sw.Flush();\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        static void LoadMemory()\n        {\n            var file = GetMemoryFileName();\n            if (File.Exists(file))\n            {\n                using (var sr = new StreamReader(file))\n                {\n                    var json = sr.ReadToEnd();\n                    _contexts = JsonConvert.DeserializeObject<DebugContextCollection>(json);\n\n                    if (_contexts != null)\n                    {\n                        LogMessage(LogEventLevel.Debug, \"Debug memory restored from file\");\n                        return;\n                    }\n                }\n            }\n\n            _contexts = new DebugContextCollection();\n        }\n\n        /// <summary>\n        /// Helper to get the file path for this app's debug memory\n        /// </summary>\n        static string GetMemoryFileName()\n        {\n            if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)\n            {\n                // CheckForMigration();\n                return string.Format(@\"\\user\\debugSettings\\program{0}\", InitialParametersClass.ApplicationNumber);\n            }\n\n            return string.Format(\"{0}{1}user{1}debugSettings{1}{2}.json\", Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);\n        }\n\n        /// <summary>\n        /// Enumeration of ErrorLogLevel values\n        /// </summary>\n        public enum ErrorLogLevel\n        {\n            /// <summary>\n            /// Error\n            /// </summary>\n            Error,\n            /// <summary>\n            /// Warning\n            /// </summary>\n            Warning,\n            /// <summary>\n            /// Notice\n            /// </summary>\n            Notice,\n            /// <summary>\n            /// None\n            /// </summary>\n            None,\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Logging/DebugConsoleSink.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Serilog.Configuration;\nusing Serilog;\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Formatting;\nusing Serilog.Formatting.Json;\nusing System.IO;\nusing System.Text;\n\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Represents a DebugConsoleSink\n    /// </summary>\n    public class DebugConsoleSink : ILogEventSink\n    {\n        private readonly ITextFormatter _textFormatter;\n\n        /// <summary>\n        /// Emit method\n        /// </summary>\n        public void Emit(LogEvent logEvent)\n        {\n            if (!Debug.IsRunningOnAppliance) return;            \n\n            /*string message = $\"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}\";\n\n            if(logEvent.Properties.TryGetValue(\"Key\",out var value) && value is ScalarValue sv && sv.Value is string rawValue)\n            {\n                message = $\"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}][{rawValue,3}]: {logEvent.RenderMessage()}\";\n            }*/\n\n            var buffer = new StringWriter(new StringBuilder(256));\n\n            _textFormatter.Format(logEvent, buffer);\n\n            var message = buffer.ToString();\n\n            CrestronConsole.PrintLine(message);\n        }\n\n        public DebugConsoleSink(ITextFormatter formatProvider )\n        {\n            _textFormatter = formatProvider ?? new JsonFormatter();\n        }\n\n    }\n\n    public static class DebugConsoleSinkExtensions\n    {\n        /// <summary>\n        /// DebugConsoleSink method\n        /// </summary>\n        public static LoggerConfiguration DebugConsoleSink(\n                             this LoggerSinkConfiguration loggerConfiguration,\n                                              ITextFormatter formatProvider = null)\n        {\n            return loggerConfiguration.Sink(new DebugConsoleSink(formatProvider));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Logging/DebugContext.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json;\n\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Represents a debugging context\n    /// </summary>\n    [Obsolete(\"DebugContext is no longer supported and will be removed in a future release.\")]\n    public class DebugContext\n    {\n        /// <summary>\n        /// Describes the folder location where a given program stores it's debug level memory. By default, the\n        /// file written will be named appNdebug where N is 1-10.\n        /// </summary>\n        public string Key { get; private set; }\n\n        /// <summary>\n        /// The name of the file containing the current debug settings.\n        /// </summary>\n        //string FileName = string.Format(@\"\\nvram\\debug\\app{0}Debug.json\", InitialParametersClass.ApplicationNumber);\n\n        DebugContextSaveData SaveData;\n\n        int SaveTimeoutMs = 30000;\n\n        CTimer SaveTimer;\n\n\n        static List<DebugContext> Contexts = new List<DebugContext>();\n\n        /// <summary>\n        /// Creates or gets a debug context\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetDebugContext method\n        /// </summary>\n        public static DebugContext GetDebugContext(string key)\n        {\n            var context = Contexts.FirstOrDefault(c => c.Key.Equals(key, StringComparison.OrdinalIgnoreCase));\n            if (context == null)\n            {\n                context = new DebugContext(key);\n                Contexts.Add(context);\n            }\n            return context;\n        }\n\n        /// <summary>\n        /// Do not use.  For S+ access.\n        /// </summary>\n        public DebugContext() { }\n\n        DebugContext(string key)\n        {\n            Key = key;\n            if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)\n            {\n                // Add command to console\n                CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, \"appdebug\",\n                    \"appdebug:P [0-2]: Sets the application's console debug message level\",\n                    ConsoleAccessLevelEnum.AccessOperator);\n            }\n\n            CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;\n\n            LoadMemory();\n        }\n\n        /// <summary>\n        /// Used to save memory when shutting down\n        /// </summary>\n        /// <param name=\"programEventType\"></param>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping)\n            {\n                if (SaveTimer != null)\n                {\n                    SaveTimer.Stop();\n                    SaveTimer = null;\n                }\n                Console(0, \"Saving debug settings\");\n                SaveMemory();\n            }\n        }\n\n        /// <summary>\n        /// Callback for console command\n        /// </summary>\n        /// <param name=\"levelString\"></param>\n        /// <summary>\n        /// SetDebugFromConsole method\n        /// </summary>\n        public void SetDebugFromConsole(string levelString)\n        {\n            try\n            {\n                if (string.IsNullOrEmpty(levelString.Trim()))\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"AppDebug level = {0}\", SaveData.Level);\n                    return;\n                }\n\n                SetDebugLevel(Convert.ToInt32(levelString));\n            }\n            catch\n            {\n                CrestronConsole.PrintLine(\"Usage: appdebug:P [0-2]\");\n            }\n        }\n\n        /// <summary>\n        /// Sets the debug level\n        /// </summary>\n        /// <param name=\"level\"> Valid values 0 (no debug), 1 (critical), 2 (all messages)</param>\n        /// <summary>\n        /// SetDebugLevel method\n        /// </summary>\n        public void SetDebugLevel(int level)\n        {\n            if (level <= 2)\n            {\n                SaveData.Level = level;\n                SaveMemoryOnTimeout();\n\n                CrestronConsole.PrintLine(\"[Application {0}], Debug level set to {1}\",\n                    InitialParametersClass.ApplicationNumber, SaveData.Level);\n            }\n        }\n\n        /// <summary>\n        /// Prints message to console if current debug level is equal to or higher than the level of this message.\n        /// Uses CrestronConsole.PrintLine.\n        /// </summary>\n        /// <param name=\"level\"></param>\n        /// <param name=\"format\">Console format string</param>\n        /// <param name=\"items\">Object parameters</param>\n        public void Console(uint level, string format, params object[] items)\n        {\n            if (SaveData.Level >= level)\n                CrestronConsole.PrintLine(\"App {0}:{1}\", InitialParametersClass.ApplicationNumber,\n                    string.Format(format, items));\n        }\n\n        /// <summary>\n        /// Console method\n        /// </summary>\n        public void Console(uint level, IKeyed dev, string format, params object[] items)\n        {\n            if (SaveData.Level >= level)\n                Console(level, \"[{0}] {1}\", dev.Key, string.Format(format, items));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"level\"></param>\n        /// <param name=\"dev\"></param>\n        /// <param name=\"errorLogLevel\"></param>\n        /// <param name=\"format\"></param>\n        /// <param name=\"items\"></param>\n        public void Console(uint level, IKeyed dev, Debug.ErrorLogLevel errorLogLevel,\n            string format, params object[] items)\n        {\n            if (SaveData.Level >= level)\n            {\n                var str = string.Format(\"[{0}] {1}\", dev.Key, string.Format(format, items));\n                Console(level, str);\n                LogError(errorLogLevel, str);\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"level\"></param>\n        /// <param name=\"errorLogLevel\"></param>\n        /// <param name=\"format\"></param>\n        /// <param name=\"items\"></param>\n        public void Console(uint level, Debug.ErrorLogLevel errorLogLevel,\n            string format, params object[] items)\n        {\n            if (SaveData.Level >= level)\n            {\n                var str = string.Format(format, items);\n                Console(level, str);\n                LogError(errorLogLevel, str);\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"errorLogLevel\"></param>\n        /// <param name=\"str\"></param>\n        /// <summary>\n        /// LogError method\n        /// </summary>\n        public void LogError(Debug.ErrorLogLevel errorLogLevel, string str)\n        {\n            string msg = string.Format(\"App {0}:{1}\", InitialParametersClass.ApplicationNumber, str);\n            switch (errorLogLevel)\n            {\n                case Debug.ErrorLogLevel.Error:\n                    ErrorLog.Error(msg);\n                    break;\n                case Debug.ErrorLogLevel.Warning:\n                    ErrorLog.Warn(msg);\n                    break;\n                case Debug.ErrorLogLevel.Notice:\n                    ErrorLog.Notice(msg);\n                    break;\n            }\n        }\n\n        /// <summary>\n        /// Writes the memory object after timeout\n        /// </summary>\n        void SaveMemoryOnTimeout()\n        {\n            if (SaveTimer == null)\n                SaveTimer = new CTimer(o =>\n                {\n                    SaveTimer = null;\n                    SaveMemory();\n                }, SaveTimeoutMs);\n            else\n                SaveTimer.Reset(SaveTimeoutMs);\n        }\n\n        /// <summary>\n        /// Writes the memory - use SaveMemoryOnTimeout\n        /// </summary>\n        void SaveMemory()\n        {\n            using (StreamWriter sw = new StreamWriter(GetMemoryFileName()))\n            {\n                var json = JsonConvert.SerializeObject(SaveData);\n                sw.Write(json);\n                sw.Flush();\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void LoadMemory()\n        {\n            var file = GetMemoryFileName();\n            if (File.Exists(file))\n            {\n                using (StreamReader sr = new StreamReader(file))\n                {\n                    var data = JsonConvert.DeserializeObject<DebugContextSaveData>(sr.ReadToEnd());\n                    if (data != null)\n                    {\n                        SaveData = data;\n                        Debug.Console(1, \"Debug memory restored from file\");\n                        return;\n                    }\n                    else\n                        SaveData = new DebugContextSaveData();\n                }\n            }\n        }\n\n        /// <summary>\n        /// Helper to get the file path for this app's debug memory\n        /// </summary>\n        string GetMemoryFileName()\n        {\n            return string.Format(@\"\\NVRAM\\debugSettings\\program{0}-{1}\", InitialParametersClass.ApplicationNumber, Key);\n        }\n    }\n\n    /// <summary>\n    /// \n    /// </summary>\n    public class DebugContextSaveData\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        public int Level { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Logging/DebugCrestronLoggerSink.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronLogger;\nusing Serilog.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Core.Logging\n{\n    /// <summary>\n    /// Represents a DebugCrestronLoggerSink\n    /// </summary>\n    public class DebugCrestronLoggerSink : ILogEventSink\n    {\n        /// <summary>\n        /// Emit method\n        /// </summary>\n        public void Emit(LogEvent logEvent)\n        {\n            if (!Debug.IsRunningOnAppliance) return;\n\n            string message = $\"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}\";\n\n            if (logEvent.Properties.TryGetValue(\"Key\", out var value) && value is ScalarValue sv && sv.Value is string rawValue)\n            {\n                message = $\"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}][{rawValue}]: {logEvent.RenderMessage()}\";\n            }\n\n            CrestronLogger.WriteToLog(message, (uint)logEvent.Level);\n        }\n\n        public DebugCrestronLoggerSink()\n        {\n            CrestronLogger.Initialize(1, LoggerModeEnum.RM);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Logging/DebugErrorLogSink.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Formatting;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Core.Logging\n{\n    /// <summary>\n    /// Represents a DebugErrorLogSink\n    /// </summary>\n    public class DebugErrorLogSink : ILogEventSink\n    {\n        private ITextFormatter _formatter;\n\n        private Dictionary<LogEventLevel, Action<string>> _errorLogMap = new Dictionary<LogEventLevel, Action<string>>\n        {\n            { LogEventLevel.Verbose, (msg) => ErrorLog.Notice(msg) },\n            {LogEventLevel.Debug, (msg) => ErrorLog.Notice(msg) },\n            {LogEventLevel.Information, (msg) => ErrorLog.Notice(msg) },\n            {LogEventLevel.Warning, (msg) => ErrorLog.Warn(msg) },\n            {LogEventLevel.Error, (msg) => ErrorLog.Error(msg) },\n            {LogEventLevel.Fatal, (msg) => ErrorLog.Error(msg) }\n        };\n        /// <summary>\n        /// Emit method\n        /// </summary>\n        public void Emit(LogEvent logEvent)\n        {\n            string message;\n\n            if (_formatter == null)\n            {\n                var programId = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance\n                    ? $\"App {InitialParametersClass.ApplicationNumber}\"\n                    : $\"Room {InitialParametersClass.RoomId}\";\n\n                message = $\"[{logEvent.Timestamp}][{logEvent.Level}][{programId}]{logEvent.RenderMessage()}\";\n\n                if (logEvent.Properties.TryGetValue(\"Key\", out var value) && value is ScalarValue sv && sv.Value is string rawValue)\n                {\n                    message = $\"[{logEvent.Timestamp}][{logEvent.Level}][{programId}][{rawValue}]: {logEvent.RenderMessage()}\";\n                }\n            } else\n            {\n                var buffer = new StringWriter(new StringBuilder(256));\n\n                _formatter.Format(logEvent, buffer);\n\n                message = buffer.ToString();\n            }\n\n            if(!_errorLogMap.TryGetValue(logEvent.Level, out var handler))\n            {\n                return;\n            }\n\n            handler(message);\n        }\n\n        public DebugErrorLogSink(ITextFormatter formatter = null)\n        {\n            _formatter = formatter;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Logging/DebugExtensions.cs",
    "content": "﻿using System;\nusing Serilog.Events;\nusing Log = PepperDash.Core.Debug;\n\nnamespace PepperDash.Core.Logging\n{\n    public static class DebugExtensions\n    {\n        /// <summary>\n        /// LogException method\n        /// </summary>\n        public static void LogException(this IKeyed device, Exception ex, string message, params object[] args)\n        {\n            Log.LogMessage(ex, message, device: device, args);\n        }\n\n        /// <summary>\n        /// LogVerbose method\n        /// </summary>\n        public static void LogVerbose(this IKeyed device, Exception ex, string message, params object[] args)\n        {\n            Log.LogVerbose(ex, device, message, args);\n        }\n\n        /// <summary>\n        /// LogVerbose method\n        /// </summary>\n        public static void LogVerbose(this IKeyed device, string message, params object[] args)\n        {\n            Log.LogVerbose(device, message, args);\n        }\n\n        /// <summary>\n        /// LogDebug method\n        /// </summary>\n        public static void LogDebug(this IKeyed device, Exception ex, string message, params object[] args)\n        {\n            Log.LogDebug(ex, device, message, args);\n        }\n\n        /// <summary>\n        /// LogDebug method\n        /// </summary>\n        public static void LogDebug(this IKeyed device, string message, params object[] args)\n        {\n            Log.LogDebug(device, message, args);\n        }\n\n        /// <summary>\n        /// LogInformation method\n        /// </summary>\n        public static void LogInformation(this IKeyed device, Exception ex, string message, params object[] args)\n        {\n            Log.LogInformation(ex, device, message, args);\n        }\n\n        /// <summary>\n        /// LogInformation method\n        /// </summary>\n        public static void LogInformation(this IKeyed device, string message, params object[] args)\n        {\n            Log.LogInformation(device, message, args);\n        }\n\n        /// <summary>\n        /// LogWarning method\n        /// </summary>\n        public static void LogWarning(this IKeyed device, Exception ex, string message, params object[] args)\n        {\n            Log.LogWarning(ex, device, message, args);\n        }\n\n        /// <summary>\n        /// LogWarning method\n        /// </summary>\n        public static void LogWarning(this IKeyed device, string message, params object[] args)\n        {\n            Log.LogWarning(device, message, args);\n        }\n\n        /// <summary>\n        /// LogError method\n        /// </summary>\n        public static void LogError(this IKeyed device, Exception ex, string message, params object[] args)\n        {\n            Log.LogError(ex, device, message, args);\n        }\n\n        /// <summary>\n        /// LogError method\n        /// </summary>\n        public static void LogError(this IKeyed device, string message, params object[] args)\n        {\n            Log.LogError(device, message, args);\n        }\n\n        /// <summary>\n        /// LogFatal method\n        /// </summary>\n        public static void LogFatal(this IKeyed device, Exception ex, string message, params object[] args)\n        {\n            Log.LogFatal(ex, device, message, args);\n        }\n\n        /// <summary>\n        /// LogFatal method\n        /// </summary>\n        public static void LogFatal(this IKeyed device, string message, params object[] args)\n        {\n            Log.LogFatal(device, message, args);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Logging/DebugMemory.cs",
    "content": "﻿using System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Core.Logging\n{\n /// <summary>\n /// Represents a DebugContextCollection\n /// </summary>\n\tpublic class DebugContextCollection\n\t{\n        /// <summary>\n        /// To prevent threading issues with the DeviceDebugSettings collection\n        /// </summary>\n        private readonly CCriticalSection _deviceDebugSettingsLock;\n\n\t\t[JsonProperty(\"items\")] private readonly Dictionary<string, DebugContextItem> _items;\n\n        /// <summary>\n        /// Collection of the debug settings for each device where the dictionary key is the device key\n        /// </summary>\n        [JsonProperty(\"deviceDebugSettings\")]\n        private Dictionary<string, object> DeviceDebugSettings { get; set; }\n\n\n\t\t/// <summary>\n\t\t/// Default constructor\n\t\t/// </summary>\n\t\tpublic DebugContextCollection()\n\t\t{\n            _deviceDebugSettingsLock = new CCriticalSection();\n            DeviceDebugSettings = new Dictionary<string, object>();\n\t\t\t_items = new Dictionary<string, DebugContextItem>();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets the level of a given context item, and adds that item if it does not \n\t\t/// exist\n\t\t/// </summary>\n\t\t/// <param name=\"contextKey\"></param>\n\t\t/// <param name=\"level\"></param>\n  /// <summary>\n  /// SetLevel method\n  /// </summary>\n\t\tpublic void SetLevel(string contextKey, int level)\n\t\t{\n\t\t\tif (level < 0 || level > 2)\n\t\t\t\treturn;\n\t\t\tGetOrCreateItem(contextKey).Level = level;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets a level or creates it if not existing\n\t\t/// </summary>\n\t\t/// <param name=\"contextKey\"></param>\n\t\t/// <returns></returns>\n  /// <summary>\n  /// GetOrCreateItem method\n  /// </summary>\n\t\tpublic DebugContextItem GetOrCreateItem(string contextKey)\n\t\t{\n\t\t\tif (!_items.ContainsKey(contextKey))\n\t\t\t\t_items[contextKey] = new DebugContextItem { Level = 0 };\n\t\t\treturn _items[contextKey];\n\t\t}\n\n\n        /// <summary>\n        /// sets the settings for a device or creates a new entry\n        /// </summary>\n        /// <param name=\"deviceKey\"></param>\n        /// <param name=\"settings\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// SetDebugSettingsForKey method\n        /// </summary>\n        public void SetDebugSettingsForKey(string deviceKey, object settings)\n        {\n            try\n            {\n                _deviceDebugSettingsLock.Enter();\n\n                if (DeviceDebugSettings.ContainsKey(deviceKey))\n                {\n                    DeviceDebugSettings[deviceKey] = settings;\n                }\n                else\n                    DeviceDebugSettings.Add(deviceKey, settings);\n            }\n            finally\n            {\n                _deviceDebugSettingsLock.Leave();\n            }\n        }\n\n        /// <summary>\n        /// Gets the device settings for a device by key or returns null\n        /// </summary>\n        /// <param name=\"deviceKey\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetDebugSettingsForKey method\n        /// </summary>\n        public object GetDebugSettingsForKey(string deviceKey)\n        {\n            return DeviceDebugSettings[deviceKey];\n        }\n\t}\n\n\t/// <summary>\n\t/// Contains information about \n\t/// </summary>\n\tpublic class DebugContextItem\n\t{\n        /// <summary>\n        /// The level of debug messages to print\n        /// </summary>\n\t\t[JsonProperty(\"level\")]\n\t\tpublic int Level { get; set; }\n\n        /// <summary>\n        /// Property to tell the program not to intitialize when it boots, if desired\n        /// </summary>\n        [JsonProperty(\"doNotLoadOnNextBoot\")]\n        public bool DoNotLoadOnNextBoot { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/Logging/DebugWebsocketSink.cs",
    "content": "﻿using System;\nusing Serilog;\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Configuration;\nusing WebSocketSharp.Server;\nusing Crestron.SimplSharp;\nusing WebSocketSharp;\nusing System.Security.Authentication;\nusing WebSocketSharp.Net;\nusing System.Security.Cryptography.X509Certificates;\nusing System.IO;\nusing Org.BouncyCastle.Asn1;\nusing Org.BouncyCastle.Asn1.X509;\nusing Org.BouncyCastle.Crypto;\nusing Org.BouncyCastle.Crypto.Generators;\nusing Org.BouncyCastle.Crypto.Operators;\nusing Org.BouncyCastle.Math;\nusing Org.BouncyCastle.Pkcs;\nusing Org.BouncyCastle.Security;\nusing Org.BouncyCastle.X509;\nusing Serilog.Formatting;\nusing Serilog.Formatting.Json;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Represents a DebugWebsocketSink\n    /// </summary>\n    public class DebugWebsocketSink : ILogEventSink\n    {\n        private HttpServer _httpsServer;\n\n        private string _path = \"/debug/join/\";\n        private const string _certificateName = \"selfCres\";\n        private const string _certificatePassword = \"cres12345\";\n\n        private static string CertPath =>\n    $\"{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}{_certificateName}.pfx\";\n\n\n        public int Port\n        {\n            get\n            {\n\n                if (_httpsServer == null) return 0;\n                return _httpsServer.Port;\n            }\n        }\n\n        public string Url\n        {\n            get\n            {\n                if (_httpsServer == null || !_httpsServer.IsListening) return \"\";\n                var service = _httpsServer.WebSocketServices[_path];\n                if (service == null) return \"\";\n\n                // Use CSLAN IP if available, otherwise fallback to primary IP. This ensures we provide a reachable URL in dual-stack environments.\n                var cslanIp = CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1);\n                if (!string.IsNullOrEmpty(cslanIp) && cslanIp != \"Invalid Value\")\n                    return $\"wss://{cslanIp}:{_httpsServer.Port}{service.Path}\";\n                else\n                    return $\"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{service.Path}\";\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the IsRunning\n        /// </summary>\n        public bool IsRunning { get => _httpsServer?.IsListening ?? false; }\n\n\n        private readonly ITextFormatter _textFormatter;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DebugWebsocketSink\"/> class with the specified text formatter.\n        /// </summary>\n        /// <remarks>This constructor initializes the WebSocket sink and ensures that a certificate is\n        /// available for secure communication. If the required certificate does not exist, it will be created\n        /// automatically. Additionally, the sink is configured to stop the server when the program is\n        /// stopping.</remarks>\n        /// <param name=\"formatProvider\">The text formatter used to format log messages. If null, a default JSON formatter is used.</param>\n        public DebugWebsocketSink(ITextFormatter formatProvider)\n        {\n\n            _textFormatter = formatProvider ?? new JsonFormatter();\n\n            if (!File.Exists(CertPath))\n                CreateCert();\n\n            try\n            {\n                CrestronEnvironment.ProgramStatusEventHandler += type =>\n                {\n                    if (type == eProgramStatusEventType.Stopping)\n                        StopServer();\n                };\n            }\n            catch\n            {\n                // CrestronEnvironment is not available in test / dev environments — safe to skip.\n            }\n        }\n\n        private static void CreateCert()\n        {\n            // NOTE: This method is called from the constructor, which is itself called during Debug's static\n            // constructor before _logger is assigned. Do NOT call any Debug.Log* methods here — use\n            // CrestronConsole.PrintLine only, to avoid a NullReferenceException that would poison the Debug type.\n            try\n            {\n                var ipAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);\n                var hostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);\n                var domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);\n\n                CrestronConsole.PrintLine(string.Format(\"CreateCert: DomainName: {0} | HostName: {1} | {1}.{0}@{2}\", domainName, hostName, ipAddress));\n\n                var subjectName = string.Format(\"CN={0}.{1}\", hostName, domainName);\n                var fqdn = string.Format(\"{0}.{1}\", hostName, domainName);\n\n                var random = new SecureRandom();\n\n                // Generate RSA 2048 key pair\n                var keyPairGenerator = new RsaKeyPairGenerator();\n                keyPairGenerator.Init(new KeyGenerationParameters(random, 2048));\n                var keyPair = keyPairGenerator.GenerateKeyPair();\n\n                // Build certificate\n                var certGenerator = new X509V3CertificateGenerator();\n                certGenerator.SetSerialNumber(BigInteger.ValueOf(Math.Abs(DateTime.UtcNow.Ticks)));\n                certGenerator.SetIssuerDN(new X509Name(subjectName));\n                certGenerator.SetSubjectDN(new X509Name(subjectName));\n                certGenerator.SetNotBefore(DateTime.UtcNow);\n                certGenerator.SetNotAfter(DateTime.UtcNow.AddYears(2));\n                certGenerator.SetPublicKey(keyPair.Public);\n\n                // Extended Key Usage: server + client auth\n                certGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, false,\n                    new ExtendedKeyUsage(new[] { KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth }));\n\n                // Subject Alternative Names: DNS + IP\n                System.Net.IPAddress parsedIp;\n                if (System.Net.IPAddress.TryParse(ipAddress, out parsedIp))\n                {\n                    certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false,\n                        new GeneralNames(new GeneralName[] {\n                            new GeneralName(GeneralName.DnsName, fqdn),\n                            new GeneralName(GeneralName.IPAddress, ipAddress)\n                        }));\n                }\n                else\n                {\n                    certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false,\n                        new GeneralNames(new GeneralName(GeneralName.DnsName, fqdn)));\n                }\n\n                // Sign with SHA256withRSA\n                var signatureFactory = new Asn1SignatureFactory(\"SHA256WITHRSA\", keyPair.Private, random);\n                var certificate = certGenerator.Generate(signatureFactory);\n\n                // Export as PKCS12/PFX\n                var pkcs12Store = new Pkcs12StoreBuilder().Build();\n                var certEntry = new X509CertificateEntry(certificate);\n                pkcs12Store.SetCertificateEntry(_certificateName, certEntry);\n                pkcs12Store.SetKeyEntry(_certificateName, new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });\n\n                var separator = Path.DirectorySeparatorChar;\n                var outputPath = string.Format(\"{0}user{1}{2}.pfx\", separator, separator, _certificateName);\n\n                using (var ms = new MemoryStream())\n                {\n                    var passwordChars = _certificatePassword.ToCharArray();\n                    try\n                    {\n                        pkcs12Store.Save(ms, passwordChars, random);\n                    }\n                    finally\n                    {\n                        Array.Clear(passwordChars, 0, passwordChars.Length);\n                    }\n                    File.WriteAllBytes(outputPath, ms.ToArray());\n                }\n\n                CrestronConsole.PrintLine(string.Format(\"CreateCert: Certificate written to {0}\", outputPath));\n            }\n            catch (Exception ex)\n            {\n                CrestronConsole.PrintLine(string.Format(\"WSS CreateCert Failed: {0}\\r\\n{1}\", ex.Message, ex.StackTrace));\n            }\n        }\n\n        /// <summary>\n        /// Emit method\n        /// </summary>\n        public void Emit(LogEvent logEvent)\n        {\n            if (_httpsServer == null || !_httpsServer.IsListening) return;\n\n            var sw = new StringWriter();\n            _textFormatter.Format(logEvent, sw);\n\n            _httpsServer.WebSocketServices.Broadcast(sw.ToString());\n\n        }\n\n        /// <summary>\n        /// Starts the WebSocket server on the specified port and configures it with the appropriate certificate.\n        /// </summary>\n        /// <remarks>This method initializes the WebSocket server and binds it to the specified port.  It\n        /// also applies the server's certificate for secure communication. Ensure that the port is not already in use\n        /// and that the certificate file is accessible.</remarks>\n        /// <param name=\"port\">The port number on which the WebSocket server will listen. Must be a valid, non-negative port number.</param>\n        public void StartServerAndSetPort(int port)\n        {\n            Debug.LogInformation(\"Starting Websocket Server on port: {0}\", port);\n\n\n            Start(port, CertPath, _certificatePassword);\n        }\n\n        private static X509Certificate2 LoadOrRecreateCert(string certPath, string certPassword)\n        {\n            if (!File.Exists(certPath))\n                CreateCert();\n\n            try\n            {\n                return LoadCertFromBouncyCastle(certPath, certPassword);\n            }\n            catch (Exception ex)\n            {\n                // Cert is corrupt or was written by an incompatible tool — delete and regenerate once.\n                CrestronConsole.PrintLine(string.Format(\"SSL cert load failed ({0}); regenerating...\", ex.Message));\n                try { File.Delete(certPath); } catch { }\n                CreateCert();\n                return LoadCertFromBouncyCastle(certPath, certPassword);\n            }\n        }\n\n        /// <summary>\n        /// Loads a PKCS#12 file written by BouncyCastle and returns an <see cref=\"X509Certificate2\"/> with\n        /// private key attached.\n        /// The PFX is parsed and re-encoded by BouncyCastle (ensuring format compatibility), then passed as\n        /// raw bytes to <see cref=\"X509Certificate2\"/> so neither <c>RSACryptoServiceProvider</c> nor the\n        /// <c>EphemeralKeySet</c> flag (unsupported on the Crestron/Mono runtime) is needed.\n        /// </summary>\n        private static X509Certificate2 LoadCertFromBouncyCastle(string certPath, string certPassword)\n        {\n            var passwordChars = certPassword.ToCharArray();\n            try\n            {\n                using (var stream = File.OpenRead(certPath))\n                {\n                    var store = new Pkcs12StoreBuilder().Build();\n                    store.Load(stream, passwordChars);\n\n                    // Re-encode through BouncyCastle to guarantee PKCS#12 format compatibility,\n                    // then hand raw bytes to X509Certificate2 — no RSACryptoServiceProvider needed.\n                    using (var ms = new MemoryStream())\n                    {\n                        store.Save(ms, passwordChars, new SecureRandom());\n                        var cert = new X509Certificate2(ms.ToArray(), certPassword);\n\n                        if (!cert.HasPrivateKey)\n                            throw new InvalidOperationException(\n                                string.Format(\"Certificate loaded from '{0}' does not contain a private key and cannot be used as a server certificate.\", certPath));\n\n                        return cert;\n                    }\n                }\n            }\n            finally\n            {\n                Array.Clear(passwordChars, 0, passwordChars.Length);\n            }\n        }\n\n        private void Start(int port, string certPath = \"\", string certPassword = \"\")\n        {\n            try\n            {\n                _httpsServer = new HttpServer(port, true);\n\n                if (!string.IsNullOrWhiteSpace(certPath))\n                {\n                    Debug.LogInformation(\"Assigning SSL Configuration\");\n\n                    _httpsServer.SslConfiguration.ServerCertificate = LoadOrRecreateCert(certPath, certPassword);\n                    _httpsServer.SslConfiguration.ClientCertificateRequired = false;\n                    _httpsServer.SslConfiguration.CheckCertificateRevocation = false;\n                    _httpsServer.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12;\n                    //this is just to test, you might want to actually validate\n                    _httpsServer.SslConfiguration.ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>\n                        {\n                            Debug.LogInformation(\"HTTPS ClientCerticateValidation Callback triggered\");\n                            return true;\n                        };\n                }\n                Debug.LogInformation(\"Adding Debug Client Service\");\n                _httpsServer.AddWebSocketService<DebugClient>(_path);\n                Debug.LogInformation(\"Assigning Log Info\");\n                _httpsServer.Log.Level = LogLevel.Trace;\n                _httpsServer.Log.Output = WriteWebSocketInternalLog;\n                Debug.LogInformation(\"Starting\");\n\n                _httpsServer.Start();\n                Debug.LogInformation(\"Ready\");\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(ex, \"WebSocket Failed to start {0}\", ex.Message);\n                Debug.LogVerbose(\"Stack Trace:\\r{0}\", ex.StackTrace);\n                // Null out the server so callers can detect failure via IsRunning / Url null guards.\n                _httpsServer = null;\n            }\n        }\n\n        /// <summary>\n        /// StopServer method\n        /// </summary>\n        public void StopServer()\n        {\n            Debug.LogInformation(\"Stopping Websocket Server\");\n\n            try\n            {\n                if (_httpsServer == null || !_httpsServer.IsListening)\n                {\n                    return;\n                }\n\n                // Prevent close-sequence internal websocket logs from re-entering the logging pipeline.\n                _httpsServer.Log.Output = (d, s) => { };\n\n                var serviceHost = _httpsServer.WebSocketServices[_path];\n\n                if (serviceHost == null)\n                {\n                    _httpsServer.Stop();\n                    _httpsServer = null;\n                    return;\n                }\n\n                serviceHost.Sessions.Broadcast(\"Server is stopping\");\n\n                foreach (var session in serviceHost.Sessions.Sessions)\n                {\n                    if (session?.Context?.WebSocket != null && session.Context.WebSocket.IsAlive)\n                    {\n                        session.Context.WebSocket.Close(1001, \"Server is stopping\");\n                    }\n                }\n\n                _httpsServer.Stop();\n\n                _httpsServer = null;\n\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(ex, \"WebSocket Failed to stop gracefully {0}\", ex.Message);\n                Debug.LogVerbose(\"Stack Trace\\r\\n{0}\", ex.StackTrace);\n            }\n        }\n\n        private static void WriteWebSocketInternalLog(LogData data, string supplemental)\n        {\n            try\n            {\n                if (data == null)\n                {\n                    return;\n                }\n\n                var message = string.IsNullOrWhiteSpace(data.Message) ? \"<none>\" : data.Message;\n                var details = string.IsNullOrWhiteSpace(supplemental) ? string.Empty : string.Format(\" | details: {0}\", supplemental);\n\n                // Use direct console output to avoid recursive log sink calls.\n                CrestronConsole.PrintLine(string.Format(\"WS[{0}] {1} | message: {2}{3}\", data.Level, data.Date, message, details));\n            }\n            catch\n            {\n                // Never throw from websocket log callback.\n            }\n        }\n    }\n\n    public static class DebugWebsocketSinkExtensions\n    {\n        /// <summary>\n        /// DebugWebsocketSink method\n        /// </summary>\n        public static LoggerConfiguration DebugWebsocketSink(\n                             this LoggerSinkConfiguration loggerConfiguration,\n                                              ITextFormatter formatProvider = null)\n        {\n            return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider));\n        }\n    }\n\n    /// <summary>\n    /// Represents a DebugClient\n    /// </summary>\n    public class DebugClient : WebSocketBehavior\n    {\n        private DateTime _connectionTime;\n\n        public TimeSpan ConnectedDuration\n        {\n            get\n            {\n                if (Context.WebSocket.IsAlive)\n                {\n                    return DateTime.Now - _connectionTime;\n                }\n                else\n                {\n                    return new TimeSpan(0);\n                }\n            }\n        }\n\n        public DebugClient()\n        {\n            Debug.Console(0, \"DebugClient Created\");\n        }\n\n        protected override void OnOpen()\n        {\n            base.OnOpen();\n\n            var url = Context.WebSocket.Url;\n            Debug.Console(0, Debug.ErrorLogLevel.Notice, \"New WebSocket Connection from: {0}\", url);\n\n            _connectionTime = DateTime.Now;\n        }\n\n        protected override void OnMessage(MessageEventArgs e)\n        {\n            base.OnMessage(e);\n\n            Debug.Console(0, \"WebSocket UiClient Message: {0}\", e.Data);\n        }\n\n        protected override void OnClose(CloseEventArgs e)\n        {\n            base.OnClose(e);\n\n            Debug.Console(0, Debug.ErrorLogLevel.Notice, \"WebSocket UiClient Closing: {0} reason: {1}\", e.Code, e.Reason);\n\n        }\n\n        protected override void OnError(WebSocketSharp.ErrorEventArgs e)\n        {\n            base.OnError(e);\n\n            Debug.Console(2, Debug.ErrorLogLevel.Notice, \"WebSocket UiClient Error: {0} message: {1}\", e.Exception, e.Message);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Network/DiscoveryThings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Not in use\n    /// </summary>\n\tpublic static class NetworkComm\n\t{\n        /// <summary>\n        /// Not in use\n        /// </summary>\n\t\tstatic NetworkComm()\n\t\t{\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/PepperDash.Core/PasswordManagement/Config.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.PasswordManagement\n{\n\t/// <summary>\n\t/// JSON password configuration \n\t/// </summary>\n\tpublic class PasswordConfig\n\t{\n\t    /// <summary>\n\t    /// Password object configured password\n\t    /// </summary>\n\t    public string password { get; set; }\n\t    /// <summary>\n\t    /// Constructor\n\t    /// </summary>\n\t    public PasswordConfig()\n\t    {\n\t        \n\t    }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/PasswordManagement/Constants.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.PasswordManagement\n{\n\t/// <summary>\n\t/// Constants\n\t/// </summary>\n\tpublic class PasswordManagementConstants\n\t{\n\t\t/// <summary>\n\t\t/// Generic boolean value change constant\n\t\t/// </summary>\n\t\tpublic const ushort BoolValueChange = 1;\n\t\t/// <summary>\n\t\t/// Evaluated boolean change constant\n\t\t/// </summary>\n\t\tpublic const ushort PasswordInitializedChange = 2;\n\t\t/// <summary>\n\t\t/// Update busy change const\n\t\t/// </summary>\n\t\tpublic const ushort PasswordUpdateBusyChange = 3;\n\t\t/// <summary>\n\t\t/// Password is valid change constant\n\t\t/// </summary>\n\t\tpublic const ushort PasswordValidationChange = 4;\n\t\t/// <summary>\n\t\t/// Password LED change constant\n\t\t/// </summary>\n\t\tpublic const ushort PasswordLedFeedbackChange = 5;\n\n\t\t/// <summary>\n\t\t/// Generic ushort value change constant\n\t\t/// </summary>\n\t\tpublic const ushort UshrtValueChange = 101;\n\t\t/// <summary>\n\t\t/// Password count\n\t\t/// </summary>\n\t\tpublic const ushort PasswordManagerCountChange = 102;\n\t\t/// <summary>\n\t\t/// Password selecte index change constant\n\t\t/// </summary>\n\t\tpublic const ushort PasswordSelectIndexChange = 103;\n\t\t/// <summary>\n\t\t/// Password length\n\t\t/// </summary>\n\t\tpublic const ushort PasswordLengthChange = 104;\n\t\t\n\t\t/// <summary>\n\t\t/// Generic string value change constant\n\t\t/// </summary>\n\t\tpublic const ushort StringValueChange = 201;\t\t\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/PasswordManagement/PasswordClient.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Core.PasswordManagement\n{\n /// <summary>\n /// Represents a PasswordClient\n /// </summary>\n\tpublic class PasswordClient\n\t{\n\t\t/// <summary>\n\t\t/// Password selected\n\t\t/// </summary>\n\t\tpublic string Password { get; set; }\n\t\t/// <summary>\n\t\t/// Password selected key\n\t\t/// </summary>\n\t\tpublic ushort Key { get; set; }\n\t\t/// <summary>\n\t\t/// Used to build the password entered by the user\n\t\t/// </summary>\n\t\tpublic string PasswordToValidate { get; set; }\n\n\t\t/// <summary>\n\t\t/// Boolean event \n\t\t/// </summary>\n\t\tpublic event EventHandler<BoolChangeEventArgs> BoolChange;\n\t\t/// <summary>\n\t\t/// Ushort event\n\t\t/// </summary>\n\t\tpublic event EventHandler<UshrtChangeEventArgs> UshrtChange;\n\t\t/// <summary>\n\t\t/// String event\n\t\t/// </summary>\n\t\tpublic event EventHandler<StringChangeEventArgs> StringChange;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic PasswordClient()\n\t\t{\n\t\t\tPasswordManager.PasswordChange += new EventHandler<StringChangeEventArgs>(PasswordManager_PasswordChange);\n\t\t}\t\t\n\n\t\t/// <summary>\n\t\t/// Initialize method\n\t\t/// </summary>\n\t\tpublic void Initialize()\n\t\t{\n\t\t\tOnBoolChange(false, 0, PasswordManagementConstants.PasswordInitializedChange);\n\n\t\t\tPassword = \"\";\n\t\t\tPasswordToValidate = \"\";\n\n\t\t\tOnUshrtChange((ushort)PasswordManager.Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);\n\t\t\tOnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Retrieve password by index\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n  /// <summary>\n  /// GetPasswordByIndex method\n  /// </summary>\n\t\tpublic void GetPasswordByIndex(ushort key)\n\t\t{\n\t\t\tOnUshrtChange((ushort)PasswordManager.Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);\n\n\t\t\tKey = key;\n\n\t\t\tvar pw = PasswordManager.Passwords[Key];\n\t\t\tif (pw == null)\n\t\t\t{\n\t\t\t\tOnUshrtChange(0, 0, PasswordManagementConstants.PasswordLengthChange);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tPassword = pw;\n\t\t\tOnUshrtChange((ushort)Password.Length, 0, PasswordManagementConstants.PasswordLengthChange);\n\t\t\tOnUshrtChange(key, 0, PasswordManagementConstants.PasswordSelectIndexChange);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Password validation method\n\t\t/// </summary>\n\t\t/// <param name=\"password\"></param>\n  /// <summary>\n  /// ValidatePassword method\n  /// </summary>\n\t\tpublic void ValidatePassword(string password)\n\t\t{\n\t\t\tif (string.IsNullOrEmpty(password))\n\t\t\t\treturn;\n\n\t\t\tif (string.Equals(Password, password))\n\t\t\t\tOnBoolChange(true, 0, PasswordManagementConstants.PasswordValidationChange);\n\t\t\telse\n\t\t\t\tOnBoolChange(false, 0, PasswordManagementConstants.PasswordValidationChange);\n\n\t\t\tClearPassword();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Builds the user entered passwrod string, will attempt to validate the user entered\n\t\t/// password against the selected password when the length of the 2 are equal\n\t\t/// </summary>\n\t\t/// <param name=\"data\"></param>\n  /// <summary>\n  /// BuildPassword method\n  /// </summary>\n\t\tpublic void BuildPassword(string data)\n\t\t{\n\t\t\tPasswordToValidate = String.Concat(PasswordToValidate, data);\n\t\t\tOnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedFeedbackChange);\n\n\t\t\tif (PasswordToValidate.Length == Password.Length)\n\t\t\t\tValidatePassword(PasswordToValidate);\n\t\t}\n\n  /// <summary>\n  /// ClearPassword method\n  /// </summary>\n\t\tpublic void ClearPassword()\n\t\t{\n\t\t\tPasswordToValidate = \"\";\n\t\t\tOnBoolChange(false, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedFeedbackChange);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected boolean change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnBoolChange(bool state, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = BoolChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new BoolChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tBoolChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected ushort change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnUshrtChange(ushort value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = UshrtChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new UshrtChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tUshrtChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected string change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnStringChange(string value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = StringChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new StringChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tStringChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// If password changes while selected change event will be notifed and update the client\n\t\t/// </summary>\n\t\t/// <param name=\"sender\"></param>\n\t\t/// <param name=\"args\"></param>\n\t\tprotected void PasswordManager_PasswordChange(object sender, StringChangeEventArgs args)\n\t\t{\n\t\t\t//throw new NotImplementedException();\n\t\t\tif (Key == args.Index)\n\t\t\t{\n\t\t\t\t//PasswordSelectedKey = args.Index;\n\t\t\t\t//PasswordSelected = args.StringValue;\n\t\t\t\tGetPasswordByIndex(args.Index);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/PasswordManagement/PasswordManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.PasswordManagement\n{\n /// <summary>\n /// Represents a PasswordManager\n /// </summary>\n\tpublic class PasswordManager\n\t{\n\t\t/// <summary>\n\t\t/// Public dictionary of known passwords\n\t\t/// </summary>\n\t\tpublic static Dictionary<uint, string> Passwords = new Dictionary<uint, string>();\n\t\t/// <summary>\n\t\t/// Private dictionary, used when passwords are updated\n\t\t/// </summary>\n\t\tprivate Dictionary<uint, string> _passwords = new Dictionary<uint, string>();\n\n\t\t/// <summary>\n\t\t/// Timer used to wait until password changes have stopped before updating the dictionary\n\t\t/// </summary>\n\t\tCTimer PasswordTimer;\n\t\t/// <summary>\n\t\t/// Timer length\n\t\t/// </summary>\n\t\tpublic long PasswordTimerElapsedMs = 5000;\n\n\t\t/// <summary>\n\t\t/// Boolean event \n\t\t/// </summary>\n\t\tpublic event EventHandler<BoolChangeEventArgs> BoolChange;\n\t\t/// <summary>\n\t\t/// Ushort event\n\t\t/// </summary>\n\t\tpublic event EventHandler<UshrtChangeEventArgs> UshrtChange;\n\t\t/// <summary>\n\t\t/// String event\n\t\t/// </summary>\n\t\tpublic event EventHandler<StringChangeEventArgs> StringChange;\n\t\t/// <summary>\n\t\t/// Event to notify clients of an updated password at the specified index (uint)\n\t\t/// </summary>\n\t\tpublic static event EventHandler<StringChangeEventArgs> PasswordChange;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic PasswordManager()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Initialize password manager\n\t\t/// </summary>\n\t\tpublic void Initialize()\n\t\t{\n\t\t\tif (Passwords == null)\n\t\t\t\tPasswords = new Dictionary<uint, string>();\n\n\t\t\tif (_passwords == null)\n\t\t\t\t_passwords = new Dictionary<uint, string>();\n\n\t\t\tOnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Updates password stored in the dictonary\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"password\"></param>\n  /// <summary>\n  /// UpdatePassword method\n  /// </summary>\n\t\tpublic void UpdatePassword(ushort key, string password)\n\t\t{\n\t\t\t// validate the parameters\n\t\t\tif (key > 0 && string.IsNullOrEmpty(password))\n\t\t\t{\n\t\t\t\tDebug.Console(1, string.Format(\"PasswordManager.UpdatePassword: key [{0}] or password are not valid\", key, password));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\t// if key exists, update the value\n\t\t\t\tif(_passwords.ContainsKey(key))\n\t\t\t\t\t_passwords[key] = password;\n\t\t\t\t// else add the key & value\n\t\t\t\telse\n\t\t\t\t\t_passwords.Add(key, password);\n\t\t\t\t\n\t\t\t\tDebug.Console(1, string.Format(\"PasswordManager.UpdatePassword: _password[{0}] = {1}\", key, _passwords[key]));\n\n\t\t\t\tif (PasswordTimer == null)\n\t\t\t\t{\n\t\t\t\t\tPasswordTimer = new CTimer((o) => PasswordTimerElapsed(), PasswordTimerElapsedMs);\n\t\t\t\t\tDebug.Console(1, string.Format(\"PasswordManager.UpdatePassword: CTimer Started\"));\n\t\t\t\t\tOnBoolChange(true, 0, PasswordManagementConstants.PasswordUpdateBusyChange);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tPasswordTimer.Reset(PasswordTimerElapsedMs);\n\t\t\t\t\tDebug.Console(1, string.Format(\"PasswordManager.UpdatePassword: CTimer Reset\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"PasswordManager.UpdatePassword key-value[{0}, {1}] failed:\\r{2}\", key, password, e);\n\t\t\t\tDebug.Console(1, msg);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// CTimer callback function\n\t\t/// </summary>\n\t\tprivate void PasswordTimerElapsed()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tPasswordTimer.Stop();\n\t\t\t\tDebug.Console(1, string.Format(\"PasswordManager.PasswordTimerElapsed: CTimer Stopped\"));\n\t\t\t\tOnBoolChange(false, 0, PasswordManagementConstants.PasswordUpdateBusyChange);\n\t\t\t\tforeach (var pw in _passwords)\n\t\t\t\t{\n\t\t\t\t\t// if key exists, continue\n\t\t\t\t\tif (Passwords.ContainsKey(pw.Key))\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.Console(1, string.Format(\"PasswordManager.PasswordTimerElapsed: pw.key[{0}] = {1}\", pw.Key, pw.Value));\n\t\t\t\t\t\tif (Passwords[pw.Key] != _passwords[pw.Key])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPasswords[pw.Key] = _passwords[pw.Key];\n\t\t\t\t\t\t\tDebug.Console(1, string.Format(\"PasswordManager.PasswordTimerElapsed: Updated Password[{0} = {1}\", pw.Key, Passwords[pw.Key]));\n\t\t\t\t\t\t\tOnPasswordChange(Passwords[pw.Key], (ushort)pw.Key, PasswordManagementConstants.StringValueChange);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// else add the key & value\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tPasswords.Add(pw.Key, pw.Value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tOnUshrtChange((ushort)Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"PasswordManager.PasswordTimerElapsed failed:\\r{0}\", e);\n\t\t\t\tDebug.Console(1, msg);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Method to change the default timer value, (default 5000ms/5s)\n\t\t/// </summary>\n\t\t/// <param name=\"time\"></param>\n  /// <summary>\n  /// PasswordTimerMs method\n  /// </summary>\n\t\tpublic void PasswordTimerMs(ushort time)\n\t\t{\n\t\t\tPasswordTimerElapsedMs = Convert.ToInt64(time);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Helper method for debugging to see what passwords are in the lists\n\t\t/// </summary>\n\t\tpublic void ListPasswords()\n\t\t{\n\t\t\tDebug.Console(0, \"PasswordManager.ListPasswords:\\r\");\n\t\t\tforeach (var pw in Passwords)\n\t\t\t\tDebug.Console(0, \"Passwords[{0}]: {1}\\r\", pw.Key, pw.Value);\n\t\t\tDebug.Console(0, \"\\n\");\n\t\t\tforeach (var pw in _passwords)\n\t\t\t\tDebug.Console(0, \"_passwords[{0}]: {1}\\r\", pw.Key, pw.Value);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected boolean change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnBoolChange(bool state, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = BoolChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new BoolChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tBoolChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected ushort change event handler\n\t\t/// </summary>\n        /// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnUshrtChange(ushort value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = UshrtChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new UshrtChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tUshrtChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected string change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnStringChange(string value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = StringChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new StringChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tStringChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected password change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnPasswordChange(string value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = PasswordChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new StringChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tPasswordChange(this, args);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/PepperDash.Core.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <ProjectType>Library</ProjectType>\n  </PropertyGroup>\n  <PropertyGroup>\n    <RootNamespace>PepperDash.Core</RootNamespace>\n    <AssemblyName>PepperDashCore</AssemblyName>\n    <TargetFramework>net472</TargetFramework>\n    <Deterministic>true</Deterministic>\n    <NeutralLanguage>en</NeutralLanguage>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n    <SignAssembly>False</SignAssembly>\n    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>\n    <Title>PepperDash Core</Title>\n    <Company>PepperDash Technologies</Company>\n    <RepositoryType>git</RepositoryType>\n    <RepositoryUrl>https://github.com/PepperDash/PepperDashCore</RepositoryUrl>\n    <PackageTags>crestron;4series;</PackageTags>   \n    <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>        \n    <InformationalVersion>$(Version)</InformationalVersion>    \n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>    \n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n    <DefineConstants>TRACE;DEBUG;SERIES4</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <DocumentationFile>bin\\4Series\\$(Configuration)\\PepperDashCore.xml</DocumentationFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Remove=\"lib\\**\" />\n    <Compile Remove=\"Properties\\**\" />\n    <EmbeddedResource Remove=\"lib\\**\" />\n    <EmbeddedResource Remove=\"Properties\\**\" />\n    <None Remove=\"lib\\**\" />\n    <None Remove=\"Properties\\**\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Net.Http\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"BouncyCastle.Cryptography\" Version=\"2.4.0\" />\n    <PackageReference Include=\"Crestron.SimplSharp.SDK.Library\" Version=\"2.21.90\" />\n    <PackageReference Include=\"Serilog\" Version=\"3.1.1\" />\n    <PackageReference Include=\"Serilog.Expressions\" Version=\"4.0.0\" />\n    <PackageReference Include=\"Serilog.Formatting.Compact\" Version=\"2.0.0\" />\n    <PackageReference Include=\"Serilog.Sinks.Console\" Version=\"5.0.1\" />\n    <PackageReference Include=\"Serilog.Sinks.File\" Version=\"5.0.0\" />\n    <PackageReference Include=\"SSH.NET\" Version=\"2024.2.0\" />\n    <PackageReference Include=\"WebSocketSharp\" Version=\"1.0.3-rc11\" />\n  </ItemGroup>\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'net6'\">\n\t  <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Remove=\"Comm\\._GenericSshClient.cs\" />\n    <Compile Remove=\"Comm\\._GenericTcpIpClient.cs\" />\n    <Compile Remove=\"Comm\\DynamicTCPServer.cs\" />\n    <Compile Remove=\"PasswordManagement\\OLD-ARRAY-Config.cs\" />\n    <Compile Remove=\"PasswordManagement\\OLD-ARRAY-PasswordClient.cs\" />\n    <Compile Remove=\"PasswordManagement\\OLD-ARRAY-PasswordManager.cs\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "src/PepperDash.Core/PepperDashCore.build/net472/PepperDashCore.props",
    "content": "<Project>\n  <PropertyGroup>\n    <Version>2.0.0-local</Version>    \n    <Authors>PepperDash Technologies</Authors>\n    <Company>PepperDash Technologies</Company>\n    <Product>PepperDash Essentials</Product>\n    <Copyright>Copyright ©  2025</Copyright>    \n    <RepositoryType>git</RepositoryType>\n    <PackageTags>Crestron; 4series</PackageTags>\n    <PackageOutputPath>../../output</PackageOutputPath>    \n    <PackageLicenseFile>LICENSE.md</PackageLicenseFile>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <None Include=\"..\\..\\LICENSE.md\" Pack=\"true\" PackagePath=\"\"/>\n    <None Include=\"..\\..\\README.md\" Pack=\"true\" PackagePath=\"\"/>\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "src/PepperDash.Core/PepperDashCore.build/net472/PepperDashCore.targets",
    "content": "<Project>\n  <ItemGroup>\n    <None Include=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz\" Condition=\"$(ProjectType) == 'Program'\">\n      <Pack>true</Pack>\n      <PackagePath>build;</PackagePath>\n    </None>\n    <None Include=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz\" Condition=\"$(ProjectType) == 'ProgramLibrary'\">\n      <Pack>true</Pack>\n      <PackagePath>build;</PackagePath>\n    </None>\n     <None Include=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).clz\" Condition=\"$(ProjectType) == 'Library'\">\n      <Pack>true</Pack>\n      <PackagePath>build;</PackagePath>\n    </None>\n  </ItemGroup>\n  <PropertyGroup Condition=\"$(ProjectType) == 'ProgramLibrary'\">\n    <FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz</FileName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"$(ProjectType) == 'Program'\">\n    <FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName>\n  </PropertyGroup>\n  <Target Name=\"DeleteCPLZ\" BeforeTargets=\"PreBuildEvent\" Condition=\"$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != '' And Exists($(FileName))\">\n    <Delete Files=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz\">\n      <Output TaskParameter=\"DeletedFiles\" ItemName=\"DeletedList\"/>\n    </Delete>\n    <Message Text=\"Deleted files: '@(DeletedList)'\" />\n  </Target>\n  <Target Name=\"CreateCPLZ\" AfterTargets=\"Build\" Condition=\"$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''\" DependsOnTargets=\"DeleteCPLZ\">\n    <Message Text=\"Creating CPLZ $(TargetDir)\"></Message>\n    <MakeDir Directories=\"$(PackageOutputPath)\" Condition=\"!Exists($(PackageOutputPath))\" />    \n    <ZipDirectory SourceDirectory=\"$(TargetDir)\" DestinationFile=\"$(PackageOutputPath)\\$(TargetName).$(Version).$(TargetFramework).cplz\" Overwrite=\"true\"/>\n    <Copy SourceFiles=\"$(PackageOutputPath)\\$(TargetName).$(Version).$(TargetFramework).cplz\" DestinationFiles=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz\" />\n  </Target>\n  <Target Name=\"DeleteCPZ\" BeforeTargets=\"PreBuildEvent\" Condition=\"$(ProjectType) == 'Program' And $(TargetDir) != '' And Exists($(FileName))\">\n    <Delete Files=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz\">\n      <Output TaskParameter=\"DeletedFiles\" ItemName=\"DeletedList\"/>\n    </Delete>\n    <Message Text=\"Deleted files: '@(DeletedList)'\" />\n  </Target>\n  <Target Name=\"Copy CPZ\" AfterTargets=\"SimplSharpPostProcess\" Condition=\"($(ProjectType) == 'Program' And ( '$(TargetFramework)' != 'net6.0' ) And ( '$(TargetFramework)' != 'net8.0' ))\">\n    <Message Text=\"Copying CPZ\"></Message>\n    <Move SourceFiles=\"$(TargetDir)$(TargetName).cpz\" DestinationFiles=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz\" />\n    <Copy SourceFiles=\"$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz\" DestinationFiles=\"$(PackageOutputPath)\\$(TargetName).$(Version).$(TargetFramework).cpz\" />\n  </Target>\n  <Target Name=\"Copy CLZ\" AfterTargets=\"SimplSharpPostProcess\" Condition=\"$(ProjectType) == 'Library'\">>\n    <Message Text=\"Copying CLZ\"></Message>\n    <Move SourceFiles=\"$(TargetDir)\\$(TargetName).clz\" DestinationFiles=\"$(TargetDir)\\$(TargetName).$(Version).clz\"/>\n    <Copy SourceFiles=\"$(TargetDir)\\$(TargetName).$(Version).clz\" DestinationFiles=\"$(PackageOutputPath)\\$(TargetName).$(Version).clz\"/>\n  </Target>\n  <Target Name=\"SimplSharpNewtonsoft\" BeforeTargets=\"FindReferenceAssembliesForReferences;ResolveReferences\">\n    <ItemGroup>\n      <ReferencePath Condition=\"'%(FileName)' == 'Newtonsoft.Json.Compact'\">\n        <Aliases>doNotUse</Aliases>\n      </ReferencePath>\n    </ItemGroup>\n  </Target>\n</Project>\n"
  },
  {
    "path": "src/PepperDash.Core/Properties/ControlSystem.cfg",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ControlSystem>\n  <Name>MC3 SSH</Name>\n  <Address>ssh 10.0.0.15</Address>\n  <ProgramSlot>Program01</ProgramSlot>\n  <Storage>Internal Flash</Storage>\n</ControlSystem>"
  },
  {
    "path": "src/PepperDash.Core/SystemInfo/EventArgs and Constants.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.SystemInfo\n{\n\t/// <summary>\n\t/// Constants \n\t/// </summary>\n\tpublic class SystemInfoConstants\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort BoolValueChange = 1;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort CompleteBoolChange = 2;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort BusyBoolChange = 3;\n        \n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort UshortValueChange = 101;\n\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort StringValueChange = 201;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort ConsoleResponseChange = 202;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort ProcessorUptimeChange = 203;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort ProgramUptimeChange = 204;\n\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic const ushort ObjectChange = 301;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort ProcessorConfigChange = 302;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort EthernetConfigChange = 303;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort ControlSubnetConfigChange = 304;\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public const ushort ProgramConfigChange = 305;\n\t}\n\n /// <summary>\n /// Represents a ProcessorChangeEventArgs\n /// </summary>\n\tpublic class ProcessorChangeEventArgs : EventArgs\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ProcessorInfo Processor { get; set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public ushort Type { get; set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public ushort Index { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ProcessorChangeEventArgs()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\tpublic ProcessorChangeEventArgs(ProcessorInfo processor, ushort type)\n\t\t{\n\t\t\tProcessor = processor;\n\t\t\tType = type;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ProcessorChangeEventArgs(ProcessorInfo processor, ushort type, ushort index)\n\t\t{\n\t\t\tProcessor = processor;\n\t\t\tType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a EthernetChangeEventArgs\n /// </summary>\n\tpublic class EthernetChangeEventArgs : EventArgs\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic EthernetInfo Adapter { get; set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public ushort Type { get; set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public ushort Index { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic EthernetChangeEventArgs()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n        /// <param name=\"ethernet\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tpublic EthernetChangeEventArgs(EthernetInfo ethernet, ushort type)\n\t\t{\n\t\t\tAdapter = ethernet;\n\t\t\tType = type;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n        /// <param name=\"ethernet\"></param>\n\t\t/// <param name=\"type\"></param>\n        /// <param name=\"index\"></param>\n\t\tpublic EthernetChangeEventArgs(EthernetInfo ethernet, ushort type, ushort index)\n\t\t{\n\t\t\tAdapter = ethernet;\n\t\t\tType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a ControlSubnetChangeEventArgs\n /// </summary>\n\tpublic class ControlSubnetChangeEventArgs : EventArgs\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ControlSubnetInfo Adapter { get; set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public ushort Type { get; set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public ushort Index { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ControlSubnetChangeEventArgs()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\tpublic ControlSubnetChangeEventArgs(ControlSubnetInfo controlSubnet, ushort type)\n\t\t{\n\t\t\tAdapter = controlSubnet;\n\t\t\tType = type;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n\t\tpublic ControlSubnetChangeEventArgs(ControlSubnetInfo controlSubnet, ushort type, ushort index)\n\t\t{\n\t\t\tAdapter = controlSubnet;\n\t\t\tType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a ProgramChangeEventArgs\n /// </summary>\n\tpublic class ProgramChangeEventArgs : EventArgs\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ProgramInfo Program { get; set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public ushort Type { get; set; }\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n        public ushort Index { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ProgramChangeEventArgs()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n        /// <param name=\"program\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tpublic ProgramChangeEventArgs(ProgramInfo program, ushort type)\n\t\t{\n\t\t\tProgram = program;\n\t\t\tType = type;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor overload\n\t\t/// </summary>\n        /// <param name=\"program\"></param>\n\t\t/// <param name=\"type\"></param>\n        /// <param name=\"index\"></param>\n\t\tpublic ProgramChangeEventArgs(ProgramInfo program, ushort type, ushort index)\n\t\t{\n\t\t\tProgram = program;\n\t\t\tType = type;\n\t\t\tIndex = index;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/SystemInfo/SystemInfoConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.SystemInfo\n{\n\t/// <summary>\n\t/// Processor info class\n\t/// </summary>\n\tpublic class ProcessorInfo\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Model { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string SerialNumber { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Firmware { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string FirmwareDate { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string OsVersion { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string RuntimeEnvironment { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string DevicePlatform { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string ModuleDirectory { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string LocalTimeZone { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string ProgramIdTag { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ProcessorInfo()\n\t\t{\n\t\t\t\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Ethernet info class\n\t/// </summary>\n\tpublic class EthernetInfo\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort DhcpIsOn { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Hostname { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string MacAddress { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string IpAddress { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Subnet { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Gateway { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Dns1 { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Dns2 { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Dns3 { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Domain { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic EthernetInfo()\n\t\t{\n\t\t\t\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Control subnet info class\n\t/// </summary>\n\tpublic class ControlSubnetInfo\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort Enabled { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic ushort IsInAutomaticMode { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string MacAddress { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string IpAddress { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Subnet { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string RouterPrefix { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ControlSubnetInfo()\n\t\t{\n\t\t\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Program info class\n\t/// </summary>\n\tpublic class ProgramInfo\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Name { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Header { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string System { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string ProgramIdTag { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string CompileTime { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Database { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Environment { get; set; }\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic string Programmer { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic ProgramInfo()\n\t\t{\n\t\t\t\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/SystemInfo/SystemInfoToSimpl.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.SystemInfo\n{\n\t/// <summary>\n\t/// System Info class\n\t/// </summary>\n\tpublic class SystemInfoToSimpl\n\t{\n        /// <summary>\n        /// Notifies of bool change\n        /// </summary>\n\t\tpublic event EventHandler<BoolChangeEventArgs> BoolChange;\n        /// <summary>\n        /// Notifies of string change\n        /// </summary>\n\t\tpublic event EventHandler<StringChangeEventArgs> StringChange;\n\n        /// <summary>\n        /// Notifies of processor change\n        /// </summary>\n\t\tpublic event EventHandler<ProcessorChangeEventArgs> ProcessorChange;\n        /// <summary>\n        /// Notifies of ethernet change\n        /// </summary>\n\t\tpublic event EventHandler<EthernetChangeEventArgs> EthernetChange;\n        /// <summary>\n        /// Notifies of control subnet change\n        /// </summary>\n\t\tpublic event EventHandler<ControlSubnetChangeEventArgs> ControlSubnetChange;\n        /// <summary>\n        /// Notifies of program change\n        /// </summary>\n\t\tpublic event EventHandler<ProgramChangeEventArgs> ProgramChange;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic SystemInfoToSimpl()\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the current processor info\n\t\t/// </summary>\n\t\tpublic void GetProcessorInfo()\n\t\t{\n\t\t\tOnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar processor = new ProcessorInfo();\n\t\t\t\tprocessor.Model = InitialParametersClass.ControllerPromptName;\n\t\t\t\tprocessor.SerialNumber = CrestronEnvironment.SystemInfo.SerialNumber;\n\t\t\t\tprocessor.ModuleDirectory = InitialParametersClass.ProgramDirectory.ToString();\n\t\t\t\tprocessor.ProgramIdTag = InitialParametersClass.ProgramIDTag;\n\t\t\t\tprocessor.DevicePlatform = CrestronEnvironment.DevicePlatform.ToString();\n\t\t\t\tprocessor.OsVersion = CrestronEnvironment.OSVersion.Version.ToString();\n\t\t\t\tprocessor.RuntimeEnvironment = CrestronEnvironment.RuntimeEnvironment.ToString();\n\t\t\t\tprocessor.LocalTimeZone = CrestronEnvironment.GetTimeZone().Offset;\n\n\t\t\t\t// Does not return firmware version matching a \"ver\" command\n\t\t\t\t// returns the \"ver -v\" 'CAB' version\n\t\t\t\t// example return ver -v: \n\t\t\t\t//\t\tRMC3 Cntrl Eng [v1.503.3568.25373 (Oct 09 2018), #4001E302] @E-00107f4420f0\n\t\t\t\t//\t\tBuild: 14:05:46  Oct 09 2018 (3568.25373)\n\t\t\t\t//\t\tCab: 1.503.0070\n\t\t\t\t//\t\tApplications:  1.0.6855.21351\n\t\t\t\t//\t\tUpdater: 1.4.24\n\t\t\t\t//\t\tBootloader: 1.22.00\n\t\t\t\t//\t\tRMC3-SetupProgram: 1.003.0011\n\t\t\t\t//\t\tIOPVersion: FPGA [v09] slot:7\n\t\t\t\t//\t\tPUF: Unknown\n\t\t\t\t//Firmware = CrestronEnvironment.OSVersion.Firmware;\n\t\t\t\t//Firmware = InitialParametersClass.FirmwareVersion;\n\n\t\t\t\t// Use below logic to get actual firmware ver, not the 'CAB' returned by the above\n\t\t\t\t// matches console return of a \"ver\" and on SystemInfo page\n\t\t\t\t// example return ver: \n\t\t\t\t//\t\tRMC3 Cntrl Eng [v1.503.3568.25373 (Oct 09 2018), #4001E302] @E-00107f4420f0\n\t\t\t\tvar response = \"\";\n\t\t\t\tCrestronConsole.SendControlSystemCommand(\"ver\", ref response);\n\t\t\t\tprocessor.Firmware = ParseConsoleResponse(response, \"Cntrl Eng\", \"[\", \"(\");\n\t\t\t\tprocessor.FirmwareDate = ParseConsoleResponse(response, \"Cntrl Eng\", \"(\", \")\");\n\n\t\t\t\tOnProcessorChange(processor, 0, SystemInfoConstants.ProcessorConfigChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"GetProcessorInfo failed: {0}\", e.Message);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\t\t\t}\t\t\t\n\n\t\t\tOnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);\n\t\t}\n\n  /// <summary>\n  /// GetEthernetInfo method\n  /// </summary>\n\t\tpublic void GetEthernetInfo()\n\t\t{\n\t\t\tOnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);\n\n\t\t\tvar adapter = new EthernetInfo();\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\t// get lan adapter id\n\t\t\t\tvar adapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);\n\n\t\t\t\t// get lan adapter info\n\t\t\t\tvar dhcpState = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterId);\n\t\t\t\tif (!string.IsNullOrEmpty(dhcpState))\n\t\t\t\t\tadapter.DhcpIsOn = (ushort)(dhcpState.ToLower().Contains(\"on\") ? 1 : 0);\n\n\t\t\t\tadapter.Hostname = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterId);\n\t\t\t\tadapter.MacAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterId);\n\t\t\t\tadapter.IpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterId);\n\t\t\t\tadapter.Subnet = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterId);\n\t\t\t\tadapter.Gateway = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterId);\n\t\t\t\tadapter.Domain = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterId);\n\n\t\t\t\t// returns comma seperate list of dns servers with trailing comma\n\t\t\t\t// example return: \"8.8.8.8 (DHCP),8.8.4.4 (DHCP),\"\n\t\t\t\tstring dns = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterId);\n\t\t\t\tif (dns.Contains(\",\"))\n\t\t\t\t{\n\t\t\t\t\tstring[] dnsList = dns.Split(',');\n\t\t\t\t\tfor (var i = 0; i < dnsList.Length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(i == 0)\n\t\t\t\t\t\t\tadapter.Dns1 = !string.IsNullOrEmpty(dnsList[0]) ? dnsList[0] : \"0.0.0.0\";\n\t\t\t\t\t\tif(i == 1)\n\t\t\t\t\t\t\tadapter.Dns2 = !string.IsNullOrEmpty(dnsList[1]) ? dnsList[1] : \"0.0.0.0\";\n\t\t\t\t\t\tif(i == 2)\n\t\t\t\t\t\t\tadapter.Dns3 = !string.IsNullOrEmpty(dnsList[2]) ? dnsList[2] : \"0.0.0.0\";\n\t\t\t\t\t}\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tadapter.Dns1 = !string.IsNullOrEmpty(dns) ? dns : \"0.0.0.0\";\n\t\t\t\t\tadapter.Dns2 = \"0.0.0.0\";\n\t\t\t\t\tadapter.Dns3 = \"0.0.0.0\";\n\t\t\t\t}\n\n\t\t\t\tOnEthernetInfoChange(adapter, 0, SystemInfoConstants.EthernetConfigChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"GetEthernetInfo failed: {0}\", e.Message);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\t\t\t}\n\n\t\t\tOnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);\n\t\t}\n\n  /// <summary>\n  /// GetControlSubnetInfo method\n  /// </summary>\n\t\tpublic void GetControlSubnetInfo()\n\t\t{\n\t\t\tOnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);\n\n\t\t\tvar adapter = new ControlSubnetInfo();\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\t// get cs adapter id\n\t\t\t\tvar adapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);\n\t\t\t\tif (!adapterId.Equals(EthernetAdapterType.EthernetUnknownAdapter))\n\t\t\t\t{\n\t\t\t\t\tadapter.Enabled = 1;\n\t\t\t\t\tadapter.IsInAutomaticMode = (ushort)(CrestronEthernetHelper.IsControlSubnetInAutomaticMode ? 1 : 0);\n\t\t\t\t\tadapter.MacAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterId);\n\t\t\t\t\tadapter.IpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterId);\n\t\t\t\t\tadapter.Subnet = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterId);\n\t\t\t\t\tadapter.RouterPrefix = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CONTROL_SUBNET_ROUTER_PREFIX, adapterId);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tadapter.Enabled = 0;\n\t\t\t\tadapter.IsInAutomaticMode = 0;\n\t\t\t\tadapter.MacAddress = \"NA\";\n\t\t\t\tadapter.IpAddress = \"NA\";\n\t\t\t\tadapter.Subnet = \"NA\";\n\t\t\t\tadapter.RouterPrefix = \"NA\";\n\n\t\t\t\tvar msg = string.Format(\"GetControlSubnetInfo failed: {0}\", e.Message);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\t\t\t}\n\n\t\t\tOnControlSubnetInfoChange(adapter, 0, SystemInfoConstants.ControlSubnetConfigChange);\n\t\t\tOnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the program info by index\n\t\t/// </summary>\n\t\t/// <param name=\"index\"></param>\n  /// <summary>\n  /// GetProgramInfoByIndex method\n  /// </summary>\n\t\tpublic void GetProgramInfoByIndex(ushort index)\n\t\t{\n\t\t\tif (index < 1 || index > 10)\n\t\t\t\treturn;\n\n\t\t\tOnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);\n\n\t\t\tvar program = new ProgramInfo();\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar response = \"\";\n\t\t\t\tCrestronConsole.SendControlSystemCommand(string.Format(\"progcomments:{0}\", index), ref response);\n\t\t\t\t\n\t\t\t\t// no program loaded or running\n\t\t\t\tif (response.Contains(\"Bad or Incomplete Command\"))\n\t\t\t\t{\n\t\t\t\t\tprogram.Name = \"\";\n\t\t\t\t\tprogram.System = \"\";\n\t\t\t\t\tprogram.Programmer = \"\";\n\t\t\t\t\tprogram.CompileTime = \"\";\n\t\t\t\t\tprogram.Database = \"\";\n\t\t\t\t\tprogram.Environment = \"\";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// SIMPL returns\n\t\t\t\t\tprogram.Name = ParseConsoleResponse(response, \"Program File\", \":\", \"\\x0D\");\n\t\t\t\t\tprogram.System = ParseConsoleResponse(response, \"System Name\", \":\", \"\\x0D\");\n\t\t\t\t\tprogram.ProgramIdTag = ParseConsoleResponse(response, \"Friendly Name\", \":\", \"\\x0D\");\n\t\t\t\t\tprogram.Programmer = ParseConsoleResponse(response, \"Programmer\", \":\", \"\\x0D\");\n\t\t\t\t\tprogram.CompileTime = ParseConsoleResponse(response, \"Compiled On\", \":\", \"\\x0D\");\n\t\t\t\t\tprogram.Database = ParseConsoleResponse(response, \"CrestronDB\", \":\", \"\\x0D\");\n\t\t\t\t\tprogram.Environment = ParseConsoleResponse(response, \"Source Env\", \":\", \"\\x0D\");\n\n\t\t\t\t\t// S# returns\n\t\t\t\t\tif (program.System.Length == 0)\n\t\t\t\t\t\tprogram.System = ParseConsoleResponse(response, \"Application Name\", \":\", \"\\x0D\");\n\t\t\t\t\tif (program.Database.Length == 0)\n\t\t\t\t\t\tprogram.Database = ParseConsoleResponse(response, \"PlugInVersion\", \":\", \"\\x0D\");\n\t\t\t\t\tif (program.Environment.Length == 0)\n\t\t\t\t\t\tprogram.Environment = ParseConsoleResponse(response, \"Program Tool\", \":\", \"\\x0D\");\n\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tOnProgramChange(program, index, SystemInfoConstants.ProgramConfigChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"GetProgramInfoByIndex failed: {0}\", e.Message);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\t\t\t}\n\n\t\t\tOnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);\n\t\t}\n\n  /// <summary>\n  /// RefreshProcessorUptime method\n  /// </summary>\n\t\tpublic void RefreshProcessorUptime()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tstring response = \"\";\n\t\t\t\tCrestronConsole.SendControlSystemCommand(\"uptime\", ref response);\n\t\t\t\tvar uptime = ParseConsoleResponse(response, \"running for\", \"running for\", \"\\x0D\");\n\t\t\t\tOnStringChange(uptime, 0, SystemInfoConstants.ProcessorUptimeChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"RefreshProcessorUptime failed:\\r{0}\", e.Message);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the program uptime, by index, and passes it to S+\n\t\t/// </summary>\n\t\t/// <param name=\"index\"></param>\n  /// <summary>\n  /// RefreshProgramUptimeByIndex method\n  /// </summary>\n\t\tpublic void RefreshProgramUptimeByIndex(int index)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tstring response = \"\";\n\t\t\t\tCrestronConsole.SendControlSystemCommand(string.Format(\"proguptime:{0}\", index), ref response);\n\t\t\t\tstring uptime = ParseConsoleResponse(response, \"running for\", \"running for\", \"\\x0D\");\n\t\t\t\tOnStringChange(uptime, (ushort)index, SystemInfoConstants.ProgramUptimeChange);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"RefreshProgramUptimebyIndex({0}) failed:\\r{1}\", index, e.Message);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sends command to console, passes response back using string change event\n\t\t/// </summary>\n\t\t/// <param name=\"cmd\"></param>\n  /// <summary>\n  /// SendConsoleCommand method\n  /// </summary>\n\t\tpublic void SendConsoleCommand(string cmd)\n\t\t{\n\t\t\tif (string.IsNullOrEmpty(cmd))\n\t\t\t\treturn;\n\n\t\t\tstring response = \"\";\n\t\t\tCrestronConsole.SendControlSystemCommand(cmd, ref response);\n\t\t\tif (!string.IsNullOrEmpty(response))\n\t\t\t{\n\t\t\t\tif (response.EndsWith(\"\\x0D\\\\x0A\"))\n\t\t\t\t\tresponse.Trim('\\n');\n\n\t\t\t\tOnStringChange(response, 0, SystemInfoConstants.ConsoleResponseChange);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// private method to parse console messages\n\t\t/// </summary>\n        /// <param name=\"data\"></param>\n\t\t/// <param name=\"line\"></param>\n        /// <param name=\"dataStart\"></param>\n        /// <param name=\"dataEnd\"></param>\n\t\t/// <returns></returns>\n\t\tprivate string ParseConsoleResponse(string data, string line, string dataStart, string dataEnd)\n\t\t{\n\t\t\tvar response = \"\";\n\n\t\t\tif (string.IsNullOrEmpty(data) || string.IsNullOrEmpty(line) || string.IsNullOrEmpty(dataStart) || string.IsNullOrEmpty(dataEnd))\n\t\t\t\treturn response;\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar linePos = data.IndexOf(line);\n\t\t\t\tvar startPos = data.IndexOf(dataStart, linePos) + dataStart.Length;\n\t\t\t\tvar endPos = data.IndexOf(dataEnd, startPos);\t\t\t\t\n\t\t\t\tresponse = data.Substring(startPos, endPos - startPos).Trim();\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"ParseConsoleResponse failed: {0}\", e.Message);\n\t\t\t\tCrestronConsole.PrintLine(msg);\n\t\t\t\t//ErrorLog.Error(msg);\n\t\t\t}\n\n\t\t\treturn response;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected boolean change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnBoolChange(bool state, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = BoolChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new BoolChangeEventArgs(state, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tBoolChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected string change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"value\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnStringChange(string value, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = StringChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new StringChangeEventArgs(value, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tStringChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Protected processor config change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"processor\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnProcessorChange(ProcessorInfo processor, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = ProcessorChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new ProcessorChangeEventArgs(processor, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tProcessorChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Ethernet change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"ethernet\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnEthernetInfoChange(EthernetInfo ethernet, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = EthernetChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new EthernetChangeEventArgs(ethernet, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tEthernetChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Control Subnet change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"ethernet\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnControlSubnetInfoChange(ControlSubnetInfo ethernet, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = ControlSubnetChange;\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new ControlSubnetChangeEventArgs(ethernet, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tControlSubnetChange(this, args);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Program change event handler\n\t\t/// </summary>\n\t\t/// <param name=\"program\"></param>\n\t\t/// <param name=\"index\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\tprotected void OnProgramChange(ProgramInfo program, ushort index, ushort type)\n\t\t{\n\t\t\tvar handler = ProgramChange;\n\n\t\t\tif (handler != null)\n\t\t\t{\n\t\t\t\tvar args = new ProgramChangeEventArgs(program, type);\n\t\t\t\targs.Index = index;\n\t\t\t\tProgramChange(this, args);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/Web/BouncyCertificate.cs",
    "content": "﻿using Crestron.SimplSharp;\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing Org.BouncyCastle.Asn1;\nusing Org.BouncyCastle.Asn1.X509;\nusing Org.BouncyCastle.Crypto;\nusing Org.BouncyCastle.Crypto.Generators;\nusing Org.BouncyCastle.Crypto.Prng;\nusing Org.BouncyCastle.Pkcs;\nusing Org.BouncyCastle.Security;\nusing Org.BouncyCastle.Utilities;\nusing Org.BouncyCastle.X509;\nusing X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;\nusing X509KeyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags;\nusing X509ContentType = System.Security.Cryptography.X509Certificates.X509ContentType;\nusing Org.BouncyCastle.Crypto.Operators;\nusing BigInteger = Org.BouncyCastle.Math.BigInteger;\nusing X509Certificate = Org.BouncyCastle.X509.X509Certificate;\n\nnamespace PepperDash.Core\n{\n    /// <summary>\n    /// Taken From https://github.com/rlipscombe/bouncy-castle-csharp/\n    /// </summary>\n    internal class BouncyCertificate\n    {\n        public string CertificatePassword { get; set; } = \"password\";\n        public X509Certificate2 LoadCertificate(string issuerFileName, string password)\n        {\n            // We need to pass 'Exportable', otherwise we can't get the private key.\n            var issuerCertificate = new X509Certificate2(issuerFileName, password, X509KeyStorageFlags.Exportable);\n            return issuerCertificate;\n        }\n\n        /// <summary>\n        /// IssueCertificate method\n        /// </summary>\n        public X509Certificate2 IssueCertificate(string subjectName, X509Certificate2 issuerCertificate, string[] subjectAlternativeNames, KeyPurposeID[] usages)\n        {\n            // It's self-signed, so these are the same.\n            var issuerName = issuerCertificate.Subject;\n\n            var random = GetSecureRandom();\n            var subjectKeyPair = GenerateKeyPair(random, 2048);\n\n            var issuerKeyPair = DotNetUtilities.GetKeyPair(issuerCertificate.PrivateKey);\n\n            var serialNumber = GenerateSerialNumber(random);\n            var issuerSerialNumber = new BigInteger(issuerCertificate.GetSerialNumber());\n\n            const bool isCertificateAuthority = false;\n            var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,\n                                                  subjectAlternativeNames, issuerName, issuerKeyPair,\n                                                  issuerSerialNumber, isCertificateAuthority,\n                                                  usages);\n            return ConvertCertificate(certificate, subjectKeyPair, random);\n        }\n\n        /// <summary>\n        /// CreateCertificateAuthorityCertificate method\n        /// </summary>\n        public X509Certificate2 CreateCertificateAuthorityCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)\n        {\n            // It's self-signed, so these are the same.\n            var issuerName = subjectName;\n\n            var random = GetSecureRandom();\n            var subjectKeyPair = GenerateKeyPair(random, 2048);\n\n            // It's self-signed, so these are the same.\n            var issuerKeyPair = subjectKeyPair;\n\n            var serialNumber = GenerateSerialNumber(random);\n            var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number.\n\n            const bool isCertificateAuthority = true;\n            var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,\n                                                  subjectAlternativeNames, issuerName, issuerKeyPair,\n                                                  issuerSerialNumber, isCertificateAuthority,\n                                                  usages);\n            return ConvertCertificate(certificate, subjectKeyPair, random);\n        }\n\n        /// <summary>\n        /// CreateSelfSignedCertificate method\n        /// </summary>\n        public X509Certificate2 CreateSelfSignedCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)\n        {\n            // It's self-signed, so these are the same.\n            var issuerName = subjectName;\n\n            var random = GetSecureRandom();\n            var subjectKeyPair = GenerateKeyPair(random, 2048);\n\n            // It's self-signed, so these are the same.\n            var issuerKeyPair = subjectKeyPair;\n\n            var serialNumber = GenerateSerialNumber(random);\n            var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number.\n\n            const bool isCertificateAuthority = false;\n            var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,\n                                                  subjectAlternativeNames, issuerName, issuerKeyPair,\n                                                  issuerSerialNumber, isCertificateAuthority,\n                                                  usages);\n            return ConvertCertificate(certificate, subjectKeyPair, random);\n        }\n\n        private SecureRandom GetSecureRandom()\n        {\n            // Since we're on Windows, we'll use the CryptoAPI one (on the assumption\n            // that it might have access to better sources of entropy than the built-in\n            // Bouncy Castle ones):\n            var randomGenerator = new CryptoApiRandomGenerator();\n            var random = new SecureRandom(randomGenerator);\n            return random;\n        }\n\n        private X509Certificate GenerateCertificate(SecureRandom random,\n                                                           string subjectName,\n                                                           AsymmetricCipherKeyPair subjectKeyPair,\n                                                           BigInteger subjectSerialNumber,\n                                                           string[] subjectAlternativeNames,\n                                                           string issuerName,\n                                                           AsymmetricCipherKeyPair issuerKeyPair,\n                                                           BigInteger issuerSerialNumber,\n                                                           bool isCertificateAuthority,\n                                                           KeyPurposeID[] usages)\n        {\n            var certificateGenerator = new X509V3CertificateGenerator();\n\n            certificateGenerator.SetSerialNumber(subjectSerialNumber);\n\n            var issuerDN = new X509Name(issuerName);\n            certificateGenerator.SetIssuerDN(issuerDN);\n\n            // Note: The subject can be omitted if you specify a subject alternative name (SAN).\n            var subjectDN = new X509Name(subjectName);\n            certificateGenerator.SetSubjectDN(subjectDN);\n\n            // Our certificate needs valid from/to values.\n            var notBefore = DateTime.UtcNow.Date;\n            var notAfter = notBefore.AddYears(2);\n\n            certificateGenerator.SetNotBefore(notBefore);\n            certificateGenerator.SetNotAfter(notAfter);\n\n            // The subject's public key goes in the certificate.\n            certificateGenerator.SetPublicKey(subjectKeyPair.Public);\n\n            AddAuthorityKeyIdentifier(certificateGenerator, issuerDN, issuerKeyPair, issuerSerialNumber);\n            AddSubjectKeyIdentifier(certificateGenerator, subjectKeyPair);\n            //AddBasicConstraints(certificateGenerator, isCertificateAuthority);\n\n            if (usages != null && usages.Any())\n                AddExtendedKeyUsage(certificateGenerator, usages);\n\n            if (subjectAlternativeNames != null && subjectAlternativeNames.Any())\n                AddSubjectAlternativeNames(certificateGenerator, subjectAlternativeNames);\n\n            // Set the signature algorithm. This is used to generate the thumbprint which is then signed\n            // with the issuer's private key. We'll use SHA-256, which is (currently) considered fairly strong.\n            const string signatureAlgorithm = \"SHA256WithRSA\";\n\n            // The certificate is signed with the issuer's private key.\n            ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private, random);\n            var certificate = certificateGenerator.Generate(signatureFactory);\n            return certificate;\n        }\n\n        /// <summary>\n        /// The certificate needs a serial number. This is used for revocation,\n        /// and usually should be an incrementing index (which makes it easier to revoke a range of certificates).\n        /// Since we don't have anywhere to store the incrementing index, we can just use a random number.\n        /// </summary>\n        /// <param name=\"random\"></param>\n        /// <returns></returns>\n        private BigInteger GenerateSerialNumber(SecureRandom random)\n        {\n            var serialNumber =\n                BigIntegers.CreateRandomInRange(\n                    BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);\n            return serialNumber;\n        }\n\n        /// <summary>\n        /// Generate a key pair.\n        /// </summary>\n        /// <param name=\"random\">The random number generator.</param>\n        /// <param name=\"strength\">The key length in bits. For RSA, 2048 bits should be considered the minimum acceptable these days.</param>\n        /// <returns></returns>\n        private AsymmetricCipherKeyPair GenerateKeyPair(SecureRandom random, int strength)\n        {\n            var keyGenerationParameters = new KeyGenerationParameters(random, strength);\n\n            var keyPairGenerator = new RsaKeyPairGenerator();\n            keyPairGenerator.Init(keyGenerationParameters);\n            var subjectKeyPair = keyPairGenerator.GenerateKeyPair();\n            return subjectKeyPair;\n        }\n\n        /// <summary>\n        /// Add the Authority Key Identifier. According to http://www.alvestrand.no/objectid/2.5.29.35.html, this\n        /// identifies the public key to be used to verify the signature on this certificate.\n        /// In a certificate chain, this corresponds to the \"Subject Key Identifier\" on the *issuer* certificate.\n        /// The Bouncy Castle documentation, at http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation,\n        /// shows how to create this from the issuing certificate. Since we're creating a self-signed certificate, we have to do this slightly differently.\n        /// </summary>\n        /// <param name=\"certificateGenerator\"></param>\n        /// <param name=\"issuerDN\"></param>\n        /// <param name=\"issuerKeyPair\"></param>\n        /// <param name=\"issuerSerialNumber\"></param>\n        private void AddAuthorityKeyIdentifier(X509V3CertificateGenerator certificateGenerator,\n                                                      X509Name issuerDN,\n                                                      AsymmetricCipherKeyPair issuerKeyPair,\n                                                      BigInteger issuerSerialNumber)\n        {\n            var authorityKeyIdentifierExtension =\n                new AuthorityKeyIdentifier(\n                    SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKeyPair.Public),\n                    new GeneralNames(new GeneralName(issuerDN)),\n            issuerSerialNumber);\n            certificateGenerator.AddExtension(\n                X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifierExtension);\n        }\n\n        /// <summary>\n        /// Add the \"Subject Alternative Names\" extension. Note that you have to repeat\n        /// the value from the \"Subject Name\" property.\n        /// </summary>\n        /// <param name=\"certificateGenerator\"></param>\n        /// <param name=\"subjectAlternativeNames\"></param>\n        private void AddSubjectAlternativeNames(X509V3CertificateGenerator certificateGenerator,\n                                                       IEnumerable<string> subjectAlternativeNames)\n        {\n            var subjectAlternativeNamesExtension =\n                new DerSequence(\n                    subjectAlternativeNames.Select(name => new GeneralName(GeneralName.DnsName, name))\n                                           .ToArray<Asn1Encodable>());\n            certificateGenerator.AddExtension(\n                X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);\n        }\n\n        /// <summary>\n        /// Add the \"Extended Key Usage\" extension, specifying (for example) \"server authentication\".\n        /// </summary>\n        /// <param name=\"certificateGenerator\"></param>\n        /// <param name=\"usages\"></param>\n        private void AddExtendedKeyUsage(X509V3CertificateGenerator certificateGenerator, KeyPurposeID[] usages)\n        {\n            certificateGenerator.AddExtension(\n                X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(usages));\n        }\n\n        /// <summary>\n        /// Add the \"Basic Constraints\" extension.\n        /// </summary>\n        /// <param name=\"certificateGenerator\"></param>\n        /// <param name=\"isCertificateAuthority\"></param>\n        private void AddBasicConstraints(X509V3CertificateGenerator certificateGenerator,\n                                                bool isCertificateAuthority)\n        {\n            certificateGenerator.AddExtension(\n                X509Extensions.BasicConstraints.Id, true, new BasicConstraints(isCertificateAuthority));\n        }\n\n        /// <summary>\n        /// Add the Subject Key Identifier.\n        /// </summary>\n        /// <param name=\"certificateGenerator\"></param>\n        /// <param name=\"subjectKeyPair\"></param>\n        private void AddSubjectKeyIdentifier(X509V3CertificateGenerator certificateGenerator,\n                                                    AsymmetricCipherKeyPair subjectKeyPair)\n        {\n            var subjectKeyIdentifierExtension =\n                new SubjectKeyIdentifier(\n                    SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public));\n            certificateGenerator.AddExtension(\n                X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifierExtension);\n        }\n\n        private X509Certificate2 ConvertCertificate(X509Certificate certificate,\n                                                           AsymmetricCipherKeyPair subjectKeyPair,\n                                                           SecureRandom random)\n        {\n            // Now to convert the Bouncy Castle certificate to a .NET certificate.\n            // See http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx\n            // ...but, basically, we create a PKCS12 store (a .PFX file) in memory, and add the public and private key to that.\n            var store = new Pkcs12StoreBuilder().Build();\n\n            // What Bouncy Castle calls \"alias\" is the same as what Windows terms the \"friendly name\".\n            string friendlyName = certificate.SubjectDN.ToString();\n\n            // Add the certificate.\n            var certificateEntry = new X509CertificateEntry(certificate);\n            store.SetCertificateEntry(friendlyName, certificateEntry);\n\n            // Add the private key.\n            store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });\n\n            // Convert it to an X509Certificate2 object by saving/loading it from a MemoryStream.\n            // It needs a password. Since we'll remove this later, it doesn't particularly matter what we use.\n\n            var stream = new MemoryStream();\n            store.Save(stream, CertificatePassword.ToCharArray(), random);\n\n            var convertedCertificate =\n                new X509Certificate2(stream.ToArray(),\n                                     CertificatePassword,\n                                     X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);\n            return convertedCertificate;\n        }\n\n        /// <summary>\n        /// WriteCertificate method\n        /// </summary>\n        public void WriteCertificate(X509Certificate2 certificate, string outputDirectory, string certName)\n        {\n            // This password is the one attached to the PFX file. Use 'null' for no password.\n            // Create PFX (PKCS #12) with private key\n            try\n            {\n                var pfx = certificate.Export(X509ContentType.Pfx, CertificatePassword);\n                File.WriteAllBytes(string.Format(\"{0}.pfx\", Path.Combine(outputDirectory, certName)), pfx);\n            }\n            catch (Exception ex)\n            {\n                CrestronConsole.PrintLine(string.Format(\"Failed to write x509 cert pfx\\r\\n{0}\", ex.Message));\n            }\n            // Create Base 64 encoded CER (public key only)\n            using (var writer = new StreamWriter($\"{Path.Combine(outputDirectory, certName)}.cer\", false))\n            {\n                try\n                {\n                    var contents = string.Format(\"-----BEGIN CERTIFICATE-----\\r\\n{0}\\r\\n-----END CERTIFICATE-----\", Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));\n                    writer.Write(contents);\n                }\n                catch (Exception ex)\n                {\n                    CrestronConsole.PrintLine(string.Format(\"Failed to write x509 cert cer\\r\\n{0}\", ex.Message));\n                }\n            }\n        }\n        /// <summary>\n        /// AddCertToStore method\n        /// </summary>\n        public bool AddCertToStore(X509Certificate2 cert, System.Security.Cryptography.X509Certificates.StoreName st, System.Security.Cryptography.X509Certificates.StoreLocation sl)\n        {\n            bool bRet = false;\n\n            try\n            {\n                var store = new System.Security.Cryptography.X509Certificates.X509Store(st, sl);\n                store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadWrite);\n                store.Add(cert);\n\n                store.Close();\n                bRet = true;\n            }\n            catch (Exception ex)\n            {\n                CrestronConsole.PrintLine(string.Format(\"AddCertToStore Failed\\r\\n{0}\\r\\n{1}\", ex.Message, ex.StackTrace));\n            }\n\n            return bRet;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\n\nnamespace PepperDash.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a DefaultRequestHandler\n /// </summary>\n\tpublic class DefaultRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic DefaultRequestHandler()\n\t\t\t: base(true)\n\t\t{ }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Core.Web.RequestHandlers\n{\n    public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler\n    {\n        private readonly Dictionary<string, Func<HttpCwsContext, Task>> _handlers;\n        protected readonly bool EnableCors;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        protected WebApiBaseRequestAsyncHandler(bool enableCors)\n        {\n            EnableCors = enableCors;\n\n            _handlers = new Dictionary<string, Func<HttpCwsContext, Task>>\n            {\n                {\"CONNECT\", HandleConnect},\n                {\"DELETE\", HandleDelete},\n                {\"GET\", HandleGet},\n                {\"HEAD\", HandleHead},\n                {\"OPTIONS\", HandleOptions},\n                {\"PATCH\", HandlePatch},\n                {\"POST\", HandlePost},\n                {\"PUT\", HandlePut},\n                {\"TRACE\", HandleTrace}\n            };\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        protected WebApiBaseRequestAsyncHandler()\n            : this(false)\n        {\n        }\n\n        /// <summary>\n        /// Handles CONNECT method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandleConnect(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();            \n        }\n\n        /// <summary>\n        /// Handles DELETE method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandleDelete(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();\n        }\n\n        /// <summary>\n        /// Handles GET method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandleGet(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();\n        }\n\n        /// <summary>\n        /// Handles HEAD method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandleHead(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();\n        }\n\n        /// <summary>\n        /// Handles OPTIONS method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandleOptions(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();\n        }\n\n        /// <summary>\n        /// Handles PATCH method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandlePatch(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();\n        }\n\n        /// <summary>\n        /// Handles POST method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandlePost(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();\n        }\n\n        /// <summary>\n        /// Handles PUT method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandlePut(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();\n        }\n\n        /// <summary>\n        /// Handles TRACE method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected virtual async Task HandleTrace(HttpCwsContext context)\n        {\n            context.Response.StatusCode = 501;\n            context.Response.StatusDescription = \"Not Implemented\";\n            context.Response.End();\n        }\n\n        /// <summary>\n        /// Process request\n        /// </summary>\n        /// <param name=\"context\"></param>\n        /// <summary>\n        /// ProcessRequest method\n        /// </summary>\n        public void ProcessRequest(HttpCwsContext context)\n        {\n            if (!_handlers.TryGetValue(context.Request.HttpMethod, out Func<HttpCwsContext, Task> handler))\n            {\n                return;\n            }\n\n            if (EnableCors)\n            {\n                context.Response.Headers.Add(\"Access-Control-Allow-Origin\", \"*\");\n                context.Response.Headers.Add(\"Access-Control-Allow-Methods\", \"POST, GET, OPTIONS\");\n            }\n\n            var handlerTask = handler(context);\n\n            handlerTask.GetAwaiter().GetResult();\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp.WebScripting;\n\nnamespace PepperDash.Core.Web.RequestHandlers\n{\n\t/// <summary>\n\t/// CWS Base Handler, implements IHttpCwsHandler\n\t/// </summary>\n\tpublic abstract class WebApiBaseRequestHandler : IHttpCwsHandler\n\t{\n\t\tprivate readonly Dictionary<string, Action<HttpCwsContext>> _handlers;\n\t\tprotected readonly bool EnableCors;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tprotected WebApiBaseRequestHandler(bool enableCors)\n\t\t{\n\t\t\tEnableCors = enableCors;\n\n\t\t\t_handlers = new Dictionary<string, Action<HttpCwsContext>>\n\t\t\t{\n\t\t\t\t{\"CONNECT\", HandleConnect},\n\t\t\t\t{\"DELETE\", HandleDelete},\n\t\t\t\t{\"GET\", HandleGet},\n\t\t\t\t{\"HEAD\", HandleHead},\n\t\t\t\t{\"OPTIONS\", HandleOptions},\n\t\t\t\t{\"PATCH\", HandlePatch},\n\t\t\t\t{\"POST\", HandlePost},\n\t\t\t\t{\"PUT\", HandlePut},\n\t\t\t\t{\"TRACE\", HandleTrace}\n\t\t\t};\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tprotected WebApiBaseRequestHandler()\n\t\t\t: this(false)\n\t\t{\n\t\t}\t\t\n\n\t\t/// <summary>\n\t\t/// Handles CONNECT method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandleConnect(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles DELETE method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandleDelete(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles HEAD method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandleHead(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles OPTIONS method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandleOptions(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles PATCH method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandlePatch(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandlePost(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles PUT method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandlePut(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles TRACE method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected virtual void HandleTrace(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Process request\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n  /// <summary>\n  /// ProcessRequest method\n  /// </summary>\n\t\tpublic void ProcessRequest(HttpCwsContext context)\n\t\t{\n\t\t\tAction<HttpCwsContext> handler;\n\n\t\t\tif (!_handlers.TryGetValue(context.Request.HttpMethod, out handler))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (EnableCors)\n\t\t\t{\n\t\t\t\tcontext.Response.Headers.Add(\"Access-Control-Allow-Origin\", \"*\");\n\t\t\t\tcontext.Response.Headers.Add(\"Access-Control-Allow-Methods\", \"POST, GET, OPTIONS\");\n\t\t\t}\n\n\t\t\thandler(context);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/Web/WebApiServer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core.Logging;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Core.Web\n{\n\t/// <summary>\n\t/// Web API server\n\t/// </summary>\n\tpublic class WebApiServer : IKeyName\n\t{\n\t\tprivate const string SplusKey = \"Uninitialized Web API Server\";\n\t\tprivate const string DefaultName = \"Web API Server\";\n\t\tprivate const string DefaultBasePath = \"/api\";\n\n\t\tprivate const uint DebugTrace = 0;\n\t\tprivate const uint DebugInfo = 1;\n\t\tprivate const uint DebugVerbose = 2;\n\n\t\tprivate readonly CCriticalSection _serverLock = new CCriticalSection();\n\t\tprivate HttpCwsServer _server;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Key\n\t\t/// </summary>\n\t\tpublic string Key { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Name\n\t\t/// </summary>\n\t\tpublic string Name { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the BasePath\n\t\t/// </summary>\n\t\tpublic string BasePath { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IsRegistered\n\t\t/// </summary>\n\t\tpublic bool IsRegistered { get; private set; }\n\n\t\t//public IHttpCwsHandler HttpRequestHandler\n\t\t//{\n\t\t//    get { return _server.HttpRequestHandler; }\n\t\t//    set\n\t\t//    {\n\t\t//        if (_server == null) return;\n\t\t//        _server.HttpRequestHandler = value;\n\t\t//    }\n\t\t//}\n\n\t\t//public event EventHandler<HttpCwsRequestEventArgs> ReceivedRequestEvent\n\t\t//{\n\t\t//    add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); }\n\t\t//    remove { _server.ReceivedRequestEvent -= new HttpCwsRequestEventHandler(value); }\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Constructor for S+.  Make sure to set necessary properties using init method\n\t\t/// </summary>\n\t\tpublic WebApiServer()\n\t\t\t: this(SplusKey, DefaultName, null)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"basePath\"></param>\n\t\tpublic WebApiServer(string key, string basePath)\n\t\t\t: this(key, DefaultName, basePath)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"name\"></param>\n\t\t/// <param name=\"basePath\"></param>\n\t\tpublic WebApiServer(string key, string name, string basePath)\n\t\t{\n\t\t\tKey = key;\n\t\t\tName = string.IsNullOrEmpty(name) ? DefaultName : name;\n\t\t\tBasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;\n\n\t\t\tif (_server == null) _server = new HttpCwsServer(BasePath);\n\n\t\t\t_server.AuthenticateAllRoutes = false;\n\n\t\t\t_server.setProcessName(Key);\n\t\t\t_server.HttpRequestHandler = new DefaultRequestHandler();\n\n\t\t\tCrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;\n\t\t\tCrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Program status event handler\n\t\t/// </summary>\n\t\t/// <param name=\"programEventType\"></param>\n\t\tvoid CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n\t\t{\n\t\t\tif (programEventType != eProgramStatusEventType.Stopping) return;\n\n\t\t\tthis.LogInformation(\"Program stopping. stopping server\");\n\n\t\t\tStop();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Ethernet event handler\n\t\t/// </summary>\n\t\t/// <param name=\"ethernetEventArgs\"></param>\n\t\tvoid CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)\n\t\t{\n\t\t\t// Re-enable the server if the link comes back up and the status should be connected\n\t\t\tif (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered)\n\t\t\t{\n\t\t\t\tthis.LogInformation(\"Ethernet link up. Server is alreedy registered.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.LogInformation(\"Ethernet link up. Starting server\");\n\n\t\t\tStart();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Initialize method\n\t\t/// </summary>\n\t\tpublic void Initialize(string key, string basePath)\n\t\t{\n\t\t\tKey = key;\n\t\t\tBasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds a route to CWS\n\t\t/// </summary>\n\t\tpublic void AddRoute(HttpCwsRoute route)\n\t\t{\n\t\t\tif (route == null)\n\t\t\t{\n\t\t\t\tthis.LogWarning(\"Failed to add route, route parameter is null\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t_server.Routes.Add(route);\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes a route from CWS\n\t\t/// </summary>\n\t\t/// <param name=\"route\"></param>\n\t\tpublic void RemoveRoute(HttpCwsRoute route)\n\t\t{\n\t\t\tif (route == null)\n\t\t\t{\n\t\t\t\tthis.LogWarning(\"Failed to remove route, route parameter is null\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t_server.Routes.Remove(route);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets the fallback request handler that is invoked when no registered route\n\t\t/// matches an incoming request.  Must be called before <see cref=\"Start\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"handler\">The handler to use as the server-level fallback.</param>\n\t\tpublic void SetFallbackHandler(IHttpCwsHandler handler)\n\t\t{\n\t\t\tif (handler == null)\n\t\t\t{\n\t\t\t\tthis.LogWarning(\"SetFallbackHandler: handler parameter is null, ignoring\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t_server.HttpRequestHandler = handler;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// GetRouteCollection method\n\t\t/// </summary>\n\t\tpublic HttpCwsRouteCollection GetRouteCollection()\n\t\t{\n\t\t\treturn _server.Routes;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Starts CWS instance\n\t\t/// </summary>\n\t\tpublic void Start()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\t_serverLock.Enter();\n\n\t\t\t\tif (_server == null)\n\t\t\t\t{\n\t\t\t\t\tthis.LogWarning(\"Server is null, unable to start\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (IsRegistered)\n\t\t\t\t{\n\t\t\t\t\tthis.LogWarning(\"Server has already been started\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tIsRegistered = _server.Register();\n\n\t\t\t\tthis.LogInformation(\"Starting server, registration {registrationResult}\", IsRegistered ? \"was successful\" : \"failed\");\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tthis.LogError(\"Start Exception Message: {message}\", ex.Message);\n\t\t\t\tthis.LogDebug(ex, \"Start Exception StackTrace\");\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\t_serverLock.Leave();\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Stop method\n\t\t/// </summary>\n\t\tpublic void Stop()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\t_serverLock.Enter();\n\n\t\t\t\tif (_server == null)\n\t\t\t\t{\n\t\t\t\t\tthis.LogWarning(\"Server is null or has already been stopped\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tIsRegistered = _server.Unregister() == false;\n\n\t\t\t\tthis.LogInformation(\"Stopping server, unregistration {unregistrationResult}\", IsRegistered ? \"failed\" : \"was successful\");\n\n\t\t\t\t_server.Dispose();\n\t\t\t\t_server = null;\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tthis.LogError(\"Server Stop Exception Message: {message}\", ex.Message);\n\t\t\t\tthis.LogDebug(ex, \"Server Stop Exception StackTrace\");\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\t_serverLock.Leave();\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Received request handler\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This is here for development and testing\n\t\t/// </remarks>\n\t\t/// <param name=\"sender\"></param>\n\t\t/// <param name=\"args\"></param>\n\t\tpublic void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar j = JsonConvert.SerializeObject(args.Context, Formatting.Indented);\n\t\t\t\tthis.LogVerbose(\"RecieveRequestEventHandler Context:\\x0d\\x0a{0}\", j);\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tthis.LogError(\"ReceivedRequestEventHandler Exception Message: {message}\", ex.Message);\n\t\t\t\tthis.LogDebug(ex, \"ReceivedRequestEventHandler Exception StackTrace: {stackTrace}\", ex.StackTrace);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/WebApi/Presets/Preset.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Core.WebApi.Presets\n{\n /// <summary>\n /// Represents a Preset\n /// </summary>\n\tpublic class Preset\n\t{\n        /// <summary>\n        /// ID of preset\n        /// </summary>\n\t\tpublic int Id { get; set; }\n\n  /// <summary>\n  /// Gets or sets the UserId\n  /// </summary>\n\t\tpublic int UserId { get; set; }\n\n  /// <summary>\n  /// Gets or sets the RoomTypeId\n  /// </summary>\n\t\tpublic int RoomTypeId { get; set; }\n\n  /// <summary>\n  /// Gets or sets the PresetName\n  /// </summary>\n\t\tpublic string PresetName { get; set; }\n\n  /// <summary>\n  /// Gets or sets the PresetNumber\n  /// </summary>\n\t\tpublic int PresetNumber { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Data\n  /// </summary>\n\t\tpublic string Data { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n\t\tpublic Preset()\n\t\t{\n\t\t\tPresetName = \"\";\n\t\t\tPresetNumber = 1;\n\t\t\tData = \"{}\";\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a PresetReceivedEventArgs\n /// </summary>\n\tpublic class PresetReceivedEventArgs : EventArgs\n\t{\n        /// <summary>\n        /// True when the preset is found\n        /// </summary>\n        public bool LookupSuccess { get; private set; }\n        \n        /// <summary>\n        /// Gets or sets the ULookupSuccess\n        /// </summary>\n        public ushort ULookupSuccess { get { return (ushort)(LookupSuccess ? 1 : 0); } }\n\n        /// <summary>\n        /// Gets or sets the Preset\n        /// </summary>\n        public Preset Preset { get; private set; }\n\n\t\t/// <summary>\n\t\t/// For Simpl+\n\t\t/// </summary>\n\t\tpublic PresetReceivedEventArgs() { }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"preset\"></param>\n        /// <param name=\"success\"></param>\n\t\tpublic PresetReceivedEventArgs(Preset preset, bool success)\n\t\t{\n            LookupSuccess = success;\n\t\t\tPreset = preset;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/WebApi/Presets/User.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Core.WebApi.Presets\n{\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic class User\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int Id { get; set; }\n\t\t\n  /// <summary>\n  /// Gets or sets the ExternalId\n  /// </summary>\n\t\tpublic string ExternalId { get; set; }\n\n  /// <summary>\n  /// Gets or sets the FirstName\n  /// </summary>\n\t\tpublic string FirstName { get; set; }\n\n  /// <summary>\n  /// Gets or sets the LastName\n  /// </summary>\n\t\tpublic string LastName { get; set; }\n\t}\n\n\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic class UserReceivedEventArgs : EventArgs\n\t{\n        /// <summary>\n        /// True when user is found\n        /// </summary>\n        public bool LookupSuccess { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the ULookupSuccess\n        /// </summary>\n        public ushort ULookupSuccess { get { return (ushort)(LookupSuccess ? 1 : 0); } }\n\n  /// <summary>\n  /// Gets or sets the User\n  /// </summary>\n\t\tpublic User User { get; private set; }\n\n\t\t/// <summary>\n\t\t/// For Simpl+\n\t\t/// </summary>\n\t\tpublic UserReceivedEventArgs() { }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"user\"></param>\n        /// <param name=\"success\"></param>\n\t\tpublic UserReceivedEventArgs(User user, bool success)\n\t\t{\n            LookupSuccess = success;\n\t\t\tUser = user;\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a UserAndRoomMessage\n /// </summary>\n\tpublic class UserAndRoomMessage\n\t{\n        /// <summary>\n        /// \n        /// </summary>\n\t\tpublic int UserId { get; set; }\n\n  /// <summary>\n  /// Gets or sets the RoomTypeId\n  /// </summary>\n\t\tpublic int RoomTypeId { get; set; }\n\n  /// <summary>\n  /// Gets or sets the PresetNumber\n  /// </summary>\n\t\tpublic int PresetNumber { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp;                                          // For Basic SIMPL# Classes\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharp.Net.Http;\nusing Crestron.SimplSharp.Net.Https;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core.JsonToSimpl;\n\n\nnamespace PepperDash.Core.WebApi.Presets\n{\n\t/// <summary>\n\t/// Passcode client for the WebApi\n\t/// </summary>\n\t[Obsolete(\"WebApiPasscodeClient is no longer supported and will be removed in a future release.\")]\n\tpublic class WebApiPasscodeClient : IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// Notifies when user received\n\t\t/// </summary>\n\t\tpublic event EventHandler<UserReceivedEventArgs> UserReceived;\n\n\t\t/// <summary>\n\t\t/// Notifies when Preset received\n\t\t/// </summary>\n\t\tpublic event EventHandler<PresetReceivedEventArgs> PresetReceived;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Key\n\t\t/// </summary>\n\t\tpublic string Key { get; private set; }\n\n\t\t//string JsonMasterKey;\n\n\t\t/// <summary>\n\t\t/// An embedded JsonToSimpl master object.\n\t\t/// </summary>\n\t\tJsonToSimplGenericMaster J2SMaster;\n\n\t\tstring UrlBase;\n\n\t\tstring DefaultPresetJsonFilePath;\n\n\t\tUser CurrentUser;\n\n\t\tPreset CurrentPreset;\n\n\n\t\t/// <summary>\n\t\t/// SIMPL+ can only execute the default constructor. If you have variables that require initialization, please\n\t\t/// use an Initialize method\n\t\t/// </summary>\n\t\tpublic WebApiPasscodeClient()\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Initializes the instance\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"jsonMasterKey\"></param>\n\t\t/// <param name=\"urlBase\"></param>\n\t\t/// <param name=\"defaultPresetJsonFilePath\"></param>\n\t\tpublic void Initialize(string key, string jsonMasterKey, string urlBase, string defaultPresetJsonFilePath)\n\t\t{\n\t\t\tKey = key;\n\t\t\t//JsonMasterKey = jsonMasterKey;\n\t\t\tUrlBase = urlBase;\n\t\t\tDefaultPresetJsonFilePath = defaultPresetJsonFilePath;\n\n\t\t\tJ2SMaster = new JsonToSimplGenericMaster();\n\t\t\tJ2SMaster.SaveCallback = this.SaveCallback;\n\t\t\tJ2SMaster.Initialize(jsonMasterKey);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the user for a passcode\n\t\t/// </summary>\n\t\t/// <param name=\"passcode\"></param>\n\t\t/// <summary>\n\t\t/// GetUserForPasscode method\n\t\t/// </summary>\n\t\tpublic void GetUserForPasscode(string passcode)\n\t\t{\n\t\t\t// Bullshit duplicate code here... These two cases should be the same \n\t\t\t// except for https/http and the certificate ignores \n\t\t\tif (!UrlBase.StartsWith(\"https\"))\n\t\t\t\treturn;\n\t\t\tvar req = new HttpsClientRequest();\n\t\t\treq.Url = new UrlParser(UrlBase + \"/api/users/dopin\");\n\t\t\treq.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;\n\t\t\treq.Header.AddHeader(new HttpsHeader(\"Content-Type\", \"application/json\"));\n\t\t\treq.Header.AddHeader(new HttpsHeader(\"Accept\", \"application/json\"));\n\t\t\tvar jo = new JObject();\n\t\t\tjo.Add(\"pin\", passcode);\n\t\t\treq.ContentString = jo.ToString();\n\n\t\t\tvar client = new HttpsClient();\n\t\t\tclient.HostVerification = false;\n\t\t\tclient.PeerVerification = false;\n\t\t\tvar resp = client.Dispatch(req);\n\t\t\tvar handler = UserReceived;\n\t\t\tif (resp.Code == 200)\n\t\t\t{\n\t\t\t\t//CrestronConsole.PrintLine(\"Received: {0}\", resp.ContentString);\n\t\t\t\tvar user = JsonConvert.DeserializeObject<User>(resp.ContentString);\n\t\t\t\tCurrentUser = user;\n\t\t\t\tif (handler != null)\n\t\t\t\t\thandler(this, new UserReceivedEventArgs(user, true));\n\t\t\t}\n\t\t\telse\n\t\t\t\tif (handler != null)\n\t\t\t\t\thandler(this, new UserReceivedEventArgs(null, false));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"roomTypeId\"></param>\n\t\t/// <param name=\"presetNumber\"></param>\n\t\t/// <summary>\n\t\t/// GetPresetForThisUser method\n\t\t/// </summary>\n\t\tpublic void GetPresetForThisUser(int roomTypeId, int presetNumber)\n\t\t{\n\t\t\tif (CurrentUser == null)\n\t\t\t{\n\t\t\t\tCrestronConsole.PrintLine(\"GetPresetForThisUser no user loaded\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar msg = new UserAndRoomMessage\n\t\t\t{\n\t\t\t\tUserId = CurrentUser.Id,\n\t\t\t\tRoomTypeId = roomTypeId,\n\t\t\t\tPresetNumber = presetNumber\n\t\t\t};\n\n\t\t\tvar handler = PresetReceived;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (!UrlBase.StartsWith(\"https\"))\n\t\t\t\t\treturn;\n\t\t\t\tvar req = new HttpsClientRequest();\n\t\t\t\treq.Url = new UrlParser(UrlBase + \"/api/presets/userandroom\");\n\t\t\t\treq.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;\n\t\t\t\treq.Header.AddHeader(new HttpsHeader(\"Content-Type\", \"application/json\"));\n\t\t\t\treq.Header.AddHeader(new HttpsHeader(\"Accept\", \"application/json\"));\n\t\t\t\treq.ContentString = JsonConvert.SerializeObject(msg);\n\n\t\t\t\tvar client = new HttpsClient();\n\t\t\t\tclient.HostVerification = false;\n\t\t\t\tclient.PeerVerification = false;\n\n\t\t\t\t// ask for the preset\n\t\t\t\tvar resp = client.Dispatch(req);\n\t\t\t\tif (resp.Code == 200) // got it\n\t\t\t\t{\n\t\t\t\t\t//Debug.Console(1, this, \"Received: {0}\", resp.ContentString);\n\t\t\t\t\tvar preset = JsonConvert.DeserializeObject<Preset>(resp.ContentString);\n\t\t\t\t\tCurrentPreset = preset;\n\n\t\t\t\t\t//if there's no preset data, load the template\n\t\t\t\t\tif (preset.Data == null || preset.Data.Trim() == string.Empty || JObject.Parse(preset.Data).Count == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t//Debug.Console(1, this, \"Loaded preset has no data. Loading default template.\");\n\t\t\t\t\t\tLoadDefaultPresetData();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tJ2SMaster.LoadWithJson(preset.Data);\n\t\t\t\t\tif (handler != null)\n\t\t\t\t\t\thandler(this, new PresetReceivedEventArgs(preset, true));\n\t\t\t\t}\n\t\t\t\telse // no existing preset\n\t\t\t\t{\n\t\t\t\t\tCurrentPreset = new Preset();\n\t\t\t\t\tLoadDefaultPresetData();\n\t\t\t\t\tif (handler != null)\n\t\t\t\t\t\thandler(this, new PresetReceivedEventArgs(null, false));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (HttpException e)\n\t\t\t{\n\t\t\t\tvar resp = e.Response;\n\t\t\t\tDebug.Console(1, this, \"No preset received (code {0}). Loading default template\", resp.Code);\n\t\t\t\tLoadDefaultPresetData();\n\t\t\t\tif (handler != null)\n\t\t\t\t\tPresetReceived(this, new PresetReceivedEventArgs(null, false));\n\t\t\t}\n\t\t}\n\n\t\tvoid LoadDefaultPresetData()\n\t\t{\n\t\t\tCurrentPreset = null;\n\t\t\tif (!File.Exists(DefaultPresetJsonFilePath))\n\t\t\t{\n\t\t\t\tDebug.Console(0, this, \"Cannot load default preset file. Saving will not work\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tusing (StreamReader sr = new StreamReader(DefaultPresetJsonFilePath))\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tvar data = sr.ReadToEnd();\n\t\t\t\t\tJ2SMaster.SetJsonWithoutEvaluating(data);\n\t\t\t\t\tCurrentPreset = new Preset() { Data = data, UserId = CurrentUser.Id };\n\t\t\t\t}\n\t\t\t\tcatch (Exception e)\n\t\t\t\t{\n\t\t\t\t\tDebug.Console(0, this, \"Error reading default preset JSON: \\r{0}\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"roomTypeId\"></param>\n\t\t/// <param name=\"presetNumber\"></param>\n\t\t/// <summary>\n\t\t/// SavePresetForThisUser method\n\t\t/// </summary>\n\t\tpublic void SavePresetForThisUser(int roomTypeId, int presetNumber)\n\t\t{\n\t\t\tif (CurrentPreset == null)\n\t\t\t\tLoadDefaultPresetData();\n\t\t\t//return;\n\n\t\t\t//// A new preset needs to have its numbers set\n\t\t\t//if (CurrentPreset.IsNewPreset)\n\t\t\t//{\n\t\t\tCurrentPreset.UserId = CurrentUser.Id;\n\t\t\tCurrentPreset.RoomTypeId = roomTypeId;\n\t\t\tCurrentPreset.PresetNumber = presetNumber;\n\t\t\t//}\n\t\t\tJ2SMaster.Save(); // Will trigger callback when ready\n\t\t}\n\n\t\t/// <summary>\n\t\t/// After save operation on JSON master happens, send it to server\n\t\t/// </summary>\n\t\t/// <param name=\"json\"></param>\n\t\tvoid SaveCallback(string json)\n\t\t{\n\t\t\tCurrentPreset.Data = json;\n\n\t\t\tif (!UrlBase.StartsWith(\"https\"))\n\t\t\t\treturn;\n\t\t\tvar req = new HttpsClientRequest();\n\t\t\treq.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;\n\t\t\treq.Url = new UrlParser(string.Format(\"{0}/api/presets/addorchange\", UrlBase));\n\t\t\treq.Header.AddHeader(new HttpsHeader(\"Content-Type\", \"application/json\"));\n\t\t\treq.Header.AddHeader(new HttpsHeader(\"Accept\", \"application/json\"));\n\t\t\treq.ContentString = JsonConvert.SerializeObject(CurrentPreset);\n\n\t\t\tvar client = new HttpsClient();\n\t\t\tclient.HostVerification = false;\n\t\t\tclient.PeerVerification = false;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar resp = client.Dispatch(req);\n\n\t\t\t\t// 201=created\n\t\t\t\t// 204=empty content\n\t\t\t\tif (resp.Code == 201)\n\t\t\t\t\tCrestronConsole.PrintLine(\"Preset added\");\n\t\t\t\telse if (resp.Code == 204)\n\t\t\t\t\tCrestronConsole.PrintLine(\"Preset updated\");\n\t\t\t\telse if (resp.Code == 209)\n\t\t\t\t\tCrestronConsole.PrintLine(\"Preset already exists. Cannot save as new.\");\n\t\t\t\telse\n\t\t\t\t\tCrestronConsole.PrintLine(\"Preset save failed: {0}\\r\", resp.Code, resp.ContentString);\n\t\t\t}\n\t\t\tcatch (HttpException e)\n\t\t\t{\n\n\t\t\t\tCrestronConsole.PrintLine(\"Preset save exception {0}\", e.Response.Code);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/Serialization/IXSigSerialization.cs",
    "content": "using System.Collections.Generic;\nusing PepperDash.Core.Intersystem.Tokens;\n\nnamespace PepperDash.Core.Intersystem.Serialization\n{\n    /// <summary>\n    /// Interface to determine XSig serialization for an object.\n    /// </summary>\n    public interface IXSigSerialization\n    {\n        /// <summary>\n        /// Serialize the sig data\n        /// </summary>\n        /// <returns></returns>\n        IEnumerable<XSigToken> Serialize();\n\n        /// <summary>\n        /// Deserialize the sig data\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"tokens\"></param>\n        /// <returns></returns>\n        T Deserialize<T>(IEnumerable<XSigToken> tokens) where T : class, IXSigSerialization;\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/Serialization/XSigSerializationException.cs",
    "content": "using System;\n\nnamespace PepperDash.Core.Intersystem.Serialization\n{\n    /// <summary>\n    /// Class to handle this specific exception type\n    /// </summary>\n    public class XSigSerializationException : Exception\n    {\n        /// <summary>\n        /// default constructor\n        /// </summary>\n        public XSigSerializationException() { }\n\n        /// <summary>\n        /// constructor with message\n        /// </summary>\n        /// <param name=\"message\"></param>\n        public XSigSerializationException(string message) : base(message) { }\n\n        /// <summary>\n        /// constructor with message and innner exception\n        /// </summary>\n        /// <param name=\"message\"></param>\n        /// <param name=\"inner\"></param>\n        public XSigSerializationException(string message, Exception inner) : base(message, inner) { }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/Tokens/XSigAnalogToken.cs",
    "content": "using System;\n\nnamespace PepperDash.Core.Intersystem.Tokens\n{\n    /// <summary>\n    /// Represents an XSigAnalogToken\n    /// </summary>\n    public sealed class XSigAnalogToken : XSigToken, IFormattable\n    {\n        private readonly ushort _value;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"index\"></param>\n        /// <param name=\"value\"></param>\n        public XSigAnalogToken(int index, ushort value)\n            : base(index)\n        {\n            // 10-bits available for analog encoded data\n            if (index >= 1024 || index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            _value = value;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public ushort Value\n        {\n            get { return _value; }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public override XSigTokenType TokenType\n        {\n            get { return XSigTokenType.Analog; }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <returns></returns>\n        public override byte[] GetBytes()\n        {\n            return new[] {\n                (byte)(0xC0 | ((Value & 0xC000) >> 10) | (Index - 1 >> 7)),\n                (byte)((Index - 1) & 0x7F),\n                (byte)((Value & 0x3F80) >> 7),\n                (byte)(Value & 0x7F)\n            };\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"offset\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetTokenWithOffset method\n        /// </summary>\n        public override XSigToken GetTokenWithOffset(int offset)\n        {\n            if (offset == 0) return this;\n            return new XSigAnalogToken(Index + offset, Value);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <returns></returns>\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return Index + \" = 0x\" + Value.ToString(\"X4\");\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"format\"></param>\n        /// <param name=\"formatProvider\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        public string ToString(string format, IFormatProvider formatProvider)\n        {\n            return Value.ToString(format, formatProvider);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/Tokens/XSigDigitalToken.cs",
    "content": "using System;\n\nnamespace PepperDash.Core.Intersystem.Tokens\n{\n    /// <summary>\n    /// Represents an XSigDigitalToken\n    /// </summary>\n    public sealed class XSigDigitalToken : XSigToken\n    {\n        private readonly bool _value;\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"index\"></param>\n        /// <param name=\"value\"></param>\n        public XSigDigitalToken(int index, bool value)\n            : base(index)\n        {\n            // 12-bits available for digital encoded data\n            if (index >= 4096 || index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            _value = value;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public bool Value\n        {\n            get { return _value; }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public override XSigTokenType TokenType\n        {\n            get { return XSigTokenType.Digital; }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <returns></returns>\n        public override byte[] GetBytes()\n        {\n            return new[] {\n                (byte)(0x80 | (Value ? 0 : 0x20) | ((Index - 1) >> 7)),\n                (byte)((Index - 1) & 0x7F)\n            };\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"offset\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetTokenWithOffset method\n        /// </summary>\n        public override XSigToken GetTokenWithOffset(int offset)\n        {\n            if (offset == 0) return this;\n            return new XSigDigitalToken(Index + offset, Value);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <returns></returns>\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return Index + \" = \" + (Value ? \"High\" : \"Low\");\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"formatProvider\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        public string ToString(IFormatProvider formatProvider)\n        {\n            return Value.ToString(formatProvider);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/Tokens/XSigSerialToken.cs",
    "content": "using System;\nusing System.Text;\n\nnamespace PepperDash.Core.Intersystem.Tokens\n{\n    /// <summary>\n    /// Represents an XSigSerialToken\n    /// </summary>\n    public sealed class XSigSerialToken : XSigToken\n    {\n        private readonly string _value;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"index\"></param>\n        /// <param name=\"value\"></param>\n        public XSigSerialToken(int index, string value)\n            : base(index)\n        {\n            // 10-bits available for serial encoded data\n            if (index >= 1024 || index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            _value = value;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public string Value\n        {\n            get { return _value; }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public override XSigTokenType TokenType\n        {\n            get { return XSigTokenType.Serial; }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <returns></returns>\n        public override byte[] GetBytes()\n        {\n            var serialBytes = String.IsNullOrEmpty(Value) ? new byte[0] : Encoding.GetEncoding(28591).GetBytes(Value);\n            \n            var xsig = new byte[serialBytes.Length + 3];\n            xsig[0] = (byte)(0xC8 | (Index - 1 >> 7));\n            xsig[1] = (byte)((Index - 1) & 0x7F);\n            xsig[xsig.Length - 1] = 0xFF;\n\n            Buffer.BlockCopy(serialBytes, 0, xsig, 2, serialBytes.Length);\n            return xsig;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"offset\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetTokenWithOffset method\n        /// </summary>\n        public override XSigToken GetTokenWithOffset(int offset)\n        {\n            if (offset == 0) return this;\n            return new XSigSerialToken(Index + offset, Value);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <returns></returns>\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return Index + \" = \\\"\" + Value + \"\\\"\";\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/Tokens/XSigToken.cs",
    "content": "namespace PepperDash.Core.Intersystem.Tokens\n{\n    /// <summary>\n    /// Represents the base class for all XSig datatypes.\n    /// </summary>\n    public abstract class XSigToken\n    {\n        private readonly int _index;\n\n        /// <summary>\n        /// Constructs an XSigToken with the specified index.\n        /// </summary>\n        /// <param name=\"index\">Index for the data.</param>\n        protected XSigToken(int index)\n        {\n            _index = index;\n        }\n\n        /// <summary>\n        /// XSig 1-based index.\n        /// </summary>\n        public int Index\n        {\n            get { return _index; }\n        }\n\n        /// <summary>\n        /// XSigToken type.\n        /// </summary>\n        public abstract XSigTokenType TokenType { get; }\n\n        /// <summary>\n        /// Generates the XSig bytes for the corresponding token.\n        /// </summary>\n        /// <returns>XSig byte array.</returns>\n        public abstract byte[] GetBytes();\n\n        /// <summary>\n        /// Returns a new token if necessary with an updated index based on the specified offset.\n        /// </summary>\n        /// <param name=\"offset\">Offset to adjust the index with.</param>\n        /// <returns>XSigToken</returns>\n        public abstract XSigToken GetTokenWithOffset(int offset);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/Tokens/XSigTokenType.cs",
    "content": "namespace PepperDash.Core.Intersystem.Tokens\n{\n    /// <summary>\n    /// XSig token types.\n    /// </summary>\n    public enum XSigTokenType\n    {\n        /// <summary>\n        /// Digital signal datatype.\n        /// </summary>\n        Digital,\n\n        /// <summary>\n        /// Analog signal datatype.\n        /// </summary>\n        Analog,\n\n        /// <summary>\n        /// Serial signal datatype.\n        /// </summary>\n        Serial\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/XSigHelpers.cs",
    "content": "using System;\nusing System.Linq;\nusing Crestron.SimplSharp.CrestronIO;\nusing PepperDash.Core.Intersystem.Serialization;\nusing PepperDash.Core.Intersystem.Tokens;\n\n/*\n    Digital (2 bytes)\n        10C##### 0####### (mask = 11000000_10000000b -> 0xC080)\n\n    Analog (4 bytes)\n        11aa0### 0####### (mask = 11001000_10000000b -> 0xC880)\n        0aaaaaaa 0aaaaaaa\n\n    Serial (Variable length)\n        11001### 0####### (mask = 11111000_10000000b -> 0xF880)\n        dddddddd ........ <- up to 252 bytes of serial data (255 - 3)\n        11111111 <- denotes end of data\n*/\n\nnamespace PepperDash.Core.Intersystem\n{\n    /// <summary>\n    /// Helper methods for creating XSig byte sequences compatible with the Intersystem Communications (ISC) symbol.\n    /// </summary>\n    /// <remarks>\n    /// Indexing is not from the start of each signal type but rather from the beginning of the first defined signal\n    /// the Intersystem Communications (ISC) symbol.\n    /// </remarks>\n    public static class XSigHelpers\n    {\n        /// <summary>\n        /// Forces all outputs to 0.\n        /// </summary>\n        /// <returns>Bytes in XSig format for clear outputs trigger.</returns>\n        public static byte[] ClearOutputs()\n        {\n            return new byte[] { 0xFC };\n        }\n\n        /// <summary>\n        /// Evaluate all inputs and re-transmit any digital, analog, and permanent serail signals not set to 0.\n        /// </summary>\n        /// <returns>Bytes in XSig format for send status trigger.</returns>\n        public static byte[] SendStatus()\n        {\n            return new byte[] { 0xFD };\n        }\n\n        /// <summary>\n        /// Get bytes for an IXSigStateResolver object.\n        /// </summary>\n        /// <param name=\"xSigSerialization\">XSig state resolver.</param>\n        /// <returns>Bytes in XSig format for each token within the state representation.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(IXSigSerialization xSigSerialization)\n        {\n            return GetBytes(xSigSerialization, 0);\n        }\n\n        /// <summary>\n        /// Get bytes for an IXSigStateResolver object, with a specified offset.\n        /// </summary>\n        /// <param name=\"xSigSerialization\">XSig state resolver.</param>\n        /// <param name=\"offset\">Offset to which the data will be aligned.</param>\n        /// <returns>Bytes in XSig format for each token within the state representation.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(IXSigSerialization xSigSerialization, int offset)\n        {\n            var tokens = xSigSerialization.Serialize();\n            if (tokens == null) return new byte[0];\n            using (var memoryStream = new MemoryStream())\n            {\n                using (var tokenWriter = new XSigTokenStreamWriter(memoryStream))\n                    tokenWriter.WriteXSigData(xSigSerialization, offset);\n\n                return memoryStream.ToArray();\n            }\n        }\n\n        /// <summary>\n        /// Get bytes for a single digital signal.\n        /// </summary>\n        /// <param name=\"index\">1-based digital index</param>\n        /// <param name=\"value\">Digital data to be encoded</param>\n        /// <returns>Bytes in XSig format for digtial information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int index, bool value)\n        {\n            return GetBytes(index, 0, value);\n        }\n\n        /// <summary>\n        /// Get bytes for a single digital signal.\n        /// </summary>\n        /// <param name=\"index\">1-based digital index</param>\n        /// <param name=\"offset\">Index offset.</param>\n        /// <param name=\"value\">Digital data to be encoded</param>\n        /// <returns>Bytes in XSig format for digtial information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int index, int offset, bool value)\n        {\n            return new XSigDigitalToken(index + offset, value).GetBytes();\n        }\n\n        /// <summary>\n        /// Get byte sequence for multiple digital signals.\n        /// </summary>\n        /// <param name=\"startIndex\">Starting index of the sequence.</param>\n        /// <param name=\"values\">Digital signal value array.</param>\n        /// <returns>Byte sequence in XSig format for digital signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int startIndex, bool[] values)\n        {\n            return GetBytes(startIndex, 0, values);\n        }\n\n        /// <summary>\n        /// Get byte sequence for multiple digital signals.\n        /// </summary>\n        /// <param name=\"startIndex\">Starting index of the sequence.</param>\n        /// <param name=\"offset\">Index offset.</param>\n        /// <param name=\"values\">Digital signal value array.</param>\n        /// <returns>Byte sequence in XSig format for digital signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int startIndex, int offset, bool[] values)\n        {\n            // Digital XSig data is 2 bytes per value\n            const int fixedLength = 2;\n            var bytes = new byte[values.Length * fixedLength];\n            for (var i = 0; i < values.Length; i++)\n                Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength);\n\n            return bytes;\n        }\n\n        /// <summary>\n        /// Get bytes for a single analog signal.\n        /// </summary>\n        /// <param name=\"index\">1-based analog index</param>\n        /// <param name=\"value\">Analog data to be encoded</param>\n        /// <returns>Bytes in XSig format for analog signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int index, ushort value)\n        {\n            return GetBytes(index, 0, value);\n        }\n\n        /// <summary>\n        /// Get bytes for a single analog signal.\n        /// </summary>\n        /// <param name=\"index\">1-based analog index</param>\n        /// <param name=\"offset\">Index offset.</param>\n        /// <param name=\"value\">Analog data to be encoded</param>\n        /// <returns>Bytes in XSig format for analog signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int index, int offset, ushort value)\n        {\n            return new XSigAnalogToken(index + offset, value).GetBytes();\n        }\n\n        /// <summary>\n        /// Get byte sequence for multiple analog signals.\n        /// </summary>\n        /// <param name=\"startIndex\">Starting index of the sequence.</param>\n        /// <param name=\"values\">Analog signal value array.</param>\n        /// <returns>Byte sequence in XSig format for analog signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int startIndex, ushort[] values)\n        {\n            return GetBytes(startIndex, 0, values);\n        }\n\n        /// <summary>\n        /// Get byte sequence for multiple analog signals.\n        /// </summary>\n        /// <param name=\"startIndex\">Starting index of the sequence.</param>\n        /// <param name=\"offset\">Index offset.</param>\n        /// <param name=\"values\">Analog signal value array.</param>\n        /// <returns>Byte sequence in XSig format for analog signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int startIndex, int offset, ushort[] values)\n        {\n            // Analog XSig data is 4 bytes per value\n            const int fixedLength = 4;\n            var bytes = new byte[values.Length * fixedLength];\n            for (var i = 0; i < values.Length; i++)\n                Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength);\n\n            return bytes;\n        }\n\n        /// <summary>\n        /// Get bytes for a single serial signal.\n        /// </summary>\n        /// <param name=\"index\">1-based serial index</param>\n        /// <param name=\"value\">Serial data to be encoded</param>\n        /// <returns>Bytes in XSig format for serial signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int index, string value)\n        {\n            return GetBytes(index, 0, value);\n        }\n\n        /// <summary>\n        /// Get bytes for a single serial signal.\n        /// </summary>\n        /// <param name=\"index\">1-based serial index</param>\n        /// <param name=\"offset\">Index offset.</param>\n        /// <param name=\"value\">Serial data to be encoded</param>\n        /// <returns>Bytes in XSig format for serial signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int index, int offset, string value)\n        {\n            return new XSigSerialToken(index + offset, value).GetBytes();\n        }\n\n        /// <summary>\n        /// Get byte sequence for multiple serial signals.\n        /// </summary>\n        /// <param name=\"startIndex\">Starting index of the sequence.</param>\n        /// <param name=\"values\">Serial signal value array.</param>\n        /// <returns>Byte sequence in XSig format for serial signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int startIndex, string[] values)\n        {\n            return GetBytes(startIndex, 0, values);\n        }\n\n        /// <summary>\n        /// Get byte sequence for multiple serial signals.\n        /// </summary>\n        /// <param name=\"startIndex\">Starting index of the sequence.</param>\n        /// <param name=\"offset\">Index offset.</param>\n        /// <param name=\"values\">Serial signal value array.</param>\n        /// <returns>Byte sequence in XSig format for serial signal information.</returns>\n        /// <summary>\n        /// GetBytes method\n        /// </summary>\n        public static byte[] GetBytes(int startIndex, int offset, string[] values)\n        {\n            // Serial XSig data is not fixed-length like the other formats\n            var dstOffset = 0;\n            var bytes = new byte[values.Sum(v => v.Length + 3)];\n            for (var i = 0; i < values.Length; i++)\n            {\n                var data = GetBytes(startIndex++, offset, values[i]);\n                Buffer.BlockCopy(data, 0, bytes, dstOffset, data.Length);\n                dstOffset += data.Length;\n            }\n\n            return bytes;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp.CrestronIO;\nusing PepperDash.Core.Intersystem.Serialization;\nusing PepperDash.Core.Intersystem.Tokens;\n\nnamespace PepperDash.Core.Intersystem\n{\n    /// <summary>\n    /// XSigToken stream reader.\n    /// </summary>\n    public sealed class XSigTokenStreamReader : IDisposable\n    {\n        private readonly Stream _stream;\n        private readonly bool _leaveOpen;\n\n        /// <inheritdoc />\n        /// <summary>\n        /// XSigToken stream reader constructor.\n        /// </summary>\n        /// <param name=\"stream\">Input stream to read from.</param>\n        /// <exception cref=\"T:System.ArgumentNullException\">Stream is null.</exception>\n        /// <exception cref=\"T:System.ArgumentException\">Stream cannot be read from.</exception>\n        public XSigTokenStreamReader(Stream stream)\n            : this(stream, false) { }\n\n        /// <summary>\n        /// XSigToken stream reader constructor.\n        /// </summary>\n        /// <param name=\"stream\">Input stream to read from.</param>\n        /// <param name=\"leaveOpen\">Determines whether to leave the stream open or not.</param>\n        /// <exception cref=\"ArgumentNullException\">Stream is null.</exception>\n        /// <exception cref=\"ArgumentException\">Stream cannot be read from.</exception>\n        public XSigTokenStreamReader(Stream stream, bool leaveOpen)\n        {\n            if (stream == null)\n                throw new ArgumentNullException(\"stream\");\n            if (!stream.CanRead)\n                throw new ArgumentException(\"The specified stream cannot be read from.\");\n\n            _stream = stream;\n            _leaveOpen = leaveOpen;\n        }\n\n        /// <summary>\n        /// Reads a 16-bit unsigned integer from the specified stream using Big Endian byte order.\n        /// </summary>\n        /// <param name=\"stream\">Input stream</param>\n        /// <param name=\"value\">Result</param>\n        /// <returns>True if successful, otherwise false.</returns>\n        /// <summary>\n        /// TryReadUInt16BE method\n        /// </summary>\n        public static bool TryReadUInt16BE(Stream stream, out ushort value)\n        {\n            value = 0;\n            if (stream.Length < 2)\n                return false;\n\n            var buffer = new byte[2];\n            stream.Read(buffer, 0, 2);\n            value = (ushort)((buffer[0] << 8) | buffer[1]);\n            return true;\n        }\n\n        /// <summary>\n        /// Read XSig token from the stream.\n        /// </summary>\n        /// <returns>XSigToken</returns>\n        /// <exception cref=\"ArgumentOutOfRangeException\">Offset is less than 0.</exception>\n        /// <summary>\n        /// ReadXSigToken method\n        /// </summary>\n        public XSigToken ReadXSigToken()\n        {\n            ushort prefix;\n            if (!TryReadUInt16BE(_stream, out prefix))\n                return null;\n\n            if ((prefix & 0xF880) == 0xC800) // Serial data\n            {\n                var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F);\n                var n = 0;\n                const int maxSerialDataLength = 252;\n                var chars = new char[maxSerialDataLength];\n                int ch;\n                while ((ch = _stream.ReadByte()) != 0xFF)\n                {\n                    if (ch == -1) // Reached end of stream without end of data marker\n                        return null;\n                    \n                    chars[n++] = (char)ch;\n                }\n\n                return new XSigSerialToken((ushort)(index + 1), new string(chars, 0, n));\n            }\n\n            if ((prefix & 0xC880) == 0xC000) // Analog data\n            {\n                ushort data;\n                if (!TryReadUInt16BE(_stream, out data))\n                    return null;\n\n                var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F);\n                var value = ((prefix & 0x3000) << 2) | ((data & 0x7F00) >> 1) | (data & 0x7F);\n                return new XSigAnalogToken((ushort)(index + 1), (ushort)value);\n            }\n\n            if ((prefix & 0xC080) == 0x8000) // Digital data\n            {\n                var index = ((prefix & 0x1F00) >> 1) | (prefix & 0x7F);\n                var value = (prefix & 0x2000) == 0;\n                return new XSigDigitalToken((ushort)(index + 1), value);\n            }\n\n            return null;\n        }\n\n        /// <summary>\n        /// Reads all available XSig tokens from the stream.\n        /// </summary>\n        /// <returns>XSigToken collection.</returns>\n        /// <summary>\n        /// ReadAllXSigTokens method\n        /// </summary>\n        public IEnumerable<XSigToken> ReadAllXSigTokens()\n        {\n            var tokens = new List<XSigToken>();\n            XSigToken token;\n            while ((token = ReadXSigToken()) != null)\n                tokens.Add(token);\n\n            return tokens;\n        }\n\n        /// <summary>\n        /// Attempts to deserialize all XSig data within the stream from the current position.\n        /// </summary>\n        /// <typeparam name=\"T\">Type to deserialize the information to.</typeparam>\n        /// <returns>Deserialized object.</returns>\n        public T DeserializeStream<T>()\n            where T : class, IXSigSerialization, new()\n        {\n            return new T().Deserialize<T>(ReadAllXSigTokens());\n        }\n\n        /// <summary>\n        /// Disposes of the internal stream if specified to not leave open.\n        /// </summary>\n        public void Dispose()\n        {\n            if (!_leaveOpen)\n                _stream.Dispose();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs",
    "content": "using System;\nusing System.Linq;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp.CrestronIO;\nusing PepperDash.Core.Intersystem.Serialization;\nusing PepperDash.Core.Intersystem.Tokens;\n\nnamespace PepperDash.Core.Intersystem\n{\n    /// <summary>\n    /// XSigToken stream writer.\n    /// </summary>\n    public sealed class XSigTokenStreamWriter : IDisposable\n    {\n        private readonly Stream _stream;\n        private readonly bool _leaveOpen;\n\n        /// <inheritdoc />\n        /// <summary>\n        /// XSigToken stream writer constructor.\n        /// </summary>\n        /// <param name=\"stream\">Input stream to write to.</param>\n        /// <exception cref=\"T:System.ArgumentNullException\">Stream is null.</exception>\n        /// <exception cref=\"T:System.ArgumentException\">Stream cannot be written to.</exception>\n        public XSigTokenStreamWriter(Stream stream)\n            : this(stream, false) { }\n\n        /// <summary>\n        /// XSigToken stream writer constructor.\n        /// </summary>\n        /// <param name=\"stream\">Input stream to write to.</param>\n        /// <param name=\"leaveOpen\">Determines whether to leave the stream open or not.</param>\n        /// <exception cref=\"ArgumentNullException\">Stream is null.</exception>\n        /// <exception cref=\"ArgumentException\">Stream cannot be written to.</exception>\n        public XSigTokenStreamWriter(Stream stream, bool leaveOpen)\n        {\n            if (stream == null)\n                throw new ArgumentNullException(\"stream\");\n            if (!stream.CanWrite)\n                throw new ArgumentException(\"The specified stream cannot be written to.\");\n\n            _stream = stream;\n            _leaveOpen = leaveOpen;\n        }\n\n        /// <summary>\n        /// Write XSig data gathered from an IXSigStateResolver to the stream.\n        /// </summary>\n        /// <param name=\"xSigSerialization\">IXSigStateResolver object.</param>\n        /// <summary>\n        /// WriteXSigData method\n        /// </summary>\n        public void WriteXSigData(IXSigSerialization xSigSerialization)\n        {\n            WriteXSigData(xSigSerialization, 0);\n        }\n\n        /// <summary>\n        /// Write XSig data gathered from an IXSigStateResolver to the stream.\n        /// </summary>\n        /// <param name=\"xSigSerialization\">IXSigStateResolver object.</param>\n        /// <param name=\"offset\">Index offset for each XSigToken.</param>\n        /// <summary>\n        /// WriteXSigData method\n        /// </summary>\n        public void WriteXSigData(IXSigSerialization xSigSerialization, int offset)\n        {\n            if (xSigSerialization == null)\n                throw new ArgumentNullException(\"xSigSerialization\");\n\n            var tokens = xSigSerialization.Serialize();\n            WriteXSigData(tokens, offset);\n        }\n\n        /// <summary>\n        /// Write XSigToken to the stream.\n        /// </summary>\n        /// <param name=\"token\">XSigToken object.</param>\n        /// <summary>\n        /// WriteXSigData method\n        /// </summary>\n        public void WriteXSigData(XSigToken token)\n        {\n            WriteXSigData(token, 0);\n        }\n\n        /// <summary>\n        /// Write XSigToken to the stream.\n        /// </summary>\n        /// <param name=\"token\">XSigToken object.</param>\n        /// <param name=\"offset\">Index offset for each XSigToken.</param>\n        /// <summary>\n        /// WriteXSigData method\n        /// </summary>\n        public void WriteXSigData(XSigToken token, int offset)\n        {\n            WriteXSigData(new[] { token }, offset);\n        }\n\n        /// <summary>\n        /// Writes an array of XSigTokens to the stream.\n        /// </summary>\n        /// <param name=\"tokens\">XSigToken objects.</param>\n        public void WriteXSigData(XSigToken[] tokens)\n        {\n            WriteXSigData(tokens.AsEnumerable());\n        }\n\n        /// <summary>\n        /// Write an enumerable collection of XSigTokens to the stream.\n        /// </summary>\n        /// <param name=\"tokens\">XSigToken objects.</param>\n        public void WriteXSigData(IEnumerable<XSigToken> tokens)\n        {\n            WriteXSigData(tokens, 0);\n        }\n\n        /// <summary>\n        /// Write an enumerable collection of XSigTokens to the stream.\n        /// </summary>\n        /// <param name=\"tokens\">XSigToken objects.</param>\n        /// <param name=\"offset\">Index offset for each XSigToken.</param>\n        /// <summary>\n        /// WriteXSigData method\n        /// </summary>\n        public void WriteXSigData(IEnumerable<XSigToken> tokens, int offset)\n        {\n            if (offset < 0)\n                throw new ArgumentOutOfRangeException(\"offset\", \"Offset must be greater than or equal to 0.\");\n\n            if (tokens != null)\n            {\n                foreach (var token in tokens)\n                {\n                    if (token == null) continue;\n                    var bytes = token.GetTokenWithOffset(offset).GetBytes();\n                    _stream.Write(bytes, 0, bytes.Length);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Dispose method\n        /// </summary>\n        public void Dispose()\n        {\n            if (!_leaveOpen)\n                _stream.Dispose();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials/AssetLoader.cs",
    "content": "using System;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Handles extracting embedded asset bundles and moving configuration files from the\n    /// application directory to the program file-path prefix at startup.\n    /// Implemented using <c>System.IO</c> types so it can run (and be tested) outside\n    /// of a Crestron runtime environment.\n    /// </summary>\n    internal static class AssetLoader\n    {\n        /// <summary>\n        /// Scans <paramref name=\"applicationDirectoryPath\"/> for well-known zip bundles and\n        /// JSON configuration files and deploys them to <paramref name=\"filePathPrefix\"/>.\n        /// </summary>\n        /// <param name=\"applicationDirectoryPath\">\n        /// The directory to scan (typically the Crestron application root).\n        /// </param>\n        /// <param name=\"filePathPrefix\">\n        /// The program's runtime working directory (e.g. <c>/nvram/program1/</c>).\n        /// </param>\n        internal static void Load(string applicationDirectoryPath, string filePathPrefix)\n        {\n            var applicationDirectory = new DirectoryInfo(applicationDirectoryPath);\n            Debug.LogMessage(LogEventLevel.Information,\n                \"Searching: {applicationDirectory:l} for embedded assets - {Destination}\",\n                applicationDirectory.FullName, filePathPrefix);\n\n            ExtractAssetsZip(applicationDirectory, filePathPrefix);\n            ExtractHtmlAssetsZip(applicationDirectory, filePathPrefix);\n            ExtractDevToolsZip(applicationDirectory, filePathPrefix);\n            MoveConfigurationFile(applicationDirectory, filePathPrefix);\n        }\n\n        // -------------------------------------------------------------------------\n        // Private helpers\n        // -------------------------------------------------------------------------\n\n        private static void ExtractAssetsZip(DirectoryInfo applicationDirectory, string filePathPrefix)\n        {\n            var zipFiles = applicationDirectory.GetFiles(\"assets*.zip\");\n\n            if (zipFiles.Length > 1)\n                throw new Exception(\"Multiple assets zip files found. Cannot continue.\");\n\n            if (zipFiles.Length == 1)\n            {\n                var zipFile = zipFiles[0];\n                var assetsRoot = Path.GetFullPath(filePathPrefix);\n                if (!assetsRoot.EndsWith(Path.DirectorySeparatorChar.ToString()) &&\n                    !assetsRoot.EndsWith(Path.AltDirectorySeparatorChar.ToString()))\n                {\n                    assetsRoot += Path.DirectorySeparatorChar;\n                }\n\n                Debug.LogMessage(LogEventLevel.Information,\n                    \"Found assets zip file: {zipFile:l}... Unzipping...\", zipFile.FullName);\n\n                using (var archive = ZipFile.OpenRead(zipFile.FullName))\n                {\n                    foreach (var entry in archive.Entries)\n                    {\n                        var destinationPath = Path.Combine(filePathPrefix, entry.FullName);\n                        var fullDest = Path.GetFullPath(destinationPath);\n                        if (!fullDest.StartsWith(assetsRoot, StringComparison.OrdinalIgnoreCase))\n                            throw new InvalidOperationException(\n                                $\"Entry '{entry.FullName}' is trying to extract outside of the target directory.\");\n\n                        if (string.IsNullOrEmpty(entry.Name))\n                        {\n                            Directory.CreateDirectory(destinationPath);\n                            continue;\n                        }\n\n                        if (Directory.Exists(destinationPath))\n                            Directory.Delete(destinationPath, recursive: true);\n\n                        Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));\n                        entry.ExtractToFile(destinationPath, overwrite: true);\n                        Debug.LogMessage(LogEventLevel.Information,\n                            \"Extracted: {entry:l} to {Destination}\", entry.FullName, destinationPath);\n                    }\n                }\n            }\n\n            foreach (var file in zipFiles)\n                File.Delete(file.FullName);\n        }\n\n        private static void ExtractHtmlAssetsZip(DirectoryInfo applicationDirectory, string filePathPrefix)\n        {\n            var htmlZipFiles = applicationDirectory.GetFiles(\"htmlassets*.zip\");\n\n            if (htmlZipFiles.Length > 1)\n                throw new Exception(\n                    \"Multiple htmlassets zip files found in application directory. \" +\n                    \"Please ensure only one htmlassets*.zip file is present and retry.\");\n\n            if (htmlZipFiles.Length == 1)\n            {\n                var htmlZipFile = htmlZipFiles[0];\n                var programDir = new DirectoryInfo(\n                    filePathPrefix.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));\n                var userOrNvramDir = programDir.Parent;\n                var rootDir = userOrNvramDir?.Parent;\n                if (rootDir == null)\n                    throw new Exception(\n                        $\"Unable to determine root directory for html extraction. Current path: {filePathPrefix}\");\n\n                var htmlDir = Path.Combine(rootDir.FullName, \"html\");\n                var htmlRoot = Path.GetFullPath(htmlDir);\n                if (!htmlRoot.EndsWith(Path.DirectorySeparatorChar.ToString()) &&\n                    !htmlRoot.EndsWith(Path.AltDirectorySeparatorChar.ToString()))\n                {\n                    htmlRoot += Path.DirectorySeparatorChar;\n                }\n\n                Debug.LogMessage(LogEventLevel.Information,\n                    \"Found htmlassets zip file: {zipFile:l}... Unzipping...\", htmlZipFile.FullName);\n\n                using (var archive = ZipFile.OpenRead(htmlZipFile.FullName))\n                {\n                    foreach (var entry in archive.Entries)\n                    {\n                        var destinationPath = Path.Combine(htmlDir, entry.FullName);\n                        var fullDest = Path.GetFullPath(destinationPath);\n                        if (!fullDest.StartsWith(htmlRoot, StringComparison.OrdinalIgnoreCase))\n                            throw new InvalidOperationException(\n                                $\"Entry '{entry.FullName}' is trying to extract outside of the target directory.\");\n\n                        if (string.IsNullOrEmpty(entry.Name))\n                        {\n                            Directory.CreateDirectory(destinationPath);\n                            continue;\n                        }\n\n                        if (File.Exists(destinationPath))\n                            File.Delete(destinationPath);\n\n                        var parentDir = Path.GetDirectoryName(destinationPath);\n                        if (!string.IsNullOrEmpty(parentDir))\n                            Directory.CreateDirectory(parentDir);\n\n                        entry.ExtractToFile(destinationPath, overwrite: true);\n                        Debug.LogMessage(LogEventLevel.Information,\n                            \"Extracted: {entry:l} to {Destination}\", entry.FullName, destinationPath);\n                    }\n                }\n            }\n\n            foreach (var file in htmlZipFiles)\n                File.Delete(file.FullName);\n        }\n\n        private static void ExtractDevToolsZip(DirectoryInfo applicationDirectory, string filePathPrefix)\n        {\n            var devToolsZipFiles = applicationDirectory.GetFiles(\"essentials-devtools*.zip\");\n\n            if (devToolsZipFiles.Length > 1)\n                throw new Exception(\n                    \"Multiple essentials-devtools zip files found in application directory. \" +\n                    \"Please ensure only one essentials-devtools*.zip file is present and retry.\");\n\n            if (devToolsZipFiles.Length == 1)\n            {\n                var devToolsZipFile = devToolsZipFiles[0];\n                var programDir = new DirectoryInfo(\n                    filePathPrefix.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));\n                var userOrNvramDir = programDir.Parent;\n                var rootDir = userOrNvramDir?.Parent;\n                if (rootDir == null)\n                    throw new Exception(\n                        $\"Unable to determine root directory for debug html extraction. Current path: {filePathPrefix}\");\n\n                var debugDir = Path.Combine(rootDir.FullName, \"html\", \"debug\");\n                var debugRoot = Path.GetFullPath(debugDir);\n                if (!debugRoot.EndsWith(Path.DirectorySeparatorChar.ToString()) &&\n                    !debugRoot.EndsWith(Path.AltDirectorySeparatorChar.ToString()))\n                {\n                    debugRoot += Path.DirectorySeparatorChar;\n                }\n\n                Debug.LogMessage(LogEventLevel.Information,\n                    \"Found essentials-devtools zip file: {zipFile:l}... Unzipping to {Destination}...\",\n                    devToolsZipFile.FullName, debugDir);\n\n                using (var archive = ZipFile.OpenRead(devToolsZipFile.FullName))\n                {\n                    foreach (var entry in archive.Entries)\n                    {\n                        var destinationPath = Path.Combine(debugDir, entry.FullName);\n                        var fullDest = Path.GetFullPath(destinationPath);\n                        if (!fullDest.StartsWith(debugRoot, StringComparison.OrdinalIgnoreCase))\n                            throw new InvalidOperationException(\n                                $\"Entry '{entry.FullName}' is trying to extract outside of the target directory.\");\n\n                        if (string.IsNullOrEmpty(entry.Name))\n                        {\n                            Directory.CreateDirectory(destinationPath);\n                            continue;\n                        }\n\n                        if (File.Exists(destinationPath))\n                            File.Delete(destinationPath);\n\n                        var parentDir = Path.GetDirectoryName(destinationPath);\n                        if (!string.IsNullOrEmpty(parentDir))\n                            Directory.CreateDirectory(parentDir);\n\n                        entry.ExtractToFile(destinationPath, overwrite: true);\n                        Debug.LogMessage(LogEventLevel.Information,\n                            \"Extracted: {entry:l} to {Destination}\", entry.FullName, destinationPath);\n                    }\n                }\n            }\n\n            foreach (var file in devToolsZipFiles)\n                File.Delete(file.FullName);\n        }\n\n        private static void MoveConfigurationFile(DirectoryInfo applicationDirectory, string filePathPrefix)\n        {\n            var jsonFiles = applicationDirectory.GetFiles(\"*configurationFile*.json\");\n\n            if (jsonFiles.Length > 1)\n            {\n                Debug.LogError(\"Multiple configuration files found in application directory: {@jsonFiles}\",\n                    jsonFiles.Select(f => f.FullName).ToArray());\n                throw new Exception(\"Multiple configuration files found. Cannot continue.\");\n            }\n\n            if (jsonFiles.Length == 1)\n            {\n                var jsonFile = jsonFiles[0];\n                var finalPath = Path.Combine(filePathPrefix, jsonFile.Name);\n                Debug.LogMessage(LogEventLevel.Information,\n                    \"Found configuration file: {jsonFile:l}... Moving to: {Destination}\",\n                    jsonFile.FullName, finalPath);\n\n                if (File.Exists(finalPath))\n                {\n                    Debug.LogMessage(LogEventLevel.Information,\n                        \"Removing existing configuration file: {Destination}\", finalPath);\n                    File.Delete(finalPath);\n                }\n\n                jsonFile.MoveTo(finalPath);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials/ClassDiagram1.cd",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ClassDiagram MajorVersion=\"1\" MinorVersion=\"1\">\n  <Class Name=\"PepperDash.Essentials.EssentialsRoomVolumesConfig\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAQAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAIAgA=</HashCode>\n      <FileName>Audio\\EssentialsVolumeLevelConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsVolumeLevelConfig\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAEAAAAAAAAAAAAAAAAIAAAAgBAAAAAAAAAAA=</HashCode>\n      <FileName>Audio\\EssentialsVolumeLevelConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.ConfigReader\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAIAAAAAAAABAAAACAAIAAAAQAAAAAAAAAAEAA=</HashCode>\n      <FileName>Config\\ConfigReader.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.DeviceFactory\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAA=</HashCode>\n      <FileName>Config\\DeviceFactory.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsConfig\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAEAAAAAQAIAAAAAAAACAAAAAAAAAAAAAAEAAAA=</HashCode>\n      <FileName>Config\\EssentialsConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SystemTemplateConfigs\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Config\\EssentialsConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.ConfigTieLine\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAgEAAAAAAEAAAAAAAAAAAAAAAAAAAAAEAIAIA=</HashCode>\n      <FileName>Configuration ORIGINAL\\ConfigTieLine.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Configuration\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AEAAAAAAAAAAgQAAAAAAgAAAAAAAAAAAAAAgEDAADAQ=</HashCode>\n      <FileName>Configuration ORIGINAL\\Configuration.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.ConfigSourceList\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAQAAAAAAAQAAACAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Configuration ORIGINAL\\Configuration.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.ConfigSourceItem\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAgAAAAAAAEAAAAAAAAAAAAAAAAAgAAAAAAA=</HashCode>\n      <FileName>Configuration ORIGINAL\\Configuration.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.ConfigInfo\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>IQAAAAAEAAAAAAADAAACABQAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Configuration ORIGINAL\\Configuration.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SourceListConfigProperties\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAEAAAAAAAAAAAIAAAAAgAAAIAAA=</HashCode>\n      <FileName>Configuration ORIGINAL\\ConfigurationHelpers.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.DmFactory\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAQAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAA=</HashCode>\n      <FileName>Configuration ORIGINAL\\Factories\\DmFactory.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.FactoryHelper\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAEDAAKAA=</HashCode>\n      <FileName>Configuration ORIGINAL\\Factories\\FactoryHelper.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.IrOutPortConfig\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAABAAAA=</HashCode>\n      <FileName>Configuration ORIGINAL\\Factories\\FactoryHelper.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.ControlSystem\" Collapsed=\"true\">\n    <Position X=\"35\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAoAgAAIAEAACAAEAIEIBICAAAAAAABAAAAAAAAAAAA=</HashCode>\n      <FileName>ControlSystem.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Amplifier\" Collapsed=\"true\" BaseTypeListCollapsed=\"true\">\n    <Position X=\"21\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA=</HashCode>\n      <FileName>Devices\\Amplifier.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" Collapsed=\"true\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.CotijaConfig\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\CotijaConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.CotijaDdvc01RoomBridgePropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\CotijaConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.CotijaSystemController\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>CAAABAABAgACCAKGBIAAEyBAFAAACYSAgIAAAAJkAAA=</HashCode>\n      <FileName>Room\\Cotija\\CotijaSystemController.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.CotijaBridgeBase\" Collapsed=\"true\">\n    <Position X=\"9.5\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAQIQAAAAAAAAgAAAAAAQAAAAAAAAAAAAAAAABAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\RoomBridges\\CotijaBridgeBase.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.CotijaEssentialsHuddleSpaceRoomBridge\" Collapsed=\"true\">\n    <Position X=\"8.25\" Y=\"5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAIAAAAIABAAkgAABgIAAAAAAAAAEAAAAAAgBAAACA=</HashCode>\n      <FileName>Room\\Cotija\\RoomBridges\\CotijaEssentialsHuddleSpaceRoomBridge.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SourceSelectMessageContent\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\RoomBridges\\CotijaEssentialsHuddleSpaceRoomBridge.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsHuddleSpaceRoom\" Collapsed=\"true\">\n    <Position X=\"0.5\" Y=\"9\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>iQFBQAIAAgAAgQAEQACAMABAAABAACAQUAAQAAgCgBA=</HashCode>\n      <FileName>Room\\Types\\EssentialsHuddleSpaceRoom.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsHuddleVtc1Room\" Collapsed=\"true\">\n    <Position X=\"2.75\" Y=\"9\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>iQFBQAIAIgQMgQAEQAigMABAAABAADIwUACQAAgCgTE=</HashCode>\n      <FileName>Room\\Types\\EssentialsHuddleVtc1Room.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsPresentationRoom\" Collapsed=\"true\">\n    <Position X=\"5\" Y=\"9\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>iQAEAAIACkAAAQAOQASgMAAJAABgAAAQQAAQAAgCgBA=</HashCode>\n      <FileName>Room\\Types\\EssentialsPresentationRoom.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsRoomBase\" Collapsed=\"true\">\n    <Position X=\"2.75\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>gQAAEAICECgCAQAEAAAUIwIyAAAAgACiAgAAAQECAgA=</HashCode>\n      <FileName>Room\\Types\\EssentialsRoomBase.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.CrestronTouchpanelPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AICCIAAIGIAAAAAgCAAIAEAAAAAAAAAAAAEAIAAAAAA=</HashCode>\n      <FileName>UI\\CrestronTouchpanelPropertiesConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.UiSetupPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"8.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>UI\\CrestronTouchpanelPropertiesConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsTouchpanelController\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAIAEAAAAAAAAAAAAAAAAEAAAAACKIBAAgACAAAAAA=</HashCode>\n      <FileName>UI\\EssentialsTouchpanelController.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.HttpLogoServer\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAQAAAACAAAAABAAAAAAAAAABAAAAAAAAAAkAAAAA=</HashCode>\n      <FileName>UI\\HttpLogoServer.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.UIBoolJoin\" Collapsed=\"true\">\n    <Position X=\"35\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>j+jWCNqEIGzi4UTaTgyn37kpncQJK7L42VMLmMgTE5A=</HashCode>\n      <FileName>UI\\JoinConstants\\UIBoolJoin.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.UISmartObjectJoin\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"8.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>BAggBIABCQAAGAAQAAAACAACAAAAAAAAAAIAAAAAAAA=</HashCode>\n      <FileName>UI\\JoinConstants\\UISmartObjectJoin.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.UIStringJoin\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"8.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>BkBgIAgAAggOQAFGAYQIABACgCEBjkSQAUEAASIABCE=</HashCode>\n      <FileName>UI\\JoinConstants\\UIStringlJoin.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.UIUshortJoin\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"8.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAQAAAACAAAACAEAAIAAAAAAIABAIAAAABAAAEAAAAA=</HashCode>\n      <FileName>UI\\JoinConstants\\UIUshortJoin.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SubpageReferenceListActivityItem\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA=</HashCode>\n      <FileName>UI\\SubpageReferenceListActivityItem.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SubpageReferenceListButtonAndModeItem\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA=</HashCode>\n      <FileName>UI\\SubpageReferenceListCallStagingItem.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SubpageReferenceListSourceItem\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAABAAAAAAAAAAAAAAAAAAAAgAAAAACAAAAAABgAAA=</HashCode>\n      <FileName>UI\\SubpageReferenceListSourceItem.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.PanelDriverBase\" Collapsed=\"true\">\n    <Position X=\"8.75\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>CAAIkAAAAAAQEAAAEAACAAAAAIAEABAAAgAACAAAAAA=</HashCode>\n      <FileName>UIDrivers\\enums and base.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsPanelMainInterfaceDriver\" Collapsed=\"true\">\n    <Position X=\"1\" Y=\"1.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>ABAAAAAAAhgAAAAAEAAAAAAAAIAEAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\Essentials\\EssentialsPanelMainInterfaceDriver.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsPresentationPanelAvFunctionsDriver\" Collapsed=\"true\">\n    <Position X=\"3.25\" Y=\"1.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>gTEAIIAiCggFNCQ4EA4AWBkAKJCEqAAOS4CKMAQQJQA=</HashCode>\n      <FileName>UIDrivers\\Essentials\\EssentialsPresentationPanelAvFunctionsDriver.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsHuddlePanelAvFunctionsDriver\" Collapsed=\"true\">\n    <Position X=\"7.75\" Y=\"1.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>gRQAIICuAghENHQpEA4IWCkBMJDEsEAEC4CAMARQIBA=</HashCode>\n      <FileName>UIDrivers\\EssentialsHuddle\\EssentialsHuddlePanelAvFunctionsDriver.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.EssentialsHuddleVtc1PanelAvFunctionsDriver\" Collapsed=\"true\">\n    <Position X=\"12.25\" Y=\"1.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>GRwAIYC+oghAeHStEDAIWCdBMADEsBAcDwCAMARYIBg=</HashCode>\n      <FileName>UIDrivers\\EssentialsHuddleVTC\\EssentialsHuddleVtc1PanelAvFunctionsDriver.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.JoinedSigInterlock\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAQgAIAAAAAAAAAEAAAAAQAAAAEAAAAAAAAEAAAgAA=</HashCode>\n      <FileName>UIDrivers\\JoinedSigInterlock.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SingleSubpageModalAndBackDriver\" Collapsed=\"true\">\n    <Position X=\"14.5\" Y=\"1.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAEAAAQAAABIAEAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\Page Drivers\\SingleSubpageModalAndBackDriver.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SingleSubpageModalDriver\" Collapsed=\"true\">\n    <Position X=\"16.75\" Y=\"1.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAEAAAAAAABAAEAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\Page Drivers\\SingleSubpageModalDriver.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SigInterlock\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAQAAIAAAAAAAAIEAAAAAAAAAAEAAAAAAAAEAAAgAA=</HashCode>\n      <FileName>UIDrivers\\SigInterlock.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SmartObjectRoomsList\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAACAAACBAAAAAAAAAAAAAAAAAAAAIAAAAAAQA=</HashCode>\n      <FileName>UIDrivers\\SmartObjectRoomsList.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.SmartObjectRoomsListItem\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"7.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAQAAAAAAAAAAAABAAQAAAAAAAAAACAAEAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\SmartObjectRoomsList.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.VolumeDeviceChangeEventArgs\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"8.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAABAAAAAEAAAAAAAAAAAAAAAAAAAQAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\VolumeAndSourceChangeArgs.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase\" Collapsed=\"true\">\n    <Position X=\"8.25\" Y=\"9.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>IAGigRBpCgZwAIMSBBIbIgAAImAPtEBiAAgECpJgKQo=</HashCode>\n      <FileName>OTHER\\Fusion\\EssentialsHuddleSpaceFusionSystemControllerBase.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.FusionRoomExtensions\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAACgAIQAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\EssentialsHuddleSpaceFusionSystemControllerBase.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.FusionStaticAssetExtensions\" Collapsed=\"true\">\n    <Position X=\"35\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAQAAACAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\EssentialsHuddleSpaceFusionSystemControllerBase.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.EssentialsHuddleVtc1FusionController\" Collapsed=\"true\">\n    <Position X=\"8.25\" Y=\"11\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AACAAAAAAgAAAIAAAAAAAAAgIBAQAAAAAAAAAAAAAQA=</HashCode>\n      <FileName>OTHER\\Fusion\\EssentialsHuddleVtc1FusionController.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.ScheduleChangeEventArgs\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionEventHandlers.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.MeetingChangeEventArgs\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionEventHandlers.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.ProcessorProgReg\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionProcessorQueries.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.ProcessorProgramItem\" Collapsed=\"true\">\n    <Position X=\"35\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAEAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionProcessorQueries.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.FusionRoomGuids\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAICAAAAAAAAAAAAAAAAAACAAAATQAAAAAAAABAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.FusionOccupancySensorAsset\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAQAQAAAAAAAAQAAAAAAQAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.FusionAsset\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAQAQAAAAAAAAQAAAAAAQAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.RoomSchedule\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.LocalTimeRequest\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.RequestSchedule\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAACACAAAAQAAAAAAAAAAAAAAAAAAAACAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.RequestAction\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAACAAAgAAQAAAAAAAAAAAAAAAAAAIAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.ActionResponse\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAgAAQAAAAAAAAAAAAAAAAAAIAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.Parameter\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.ScheduleResponse\" Collapsed=\"true\">\n    <Position X=\"35\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAACAAAAAAQAAAAAAAAAAgAAAAAAAAAAABAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.Event\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AABCAAAAYEAAEBIBAIAAJAQAQKAYAAAIEAAAEAACCgg=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.Resources\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.Rooms\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.Room\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAgAAAAAAAAAQAAAAAAAAAAAAAAAAEAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.Attendees\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"0.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAABAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.Required\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.Optional\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.MeetingType\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.MeetingTypes\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.LiveMeeting\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAgAQAAIAAAAAAACAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Fusion.LiveMeetingURL\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"5.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>OTHER\\Fusion\\FusionRviDataClasses.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.DDVC01RoomPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"5\" Y=\"6.25\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAIAgAAQAAAAAAAAEAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\DDVC01RoomPropertiesConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.DDVC01SpeedDial\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAEAAAAQAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\DDVC01RoomPropertiesConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsHuddleRoomPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"2.75\" Y=\"4.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAgAAAAAAAAAAACAAAAAABAAAAAAAAABA=</HashCode>\n      <FileName>Room\\Config\\EssentialsHuddleRoomPropertiesConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsHuddleVtc1PropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"5\" Y=\"4.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAgAAAAAAAAAAACIAAAAABAAAAAAAAABA=</HashCode>\n      <FileName>Room\\Config\\EssentialsHuddleVtc1PropertiesConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsPresentationRoomPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"0.5\" Y=\"4.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAACAIAAAAAAAAAAABAACAAAAAABAAAAAAAAABA=</HashCode>\n      <FileName>Room\\Config\\EssentialsPresentationPropertiesConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsRoomConfig\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAEAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsRoomPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"2.75\" Y=\"3.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AABAEQAAAAEoAAEEAAQAAAACAAAAAAgggAAAAQAAAgA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsLightingPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsRoomMicrophonePrivacyConfig\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAEAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsHelpPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"35\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAIAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsOneButtonMeetingPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsRoomAddressPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAQAAAgAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsLogoPropertiesConfig\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIACQAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsRoomOccSensorConfig\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AACAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsRoomTechConfig\" Collapsed=\"true\">\n    <Position X=\"35\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsRoomEmergencyConfig\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAABAACAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomEmergencyConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Config.EssentialsRoomEmergencyTriggerConfig\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"2.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAABQAAAAAAAAA=</HashCode>\n      <FileName>Room\\Config\\EssentialsRoomEmergencyConfig.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.CotijaDdvc01DeviceBridge\" Collapsed=\"true\" BaseTypeListCollapsed=\"true\">\n    <Position X=\"21\" Y=\"1.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AUAAAAgAAAACwAYAAAAAAAEAAAAAAAAAIcAAAEAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\CotijaDdvc01DeviceBridge.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" Collapsed=\"true\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.IChannelExtensions\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\DeviceTypeInterfaces\\IChannelExtensions.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.IColorExtensions\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\DeviceTypeInterfaces\\IColorExtensions.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.IDPadExtensions\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\DeviceTypeInterfaces\\IDPadExtensions.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.IDvrExtensions\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\DeviceTypeInterfaces\\IDvrExtensions.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.INumericExtensions\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\DeviceTypeInterfaces\\INumericExtensions.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.IPowerExtensions\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\DeviceTypeInterfaces\\IPowerExtensions.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.ISetTopBoxControlsExtensions\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\DeviceTypeInterfaces\\ISetTopBoxControlsExtensions.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.ITransportExtensions\" Collapsed=\"true\">\n    <Position X=\"35\" Y=\"4.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\DeviceTypeInterfaces\\ITransportExtensions.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.Cotija.CotijaDdvc01RoomBridge\" Collapsed=\"true\">\n    <Position X=\"10.5\" Y=\"5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>gACIAAAAAAICAFAAAACAAAEIIAAAAAQAQAAAABAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\RoomBridges\\CotijaDdvc01RoomBridge.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.EssentialsRoomEmergencyBase\" Collapsed=\"true\">\n    <Position X=\"8.25\" Y=\"6.5\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Emergency\\EsentialsRoomEmergencyContactClosure.cs</FileName>\n    </TypeIdentifier>\n    <Lollipop Position=\"0.2\" />\n  </Class>\n  <Class Name=\"PepperDash.Essentials.Room.EssentialsRoomEmergencyContactClosure\" Collapsed=\"true\">\n    <Position X=\"8.25\" Y=\"7.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAASBAAAAAAAAAAAAAABAAAAAAAEAA=</HashCode>\n      <FileName>Room\\Emergency\\EsentialsRoomEmergencyContactClosure.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver\" Collapsed=\"true\">\n    <Position X=\"5.5\" Y=\"1.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAQAAAAAiACDAAAGCAACgAIAABECBAgQAAAAQAAAAA=</HashCode>\n      <FileName>UIDrivers\\EssentialsHuddle\\EssentialsHuddleTechPageDriver.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Class Name=\"PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver\" Collapsed=\"true\">\n    <Position X=\"10\" Y=\"1.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>XAASAoAiAagwAcBAGAUURWQEOHQFAKCmAABCNSSEDPA=</HashCode>\n      <FileName>UIDrivers\\VC\\EssentialsVideoCodecUiDriver.cs</FileName>\n    </TypeIdentifier>\n  </Class>\n  <Interface Name=\"PepperDash.Essentials.IHasCurrentSourceInfoChange\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"9.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Types\\EssentialsRoomBase.cs</FileName>\n    </TypeIdentifier>\n  </Interface>\n  <Interface Name=\"PepperDash.Essentials.IAVDriver\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"9.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAgAAAAAAAAACBAAACAAAAIBAAAEAAAEAgAAAAAAIAA=</HashCode>\n      <FileName>UIDrivers\\EssentialsHuddleVTC\\EssentialsHuddleVtc1PanelAvFunctionsDriver.cs</FileName>\n    </TypeIdentifier>\n  </Interface>\n  <Interface Name=\"PepperDash.Essentials.Room.Cotija.IDelayedConfiguration\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"9.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\Interfaces.cs</FileName>\n    </TypeIdentifier>\n  </Interface>\n  <Enum Name=\"PepperDash.Essentials.eShutdownType\" Collapsed=\"true\">\n    <Position X=\"29.75\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAQAAAAAAACAAAAAAAAAAAAAAAAAAAAAEACAA=</HashCode>\n      <FileName>Room\\Types\\EssentialsRoomBase.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Enum Name=\"PepperDash.Essentials.eVacancyMode\" Collapsed=\"true\">\n    <Position X=\"31.5\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAACAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAA=</HashCode>\n      <FileName>Room\\Types\\EssentialsRoomBase.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Enum Name=\"PepperDash.Essentials.eWarmingCoolingMode\" Collapsed=\"true\">\n    <Position X=\"33.25\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAIEAAAA=</HashCode>\n      <FileName>Room\\Types\\EssentialsRoomBase.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Enum Name=\"PepperDash.Essentials.eAvSubpageType\" Collapsed=\"true\">\n    <Position X=\"24.5\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AIAAAAAAAAAAAAAAAhBAAAAAAAAAAACYAAAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\enums and base.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Enum Name=\"PepperDash.Essentials.eAvSourceSubpageType\" Collapsed=\"true\">\n    <Position X=\"22.75\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAABAAAAAAAAAAAAAAACAAAEAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\enums and base.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Enum Name=\"PepperDash.Essentials.eCommonSubpageType\" Collapsed=\"true\">\n    <Position X=\"28\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAEAAAAAAAAAIAAAAAAAAQAAAAQAAAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\enums and base.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Enum Name=\"PepperDash.Essentials.eAvSmartObjects\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAABAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\enums and base.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Enum Name=\"PepperDash.Essentials.eCommonSmartObjects\" Collapsed=\"true\">\n    <Position X=\"26.25\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\enums and base.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Enum Name=\"PepperDash.Essentials.ChangeType\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"10.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>UIDrivers\\VolumeAndSourceChangeArgs.cs</FileName>\n    </TypeIdentifier>\n  </Enum>\n  <Delegate Name=\"PepperDash.Essentials.PressAndHoldAction\" Collapsed=\"true\">\n    <Position X=\"19.25\" Y=\"11.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>\n      <FileName>Room\\Cotija\\RoomBridges\\CotijaEssentialsHuddleSpaceRoomBridge.cs</FileName>\n    </TypeIdentifier>\n  </Delegate>\n  <Delegate Name=\"PepperDash.Essentials.SourceInfoChangeHandler\" Collapsed=\"true\">\n    <Position X=\"21\" Y=\"11.75\" Width=\"1.5\" />\n    <TypeIdentifier>\n      <HashCode>AAAAAAAAAAAAACAAACAAAAAAAAAAAAAAAAAAACAAAAA=</HashCode>\n      <FileName>UIDrivers\\VolumeAndSourceChangeArgs.cs</FileName>\n    </TypeIdentifier>\n  </Delegate>\n  <Font Name=\"Segoe UI\" Size=\"9\" />\n</ClassDiagram>"
  },
  {
    "path": "src/PepperDash.Essentials/ControlSystem.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Reflection;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.CrestronThread;\nusing Crestron.SimplSharpPro.Diagnostics;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Routing;\nusing PepperDash.Essentials.Core.Web;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Main control system class that inherits from CrestronControlSystem and manages program lifecycle\n    /// </summary>\n    public class ControlSystem : CrestronControlSystem, ILoadConfig\n    {\n        HttpLogoServer LogoServer;\n\n        private CTimer _startTimer;\n        private CEvent _initializeEvent;\n        private const long StartupTime = 500;\n\n        // private const string minimumFirmwareVersion = \"2.8006.00110\";\n\n        /// <summary>\n        /// Initializes a new instance of the ControlSystem class\n        /// </summary>\n        public ControlSystem()\n            : base()\n        {\n            Thread.MaxNumberOfUserThreads = 400;\n            Global.ControlSystem = this;\n            DeviceManager.Initialize(this);\n            SecretsManager.Initialize();\n            SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true;\n        }\n\n        /// <summary>\n        /// InitializeSystem method\n        /// </summary>\n        /// <inheritdoc />\n        public override void InitializeSystem()\n        {\n\n            // Get FW version and stop if it's too low to run this version of Essentials.  Must be greater than v2.8006.00110\n            // var fwVersion = InitialParametersClass.FirmwareVersion;\n\n            // Debug.LogInformation(\"Control System Hardware Version: {fwVersion}\", fwVersion);\n\n            // // split the version into parts and compare against minimumFirmwareVersion\n            // var versionParts = fwVersion.Split('.').Select(int.Parse).ToArray();\n            // var minParts = minimumFirmwareVersion.Split('.').Select(int.Parse).ToArray();\n            // if (versionParts.Length < minParts.Length\n            //     || versionParts[0] < minParts[0]\n            //     || (versionParts[0] == minParts[0] && versionParts[1] < minParts[1])\n            //     || (versionParts[0] == minParts[0] && versionParts[1] == minParts[1] && versionParts[2] <= minParts[2]))\n            // {\n            //     Debug.LogFatal(\"Firmware version {fwVersion} is too low to run this version of Essentials. Please upgrade to greater than v{minimumFirmwareVersion}.\", fwVersion, minimumFirmwareVersion);\n            //     return;\n            // }\n\n            // If the control system is a DMPS type, we need to wait to exit this method until all devices have had time to activate\n            // to allow any HD-BaseT DM endpoints to register first.\n            bool preventInitializationComplete = Global.ControlSystemIsDmpsType;\n            if (preventInitializationComplete)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"******************* InitializeSystem() Entering **********************\");\n\n                _startTimer = new CTimer(StartSystem, preventInitializationComplete, StartupTime);\n                _initializeEvent = new CEvent(true, false);\n                DeviceManager.AllDevicesRegistered += (o, a) =>\n                {\n                    _initializeEvent.Set();\n                };\n                _initializeEvent.Wait(30000);\n                Debug.LogMessage(LogEventLevel.Debug, \"******************* InitializeSystem() Exiting **********************\");\n\n                SystemMonitor.ProgramInitialization.ProgramInitializationComplete = true;\n            }\n            else\n            {\n                _startTimer = new CTimer(StartSystem, preventInitializationComplete, StartupTime);\n            }\n        }\n\n        private void StartSystem(object preventInitialization)\n        {\n            DeterminePlatform();\n\n            if (Debug.DoNotLoadConfigOnNextBoot)\n            {\n                CrestronConsole.AddNewConsoleCommand(s => CrestronInvoke.BeginInvoke((o) => GoWithLoad()), \"go\", \"Loads configuration file\",\n                    ConsoleAccessLevelEnum.AccessOperator);\n            }\n\n            CrestronConsole.AddNewConsoleCommand(PluginLoader.ReportAssemblyVersions, \"reportversions\", \"Reports the versions of the loaded assemblies\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(Core.DeviceFactory.GetDeviceFactoryTypes, \"gettypes\", \"Gets the device types that can be built. Accepts a filter string.\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(BridgeHelper.PrintJoinMap, \"getjoinmap\", \"map(s) for bridge or device on bridge [brKey [devKey]]\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(BridgeHelper.JoinmapMarkdown, \"getjoinmapmarkdown\"\n                , \"generate markdown of map(s) for bridge or device on bridge [brKey [devKey]]\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(s => Debug.LogMessage(LogEventLevel.Information, \"CONSOLE MESSAGE: {0}\", s), \"appdebugmessage\", \"Writes message to log\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(ListTieLines,\n            \"listtielines\", \"Prints out all tie lines. Usage: listtielines [signaltype]\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(VisualizeRoutes, \"visualizeroutes\",\n                \"Visualizes routes by signal type\",\n                ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(VisualizeCurrentRoutes, \"visualizecurrentroutes\",\n                \"Visualizes current active routes from DefaultCollection\",\n                ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(s =>\n            {\n                CrestronConsole.ConsoleCommandResponse\n                    (\"Current running configuration. This is the merged system and template configuration\" + CrestronEnvironment.NewLine);\n                CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject\n                    (ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented).Replace(Environment.NewLine, \"\\r\\n\"));\n            }, \"showconfig\", \"Shows the current running merged config\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(\n                PrintPortalInfo,\n                \"portalinfo\",\n                \"Shows portal URLS from configuration\",\n                ConsoleAccessLevelEnum.AccessOperator);\n\n\n            CrestronConsole.AddNewConsoleCommand(DeviceManager.GetRoutingPorts,\n                \"getroutingports\", \"Reports all routing ports, if any.  Requires a device key\", ConsoleAccessLevelEnum.AccessOperator);\n\n            DeviceManager.AddDevice(new EssentialsWebApi(\"essentialsWebApi\", \"Essentials Web API\"));\n\n            if (!Debug.DoNotLoadConfigOnNextBoot)\n            {\n                GoWithLoad();\n                return;\n            }\n\n            if (!(bool)preventInitialization)\n            {\n                SystemMonitor.ProgramInitialization.ProgramInitializationComplete = true;\n            }\n        }\n\n        private void PrintPortalInfo(string args)\n        {\n            if(ConfigReader.ConfigObject == null)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"No configuration loaded. Cannot show portal URLs.\");\n                return;\n            }\n\n             if (string.IsNullOrEmpty(ConfigReader.ConfigObject.SystemUrl) && string.IsNullOrEmpty(ConfigReader.ConfigObject.TemplateUrl))\n             {\n                 CrestronConsole.ConsoleCommandResponse(\"No portal URLs defined in config.\");\n                 return;\n             }\n\n            CrestronConsole.ConsoleCommandResponse(\n                \"This system can be found at the following URLs:{2}\" +\n                \"System URL:   {0}{2}\" +\n                \"Template URL: {1}{2}\",\n                ConfigReader.ConfigObject?.SystemUrl,\n                ConfigReader.ConfigObject?.TemplateUrl,\n                CrestronEnvironment.NewLine);\n        }\n\n        /// <summary>\n        /// DeterminePlatform method\n        /// </summary>\n        public void DeterminePlatform()\n        {\n            try\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Determining Platform...\");\n\n                string filePathPrefix;\n\n                var dirSeparator = Global.DirectorySeparator;\n\n                string directoryPrefix;\n\n                directoryPrefix = Directory.GetApplicationRootDirectory();\n\n                Global.SetAssemblyVersion(PluginLoader.GetAssemblyVersion(Assembly.GetExecutingAssembly()));\n\n                if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)   // Handles 3-series running Windows CE OS\n                {\n                    string userFolder;\n                    string nvramFolder;\n                    bool is4series = false;\n\n                    if (eCrestronSeries.Series4 == (Global.ProcessorSeries & eCrestronSeries.Series4)) // Handle 4-series\n                    {\n                        is4series = true;\n                        // Set path to user/\n                        userFolder = \"user\";\n                        nvramFolder = \"nvram\";\n                    }\n                    else\n                    {\n                        userFolder = \"User\";\n                        nvramFolder = \"Nvram\";\n                    }\n\n                    Debug.LogMessage(LogEventLevel.Information, \"Starting Essentials v{version:l} on {processorSeries:l} Appliance\", Global.AssemblyVersion, is4series ? \"4-series\" : \"3-series\");\n                    //Debug.LogMessage(LogEventLevel.Information, \"Starting Essentials v{0} on {1} Appliance\", Global.AssemblyVersion, is4series ? \"4-series\" : \"3-series\");\n\n                    // Check if User/ProgramX exists\n                    if (Directory.Exists(Global.ApplicationDirectoryPathPrefix + dirSeparator + userFolder\n                        + dirSeparator + string.Format(\"program{0}\", InitialParametersClass.ApplicationNumber)))\n                    {\n\n                        Debug.LogMessage(LogEventLevel.Information, \"{userFolder:l}/program{applicationNumber} directory found\", userFolder, InitialParametersClass.ApplicationNumber);\n                        filePathPrefix = directoryPrefix + dirSeparator + userFolder\n                        + dirSeparator + string.Format(\"program{0}\", InitialParametersClass.ApplicationNumber) + dirSeparator;\n                    }\n                    // Check if Nvram/Programx exists\n                    else if (Directory.Exists(directoryPrefix + dirSeparator + nvramFolder\n                        + dirSeparator + string.Format(\"program{0}\", InitialParametersClass.ApplicationNumber)))\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, \"{nvramFolder:l}/program{applicationNumber} directory found\", nvramFolder, InitialParametersClass.ApplicationNumber);\n\n                        filePathPrefix = directoryPrefix + dirSeparator + nvramFolder\n                        + dirSeparator + string.Format(\"program{0}\", InitialParametersClass.ApplicationNumber) + dirSeparator;\n                    }\n                    // If neither exists, set path to User/ProgramX\n                    else\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, \"{userFolder:l}/program{applicationNumber} directory found\", userFolder, InitialParametersClass.ApplicationNumber);\n\n                        filePathPrefix = directoryPrefix + dirSeparator + userFolder\n                        + dirSeparator + string.Format(\"program{0}\", InitialParametersClass.ApplicationNumber) + dirSeparator;\n                    }\n                }\n                else   // Handles Linux OS (Virtual Control)\n                {\n                    //Debug.SetDebugLevel(2);\n                    Debug.LogMessage(LogEventLevel.Information, \"Starting Essentials v{version:l} on Virtual Control Server\", Global.AssemblyVersion);\n\n                    // Set path to User/\n                    filePathPrefix = directoryPrefix + dirSeparator + \"User\" + dirSeparator;\n                }\n\n                Global.SetFilePathPrefix(filePathPrefix);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(e, \"Unable to determine platform due to exception\");\n            }\n        }\n\n        /// <summary>\n        /// GoWithLoad method\n        /// </summary>\n        public void GoWithLoad()\n        {\n            try\n            {\n                Debug.SetDoNotLoadConfigOnNextBoot(false);\n\n                PluginLoader.AddProgramAssemblies();\n\n                _ = new Core.DeviceFactory();\n\n                LoadAssets(Global.ApplicationDirectoryPathPrefix, Global.FilePathPrefix);\n\n                Debug.LogMessage(LogEventLevel.Information, \"Starting Essentials load from configuration\");\n\n                var filesReady = SetupFilesystem();\n                if (filesReady)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Checking for plugins\");\n                    PluginLoader.LoadPlugins();\n\n                    Debug.LogMessage(LogEventLevel.Information, \"Folder structure verified. Loading config...\");\n                    if (!ConfigReader.LoadConfig2() || ConfigReader.ConfigObject == null)\n                    {\n                        Debug.LogMessage(LogEventLevel.Warning, \"Unable to load config file.\");\n                    }\n\n                    Load();\n                    Debug.LogMessage(LogEventLevel.Information, \"Essentials load complete\");\n                }\n                else\n                {\n                    Debug.LogMessage(LogEventLevel.Information,\n                        @\"----------------------------------------------\n                        ------------------------------------------------\n                        ------------------------------------------------\n                        Essentials file structure setup completed.\n                        Please load config, sgd and ir files and\n                        restart program.\n                        ------------------------------------------------\n                        ------------------------------------------------\n                        ------------------------------------------------\");\n                }\n\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(e, \"FATAL INITIALIZE ERROR. System is in an inconsistent state\");\n            }\n            finally\n            {\n                // Notify the OS that the program intitialization has completed\n                SystemMonitor.ProgramInitialization.ProgramInitializationComplete = true;\n            }\n\n        }\n\n\n\n        /// <summary>\n        /// Verifies filesystem is set up. IR, SGD, and programX folders\n        /// </summary>\n        bool SetupFilesystem()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Verifying and/or creating folder structure\");\n            var configDir = Global.FilePathPrefix;\n\n            Debug.LogMessage(LogEventLevel.Information, \"FilePathPrefix: {filePathPrefix:l}\", configDir);\n            var configExists = Directory.Exists(configDir);\n            if (!configExists)\n                Directory.Create(configDir);\n\n            var irDir = Global.FilePathPrefix + \"ir\";\n            if (!Directory.Exists(irDir))\n                Directory.Create(irDir);\n\n            var sgdDir = Global.FilePathPrefix + \"sgd\";\n            if (!Directory.Exists(sgdDir))\n                Directory.Create(sgdDir);\n\n            var pluginDir = Global.FilePathPrefix + \"plugins\";\n            if (!Directory.Exists(pluginDir))\n                Directory.Create(pluginDir);\n\n            var joinmapDir = Global.FilePathPrefix + \"joinmaps\";\n            if (!Directory.Exists(joinmapDir))\n                Directory.Create(joinmapDir);\n\n            return configExists;\n        }\n\n        /// <summary>\n        /// TearDown method\n        /// </summary>\n        public void TearDown()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Tearing down existing system\");\n            DeviceManager.DeactivateAll();\n\n            TieLineCollection.Default.Clear();\n\n            foreach (var key in DeviceManager.GetDevices())\n                DeviceManager.RemoveDevice(key);\n\n            Debug.LogMessage(LogEventLevel.Information, \"Tear down COMPLETE\");\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        void Load()\n        {\n            LoadDevices();\n            LoadRooms();\n            LoadLogoServer();\n\n            DeviceManager.ActivateAll();\n\n            LoadTieLines();\n\n            /*var mobileControl = GetMobileControlDevice();\n\n\t\t    if (mobileControl == null) return;\n\n            mobileControl.LinkSystemMonitorToAppServer();*/\n\n        }\n\n        /// <summary>\n        /// LoadDevices method\n        /// </summary>\n        public void LoadDevices()\n        {\n\n            // Build the processor wrapper class\n            DeviceManager.AddDevice(new Core.Devices.CrestronProcessor(\"processor\"));\n\n            DeviceManager.AddDevice(new RoutingFeedbackManager($\"routingFeedbackManager\", \"Routing Feedback Manager\"));\n\n            // Add global System Monitor device\n            if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)\n            {\n                DeviceManager.AddDevice(\n                    new Core.Monitoring.SystemMonitorController(\"systemMonitor\"));\n            }\n\n            if (ConfigReader.ConfigObject is null)\n            {\n                Debug.LogMessage(LogEventLevel.Warning, \"LoadDevices: ConfigObject is null. Cannot load devices.\");\n                return;\n            }\n\n            foreach (var devConf in ConfigReader.ConfigObject.Devices)\n            {\n                IKeyed newDev = null;\n\n                try\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Creating device '{deviceKey:l}', type '{deviceType:l}'\", devConf.Key, devConf.Type);\n                    // Skip this to prevent unnecessary warnings\n                    if (devConf.Key == \"processor\")\n                    {\n                        var prompt = Global.ControlSystem.ControllerPrompt;\n\n                        var typeMatch = string.Equals(devConf.Type, prompt, StringComparison.OrdinalIgnoreCase) ||\n                                        string.Equals(devConf.Type, prompt.Replace(\"-\", \"\"), StringComparison.OrdinalIgnoreCase);\n\n                        if (!typeMatch)\n                            Debug.LogMessage(LogEventLevel.Information,\n                                \"WARNING: Config file defines processor type as '{deviceType:l}' but actual processor is '{processorType:l}'!  Some ports may not be available\",\n                                devConf.Type.ToUpper(), Global.ControlSystem.ControllerPrompt.ToUpper());\n\n\n                        continue;\n                    }\n\n\n                    if (newDev == null)\n                        newDev = Core.DeviceFactory.GetDevice(devConf);\n\n                    if (newDev != null)\n                        DeviceManager.AddDevice(newDev);\n                    else\n                        Debug.LogMessage(LogEventLevel.Information, \"ERROR: Cannot load unknown device type '{deviceType:l}', key '{deviceKey:l}'.\", devConf.Type, devConf.Key);\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(e, \"ERROR: Creating device {deviceKey:l}. Skipping device.\", args: new[] { devConf.Key });\n                }\n            }\n            Debug.LogMessage(LogEventLevel.Information, \"All Devices Loaded.\");\n\n        }\n\n\n        /// <summary>\n        /// LoadTieLines method\n        /// </summary>\n        public void LoadTieLines()\n        {\n            // In the future, we can't necessarily just clear here because devices\n            // might be making their own internal sources/tie lines\n\n            var tlc = TieLineCollection.Default;\n\n            if (ConfigReader.ConfigObject?.TieLines == null)\n            {\n                return;\n            }\n\n            foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines)\n            {\n                var newTL = tieLineConfig.GetTieLine();\n                if (newTL != null)\n                    tlc.Add(newTL);\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, \"All Tie Lines Loaded.\");\n\n            Extensions.MapDestinationsToSources();\n\n            Debug.LogMessage(LogEventLevel.Information, \"All Routes Mapped.\");\n        }\n\n\n\n        /// <summary>\n        /// Visualizes routes in a tree format for better understanding of signal paths\n        /// </summary>\n        private void ListTieLines(string args)\n        {\n            try\n            {\n                if (!string.IsNullOrEmpty(args) && args.Contains(\"?\"))\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"Usage: listtielines [signaltype]\\r\\n\");\n                    CrestronConsole.ConsoleCommandResponse(\"Signal types: Audio, Video, SecondaryAudio, AudioVideo, UsbInput, UsbOutput\\r\\n\");\n                    return;\n                }\n\n                eRoutingSignalType? signalTypeFilter = null;\n                if (!string.IsNullOrEmpty(args))\n                {\n                    eRoutingSignalType parsedType;\n                    if (Enum.TryParse(args.Trim(), true, out parsedType))\n                    {\n                        signalTypeFilter = parsedType;\n                    }\n                    else\n                    {\n                        CrestronConsole.ConsoleCommandResponse(\"Invalid signal type: {0}\\r\\n\", args.Trim());\n                        CrestronConsole.ConsoleCommandResponse(\"Valid types: Audio, Video, SecondaryAudio, AudioVideo, UsbInput, UsbOutput\\r\\n\");\n                        return;\n                    }\n                }\n\n                var tielines = signalTypeFilter.HasValue\n                    ? TieLineCollection.Default.Where(tl => tl.Type.HasFlag(signalTypeFilter.Value))\n                    : TieLineCollection.Default;\n\n                var count = 0;\n                foreach (var tl in tielines)\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"  {0}{1}\", tl, CrestronEnvironment.NewLine);\n                    count++;\n                }\n\n                CrestronConsole.ConsoleCommandResponse(\"\\r\\nTotal: {0} tieline{1}{2}\", count, count == 1 ? \"\" : \"s\", CrestronEnvironment.NewLine);\n            }\n            catch (Exception ex)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Error listing tielines: {0}\\r\\n\", ex.Message);\n            }\n        }\n\n        private void VisualizeRoutes(string args)\n        {\n            try\n            {\n                if (!string.IsNullOrEmpty(args) && args.Contains(\"?\"))\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"Usage: visualizeroutes [signaltype] [-s source] [-d destination]\\r\\n\");\n                    CrestronConsole.ConsoleCommandResponse(\"  signaltype: Audio, Video, AudioVideo, etc.\\r\\n\");\n                    CrestronConsole.ConsoleCommandResponse(\"  -s: Filter by source key (partial match)\\r\\n\");\n                    CrestronConsole.ConsoleCommandResponse(\"  -d: Filter by destination key (partial match)\\r\\n\");\n                    return;\n                }\n\n                ParseRouteFilters(args, out eRoutingSignalType? signalTypeFilter, out string sourceFilter, out string destFilter);\n\n                CrestronConsole.ConsoleCommandResponse(\"\\r\\n+===========================================================================+\\r\\n\");\n                CrestronConsole.ConsoleCommandResponse(\"|                         ROUTE VISUALIZATION                               |\\r\\n\");\n                CrestronConsole.ConsoleCommandResponse(\"+===========================================================================+\\r\\n\\r\\n\");\n\n                foreach (var descriptorCollection in Extensions.RouteDescriptors.Where(kv => kv.Value.Descriptors.Count() > 0))\n                {\n                    // Filter by signal type if specified\n                    if (signalTypeFilter.HasValue && descriptorCollection.Key != signalTypeFilter.Value)\n                        continue;\n\n                    CrestronConsole.ConsoleCommandResponse(\"\\r\\n+--- Signal Type: {0} ({1} routes) ---\\r\\n\",\n                        descriptorCollection.Key,\n                        descriptorCollection.Value.Descriptors.Count());\n\n                    foreach (var descriptor in descriptorCollection.Value.Descriptors)\n                    {\n                        // Filter by source/dest if specified\n                        if (sourceFilter != null && !descriptor.Source.Key.ToLower().Contains(sourceFilter))\n                            continue;\n                        if (destFilter != null && !descriptor.Destination.Key.ToLower().Contains(destFilter))\n                            continue;\n\n                        VisualizeRouteDescriptor(descriptor);\n                    }\n                }\n\n                CrestronConsole.ConsoleCommandResponse(\"\\r\\n\");\n            }\n            catch (Exception ex)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Error visualizing routes: {0}\\r\\n\", ex.Message);\n            }\n        }\n\n        private void VisualizeCurrentRoutes(string args)\n        {\n            try\n            {\n                if (!string.IsNullOrEmpty(args) && args.Contains(\"?\"))\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"Usage: visualizecurrentroutes [signaltype] [-s source] [-d destination]\\r\\n\");\n                    CrestronConsole.ConsoleCommandResponse(\"  signaltype: Audio, Video, AudioVideo, etc.\\r\\n\");\n                    CrestronConsole.ConsoleCommandResponse(\"  -s: Filter by source key (partial match)\\r\\n\");\n                    CrestronConsole.ConsoleCommandResponse(\"  -d: Filter by destination key (partial match)\\r\\n\");\n                    return;\n                }\n\n                ParseRouteFilters(args, out eRoutingSignalType? signalTypeFilter, out string sourceFilter, out string destFilter);\n\n                CrestronConsole.ConsoleCommandResponse(\"\\r\\n+===========================================================================+\\r\\n\");\n                CrestronConsole.ConsoleCommandResponse(\"|                    CURRENT ROUTES VISUALIZATION                            |\\r\\n\");\n                CrestronConsole.ConsoleCommandResponse(\"+===========================================================================+\\r\\n\\r\\n\");\n\n                var hasRoutes = false;\n\n                // Get all descriptors from DefaultCollection\n                var allDescriptors = RouteDescriptorCollection.DefaultCollection.Descriptors;\n\n                // Group by signal type\n                var groupedByType = allDescriptors.GroupBy(d => d.SignalType);\n\n                foreach (var group in groupedByType)\n                {\n                    var signalType = group.Key;\n\n                    // Filter by signal type if specified\n                    if (signalTypeFilter.HasValue && signalType != signalTypeFilter.Value)\n                        continue;\n\n                    var filteredDescriptors = group.Where(d =>\n                    {\n                        if (sourceFilter != null && !d.Source.Key.ToLower().Contains(sourceFilter))\n                            return false;\n                        if (destFilter != null && !d.Destination.Key.ToLower().Contains(destFilter))\n                            return false;\n                        return true;\n                    }).ToList();\n\n                    if (filteredDescriptors.Count == 0)\n                        continue;\n\n                    hasRoutes = true;\n                    CrestronConsole.ConsoleCommandResponse(\"\\r\\n+--- Signal Type: {0} ({1} routes) ---\\r\\n\",\n                        signalType,\n                        filteredDescriptors.Count);\n\n                    foreach (var descriptor in filteredDescriptors)\n                    {\n                        VisualizeRouteDescriptor(descriptor);\n                    }\n                }\n\n                if (!hasRoutes)\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"\\r\\nNo active routes found in current state.\\r\\n\");\n                }\n\n                CrestronConsole.ConsoleCommandResponse(\"\\r\\n\");\n            }\n            catch (Exception ex)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Error visualizing current state: {0}\\r\\n\", ex.Message);\n            }\n        }\n\n        /// <summary>\n        /// Parses route filter arguments from command line\n        /// </summary>\n        /// <param name=\"args\">Command line arguments</param>\n        /// <param name=\"signalTypeFilter\">Parsed signal type filter (if any)</param>\n        /// <param name=\"sourceFilter\">Parsed source filter (if any)</param>\n        /// <param name=\"destFilter\">Parsed destination filter (if any)</param>\n        private void ParseRouteFilters(string args, out eRoutingSignalType? signalTypeFilter, out string sourceFilter, out string destFilter)\n        {\n            signalTypeFilter = null;\n            sourceFilter = null;\n            destFilter = null;\n\n            if (string.IsNullOrEmpty(args))\n                return;\n\n            var parts = args.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);\n\n            for (int i = 0; i < parts.Length; i++)\n            {\n                var part = parts[i];\n\n                // Check for flags\n                if (part == \"-s\" && i + 1 < parts.Length)\n                {\n                    sourceFilter = parts[++i].ToLower();\n                }\n                else if (part == \"-d\" && i + 1 < parts.Length)\n                {\n                    destFilter = parts[++i].ToLower();\n                }\n                // Try to parse as signal type if not a flag and no signal type set yet\n                else if (!part.StartsWith(\"-\") && !signalTypeFilter.HasValue)\n                {\n                    if (Enum.TryParse(part, true, out eRoutingSignalType parsedType))\n                    {\n                        signalTypeFilter = parsedType;\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// Visualizes a single route descriptor in a tree format\n        /// </summary>\n        private void VisualizeRouteDescriptor(RouteDescriptor descriptor)\n        {\n            CrestronConsole.ConsoleCommandResponse(\"|\\r\\n\");\n            CrestronConsole.ConsoleCommandResponse(\"|-- {0} --> {1}\\r\\n\",\n                descriptor.Source.Key,\n                descriptor.Destination.Key);\n\n            if (descriptor.Routes == null || descriptor.Routes.Count == 0)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"|   +-- (No switching steps)\\r\\n\");\n                return;\n            }\n\n            for (int i = 0; i < descriptor.Routes.Count; i++)\n            {\n                var route = descriptor.Routes[i];\n                var isLast = i == descriptor.Routes.Count - 1;\n                var prefix = isLast ? \"+\" : \"|\";\n                var continuation = isLast ? \" \" : \"|\";\n\n                if (route.SwitchingDevice != null)\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"|   {0}-- [{1}] {2}\\r\\n\",\n                        prefix,\n                        route.SwitchingDevice.Key,\n                        GetSwitchDescription(route));\n\n                    // Add visual connection line for non-last items\n                    if (!isLast)\n                        CrestronConsole.ConsoleCommandResponse(\"|   {0}      |\\r\\n\", continuation);\n                }\n                else\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"|   {0}-- {1}\\r\\n\", prefix, route.ToString());\n                }\n            }\n        }\n\n        /// <summary>\n        /// Gets a readable description of the switching operation\n        /// </summary>\n        private string GetSwitchDescription(RouteSwitchDescriptor route)\n        {\n            if (route.OutputPort != null && route.InputPort != null)\n            {\n                return string.Format(\"{0} -> {1}\", route.OutputPort.Key, route.InputPort.Key);\n            }\n            else if (route.InputPort != null)\n            {\n                return string.Format(\"-> {0}\", route.InputPort.Key);\n            }\n            else\n            {\n                return \"(passthrough)\";\n            }\n        }\n\n        /// <summary>\n        /// LoadRooms method\n        /// </summary>\n        public void LoadRooms()\n        {\n            if (ConfigReader.ConfigObject?.Rooms == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Notice: Configuration contains no rooms - Is this intentional?  This may be a valid configuration.\");\n                return;\n            }\n\n            foreach (var roomConfig in ConfigReader.ConfigObject.Rooms)\n            {\n                try\n                {\n                    var room = Core.DeviceFactory.GetDevice(roomConfig);\n\n                    if (room == null)\n                    {\n                        Debug.LogWarning(\"ERROR: Cannot load unknown room type '{roomType:l}', key '{roomKey:l}'.\", roomConfig.Type, roomConfig.Key);\n                        continue;\n                    }\n\n                    DeviceManager.AddDevice(room);\n                }\n                catch (Exception ex)\n                {\n                    Debug.LogMessage(ex, \"Exception loading room {roomKey}:{roomType}\", null, roomConfig.Key, roomConfig.Type);\n                    continue;\n                }\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, \"All Rooms Loaded.\");\n\n\n        }\n\n        /// <summary>\n        /// Fires up a logo server if not already running\n        /// </summary>\n        [Obsolete(\"Logo server is no longer supported and will be removed in a future release.\")]\n        void LoadLogoServer()\n        {\n            if (ConfigReader.ConfigObject?.Rooms == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"No rooms configured. Bypassing Logo server startup.\");\n                return;\n            }\n\n            if (ConfigReader.ConfigObject?.Rooms == null ||\n                !ConfigReader.ConfigObject.Rooms.Any(\n                    CheckRoomConfig))\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"No rooms configured to use system Logo server. Bypassing Logo server startup\");\n                return;\n            }\n\n            try\n            {\n                LogoServer = new HttpLogoServer(8080, Global.DirectorySeparator + \"html\" + Global.DirectorySeparator + \"logo\");\n            }\n            catch (Exception)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"NOTICE: Logo server cannot be started. Likely already running in another program\");\n            }\n        }\n\n        private bool CheckRoomConfig(DeviceConfig c)\n        {\n            string logoDark = null;\n            string logoLight = null;\n            string logo = null;\n\n            try\n            {\n                if (c.Properties[\"logoDark\"] != null)\n                {\n                    logoDark = c.Properties[\"logoDark\"].Value<string>(\"type\");\n                }\n\n                if (c.Properties[\"logoLight\"] != null)\n                {\n                    logoLight = c.Properties[\"logoLight\"].Value<string>(\"type\");\n                }\n\n                if (c.Properties[\"logo\"] != null)\n                {\n                    logo = c.Properties[\"logo\"].Value<string>(\"type\");\n                }\n\n                return ((logoDark != null && logoDark == \"system\") ||\n                        (logoLight != null && logoLight == \"system\") || (logo != null && logo == \"system\"));\n            }\n            catch\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Unable to find logo information in any room config\");\n                return false;\n            }\n        }\n\n        internal static void LoadAssets(string applicationDirectoryPath, string filePathPrefix) =>\n            AssetLoader.Load(applicationDirectoryPath, filePathPrefix);\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials/Example Configuration/EssentialsHuddleSpaceRoom/configurationFile-HuddleSpace-2-Source.json",
    "content": "{\n    \"system\": {},\n    \"system_url\": \"\",\n    \"template_url\": \"\",\n    \"template\": {\n        \"sourceLists\": {\n            \"default\": {\n                \"source-2\": {\n                    \"order\": 6,\n                    \"type\": \"route\",\n                    \"altIcon\": \"Blank\",\n                    \"icon\": \"\",\n                    \"sourceKey\": \"wePresent-1\",\n                    \"includeInSourceList\": true,\n                    \"volumeControlKey\": \"$defaultAudio\",\n                    \"routeList\": [\n                        {\n                            \"sourceKey\": \"wePresent-1\",\n                            \"type\": \"audioVideo\",\n                            \"destinationKey\": \"$defaultAll\"\n                        }\n                    ]\n                },\n                \"source-1\": {\n                    \"order\": 5,\n                    \"type\": \"route\",\n                    \"altIcon\": \"Blank\",\n                    \"icon\": \"\",\n                    \"sourceKey\": \"inRoomPc-1\",\n                    \"includeInSourceList\": true,\n                    \"volumeControlKey\": \"$defaultAudio\",\n                    \"routeList\": [\n                        {\n                            \"sourceKey\": \"inRoomPc-1\",\n                            \"type\": \"audioVideo\",\n                            \"destinationKey\": \"$defaultAll\"\n                        }\n                    ]\n                },\n                \"roomOff\": {\n                    \"sourceKey\": \"$off\",\n                    \"type\": \"off\",\n                    \"routeList\": [\n                        {\n                            \"sourceKey\": \"$off\",\n                            \"type\": \"audioVideo\",\n                            \"destinationKey\": \"$defaultAll\"\n                        }\n                    ]\n                }\n            }\n        },\n        \"devices\": [\n            {\n                \"name\": \"RMC3\",\n                \"group\": \"processor\",\n                \"properties\": {\n                    \"numberOfIrPorts\": 2,\n                    \"numberOfComPorts\": 1\n                },\n                \"supportedSystemTypes\": [\n                    \"hudType\",\n                    \"presType\",\n                    \"vtType\",\n                    \"custom\"\n                ],\n                \"type\": \"rmc3\",\n                \"supportedConfigModes\": [\n                    \"compliance\",\n                    \"essentials\"\n                ],\n                \"supportsCompliance\": true,\n                \"key\": \"processor\",\n                \"uid\": 0\n            },\n            {\n                \"name\": \"Room PC\",\n                \"key\": \"inRoomPc-1\",\n                \"type\": \"inRoomPc\",\n                \"group\": \"pc\",\n                \"uid\": 8,\n                \"properties\": {\n                    \"hasAudio\": true,\n                    \"hasControls\": false,\n                    \"isDefault\": true\n                }\n            },\n            {\n                \"name\": \"Wireless Video\",\n                \"key\": \"wePresent-1\",\n                \"type\": \"genericSource\",\n                \"group\": \"genericSource\",\n                \"uid\": 9,\n                \"properties\": {\n                    \"isDefault\": false\n                }\n            },\n            {\n                \"name\": \"Samsung QM Series Display\",\n                \"key\": \"display-1\",\n                \"type\": \"samsungmdc\",\n                \"group\": \"display\",\n                \"uid\": 11,\n                \"properties\": {\n                    \"id\": \"01\",\n                    \"control\": {\n                        \"controlPortDevKey\": \"processor\",\n                        \"comParams\": {\n                            \"parity\": \"None\",\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 9600,\n                            \"softwareHandshake\": \"None\",\n                            \"dataBits\": 8,\n                            \"hardwareHandshake\": \"None\",\n                            \"stopBits\": 1\n                        },\n                        \"controlPortNumber\": 1,\n                        \"method\": \"com\"\n                    }\n                }\n            },\n            {\n                \"name\": \"TSW-760\",\n                \"key\": \"tsw760-1\",\n                \"type\": \"tsw760\",\n                \"group\": \"touchpanel\",\n                \"uid\": 14,\n                \"properties\": {\n                    \"control\": {\n                        \"method\": \"ipid\",\n                        \"ipid\": \"03\",\n                        \"params\": {\n                            \"deviceReadyResponsePattern\": \".*>\",\n                            \"endOfLineString\": \"\\n\"\n                        }\n                    },\n                    \"showVolumeGauge\": true,\n                    \"roomListKey\": \"\",\n                    \"showDate\": true,\n                    \"headerStyle\": \"Verbose\",\n                    \"sgdFile\": \"PepperDash Essentials TSW-760.sgd\",\n                    \"showTime\": true,\n                    \"sourcesOverflowCount\": 4,\n                    \"usesSplashPage\": false,\n                    \"defaultRoomKey\": \"room1\"\n                }\n            },\n            {\n                \"name\": \"iPad\",\n                \"key\": \"crestronApp-1\",\n                \"type\": \"crestronApp\",\n                \"group\": \"touchpanel\",\n                \"uid\": 15,\n                \"properties\": {\n                    \"control\": {\n                        \"method\": \"ipid\",\n                        \"ipid\": \"04\",\n                        \"params\": {\n                            \"deviceReadyResponsePattern\": \".*>\",\n                            \"endOfLineString\": \"\\n\"\n                        }\n                    },\n                    \"showVolumeGauge\": true,\n                    \"roomListKey\": \"\",\n                    \"showDate\": false,\n                    \"headerStyle\": \"Verbose\",\n                    \"sgdFile\": \"PepperDash Essentials iPad.sgd\",\n                    \"showTime\": false,\n                    \"sourcesOverflowCount\": 5,\n                    \"projectName\": \"PepperDash Essentials iPad\",\n                    \"defaultRoomKey\": \"room1\",\n                    \"usesSplashPage\": false\n                }\n            }\n        ],\n        \"info\": {\n            \"processorType\": \"rmc3\",\n            \"lastUid\": 16,\n            \"lastModifiedDate\": \"2018-02-16T17:54:41.315Z\",\n            \"systemType\": \"huddle\",\n            \"comment\": \"\",\n            \"requiredControlSofwareVersion\": \"\"\n        },\n        \"tieLines\": [\n            {\n                \"sourceKey\": \"inRoomPc-1\",\n                \"sourcePort\": \"anyVideoOut\",\n                \"destinationKey\": \"display-1\",\n                \"destinationPort\": \"HdmiIn1\",\n                \"type\": \"audioVideo\"\n            },\n            {\n                \"sourceKey\": \"wePresent-1\",\n                \"sourcePort\": \"anyOut\",\n                \"destinationKey\": \"display-1\",\n                \"destinationPort\": \"HdmiIn2\",\n                \"type\": \"audioVideo\"\n            }\n        ],\n        \"rooms\": [\n            {\n                \"type\": \"huddle\",\n                \"name\": \"Essentials Huddle 2 Inputs\",\n                \"key\": \"room1\",\n                \"properties\": {\n                    \"defaultDisplayKey\": \"display-1\",\n                    \"logo\": {\n                        \"type\": \"system\",\n                        \"url\": \"\"\n                    },\n                    \"description\": \"Huddle Space with 2 sources. RMC3 processor. iPad + TSW-560\",\n                    \"defaultSourceItem\": \"source-1\",\n                    \"hasDsp\": false,\n                    \"helpMessage\": \"\",\n                    \"sourceListKey\": \"default\",\n                    \"volumes\": {\n                        \"master\": {\n                            \"level\": 40,\n                            \"deviceKey\": \"display-1\",\n                            \"label\": \"Volume\"\n                        }\n                    },\n                    \"defaultAudioKey\": \"display-1\",\n                    \"defaultVideoBehavior\": \"basic\",\n                    \"tech\": {\n                        \"password\": \"1234\"\n                    }\n                }\n            }\n        ]\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials/Example Configuration/EssentialsHuddleVtc1Room/configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json",
    "content": "{\n    \"system\": {\n        \"rooms\": [\n            {\n                \"name\": \"Example Room\",\n                \"key\": \"room1\",\n                \"properties\": {\n                    \"occupancy\": {\n                        \"timeoutMinutes\": \"60\"\n                    }\n                }\n            }\n        ],\n        \"devices\": [\n            {\n                \"key\": \"hdMd4x14kE-1\",\n                \"uid\": 4,\n                \"properties\": {\n                    \"control\": {\n                        \"tcpSshProperties\": {\n                            \"address\": \"0.0.0.0\"\n                        }\n                    }\n                }\n            },\n            {\n                \"key\": \"mockCodec-1\",\n                \"uid\": 5,\n                \"properties\": {\n                    \"control\": {\n                        \"tcpSshProperties\": {\n                            \"address\": \"0.0.0.0\"\n                        }\n                    }\n                }\n            }\n        ],\n        \"info\": {\n            \"lastModifiedDate\": \"2019-02-26T21:08:09.195Z\"\n        }\n    },\n    \"system_url\": \"\",\n    \"template_url\": \"\",\n    \"template\": {\n        \"rooms\": [\n            {\n                \"type\": \"huddleVtc1\",\n                \"name\": \"Essentials DIN-AP3 - DM4x1\",\n                \"key\": \"room1\",\n                \"properties\": {\n                    \"defaultDisplayKey\": \"display-1\",\n                    \"description\": \"\",\n                    \"helpMessage\": \"\",\n                    \"sourceListKey\": \"default\",\n                    \"defaultVideoBehavior\": \"basic\",\n                    \"logo\": {\n                        \"type\": \"system\",\n                        \"url\": \"\"\n                    },\n                    \"occupancy\": {\n                        \"timeoutMinutes\": \"60\",\n                        \"deviceKey\": \"glsOdtCCn-1\"\n                    },\n                    \"defaultSourceItem\": \"source-1\",\n                    \"videoCodecKey\": \"mockCodec-1\",\n                    \"hasDsp\": false,\n                    \"volumes\": {\n                        \"master\": {\n                            \"level\": 40,\n                            \"deviceKey\": \"mockCodec-1\",\n                            \"label\": \"Volume\"\n                        }\n                    },\n                    \"defaultAudioKey\": \"display-1\",\n                    \"tech\": {\n                        \"password\": \"1234\"\n                    }\n                }\n            }\n        ],\n        \"devices\": [\n            {\n                \"name\": \"DIN-AP3\",\n                \"key\": \"processor\",\n                \"type\": \"dinAp3\",\n                \"group\": \"processor\",\n                \"uid\": 0,\n                \"properties\": {\n                }\n            },\n            {\n                \"name\": \"Room PC\",\n                \"key\": \"inRoomPc-1\",\n                \"type\": \"inRoomPc\",\n                \"properties\": {\n                    \"hasAudio\": true,\n                    \"hasControls\": false,\n                    \"isDefault\": true\n                },\n                \"group\": \"pc\",\n                \"uid\": 1\n            },\n            {\n                \"name\": \"Laptop\",\n                \"key\": \"laptop-1\",\n                \"type\": \"laptop\",\n                \"properties\": {\n                    \"hasAudio\": true,\n                    \"hasControls\": false\n                },\n                \"group\": \"pc\",\n                \"uid\": 2\n            },\n            {\n                \"name\": \"Wireless Video\",\n                \"key\": \"wePresent-1\",\n                \"type\": \"genericSource\",\n                \"properties\": {},\n                \"group\": \"genericSource\",\n                \"uid\": 3\n            },\n            {\n                \"name\": \"HD-MD4x1-4k-E HDMI Switch 1\",\n                \"key\": \"hdMd4x14kE-1\",\n                \"type\": \"hdMd4x14kE\",\n                \"properties\": {\n                    \"control\": {\n                        \"params\": {\n                            \"deviceReadyResponsePattern\": \".*>\",\n                            \"endOfLineString\": \"\\n\"\n                        },\n                        \"ipid\": \"10\",\n                        \"method\": \"ipidTcp\",\n                        \"tcpSshProperties\": {\n                            \"port\": 0,\n                            \"address\": \"\"\n                        }\n                    },\n                    \"parentDeviceKey\": \"processor\",\n                    \"inputs\": {\n                        \"hdmiIn2\": {\n                            \"disableHdcp\": true\n                        },\n                        \"hdmiIn3\": {\n                            \"disableHdcp\": true\n                        },\n                        \"hdmiIn4\": {\n                            \"disableHdcp\": true\n                        },\n                        \"hdmiIn1\": {\n                            \"disableHdcp\": true\n                        }\n                    }\n                },\n                \"group\": \"hdMdSwitch\",\n                \"uid\": 4\n            },\n            {\n                \"name\": \"Mock Video Codec 1\",\n                \"key\": \"mockCodec-1\",\n                \"type\": \"mockVc\",\n                \"properties\": {\n                    \"favorites\": [\n                        {\n                            \"name\": \"Corporate WebEx\",\n                            \"number\": \"5555555555\"\n                        }\n                    ]\n                },\n                \"group\": \"videoCodec\",\n                \"uid\": 5\n            },\n            {\n                \"name\": \"Samsung MDC Protocol 1\",\n                \"key\": \"display-1\",\n                \"type\": \"samsungmdc\",\n                \"properties\": {\n                    \"id\": \"01\",\n                    \"control\": {\n                        \"controlPortDevKey\": \"processor\",\n                        \"controlPortNumber\": 1,\n                        \"method\": \"com\",\n                        \"comParams\": {\n                            \"protocol\": \"RS232\",\n                            \"baudRate\": 9600,\n                            \"hardwareHandshake\": \"None\",\n                            \"softwareHandshake\": \"None\",\n                            \"dataBits\": 8,\n                            \"parity\": \"None\",\n                            \"stopBits\": 1\n                        }\n                    }\n                },\n                \"group\": \"display\",\n                \"uid\": 6\n            },\n            {\n                \"name\": \"Crestron GLS-ODT-C-CN 1\",\n                \"key\": \"glsOdtCCn-1\",\n                \"type\": \"glsOdtCCn\",\n                \"properties\": {\n                    \"control\": {\n                        \"method\": \"cresnet\",\n                        \"cresnetId\": \"97\"\n                    }\n                },\n                \"group\": \"occupancy\",\n                \"uid\": 7\n            },\n            {\n                \"name\": \"TSW-760\",\n                \"key\": \"tsw760-1\",\n                \"type\": \"tsw760\",\n                \"properties\": {\n                    \"control\": {\n                        \"params\": {\n                            \"deviceReadyResponsePattern\": \".*>\",\n                            \"endOfLineString\": \"\\n\"\n                        },\n                        \"ipid\": \"03\",\n                        \"method\": \"ipid\"\n                    },\n                    \"showVolumeGauge\": true,\n                    \"sourcesOverflowCount\": 4,\n                    \"showDate\": true,\n                    \"headerStyle\": \"Verbose\",\n                    \"sgdFile\": \"PepperDash Essentials TSW-760.sgd\",\n                    \"showTime\": true,\n                    \"roomListKey\": \"\",\n                    \"usesSplashPage\": false,\n                    \"defaultRoomKey\": \"room1\"\n                },\n                \"group\": \"touchpanel\",\n                \"uid\": 8\n            },\n            {\n                \"name\": \"Crestron XPanel\",\n                \"key\": \"crestronApp-1\",\n                \"type\": \"crestronApp\",\n                \"properties\": {\n                    \"control\": {\n                        \"params\": {\n                            \"deviceReadyResponsePattern\": \".*>\",\n                            \"endOfLineString\": \"\\n\"\n                        },\n                        \"ipid\": \"04\",\n                        \"method\": \"ipid\"\n                    },\n                    \"showVolumeGauge\": true,\n                    \"sourcesOverflowCount\": 5,\n                    \"showDate\": true,\n                    \"headerStyle\": \"Verbose\",\n                    \"sgdFile\": \"PepperDash Essentials iPad.sgd\",\n                    \"showTime\": true,\n                    \"roomListKey\": \"\",\n                    \"projectName\": \"PepperDash Essentials iPad\",\n                    \"defaultRoomKey\": \"room1\",\n                    \"usesSplashPage\": false\n                },\n                \"group\": \"touchpanel\",\n                \"uid\": 9\n            }\n        ],\n        \"info\": {\n            \"comment\": \"\",\n            \"lastUid\": 10,\n            \"lastModifiedDate\": \"2018-07-02T17:41:06.550Z\",\n            \"systemType\": \"huddle\",\n            \"processorType\": \"dinAp3\",\n            \"requiredControlSofwareVersion\": \"\"\n        },\n        \"tieLines\": [\n            {\n                \"type\": \"audioVideo\",\n                \"sourceKey\": \"hdMd4x14kE-1\",\n                \"destinationKey\": \"mockCodec-1\",\n                \"destinationPort\": \"HdmiIn2\",\n                \"sourcePort\": \"hdmiOut\"\n            },\n            {\n                \"type\": \"audioVideo\",\n                \"sourceKey\": \"mockCodec-1\",\n                \"destinationKey\": \"display-1\",\n                \"destinationPort\": \"HdmiIn1\",\n                \"sourcePort\": \"HdmiOut1\"\n            },\n            {\n                \"type\": \"audioVideo\",\n                \"sourceKey\": \"laptop-1\",\n                \"destinationKey\": \"hdMd4x14kE-1\",\n                \"destinationPort\": \"hdmiIn1\",\n                \"sourcePort\": \"anyOut\"\n            },\n            {\n                \"type\": \"audioVideo\",\n                \"sourceKey\": \"inRoomPc-1\",\n                \"destinationKey\": \"mockCodec-1\",\n                \"destinationPort\": \"HdmiIn3\",\n                \"sourcePort\": \"anyVideoOut\"\n            },\n            {\n                \"type\": \"audioVideo\",\n                \"sourceKey\": \"wePresent-1\",\n                \"destinationKey\": \"hdMd4x14kE-1\",\n                \"destinationPort\": \"hdmiIn2\",\n                \"sourcePort\": \"anyOut\"\n            }\n        ],\n        \"sourceLists\": {\n            \"default\": {\n                \"source-2\": {\n                    \"order\": 6,\n                    \"icon\": \"\",\n                    \"altIcon\": \"Blank\",\n                    \"type\": \"route\",\n                    \"sourceKey\": \"laptop-1\",\n                    \"includeInSourceList\": true,\n                    \"volumeControlKey\": \"$defaultAudio\",\n                    \"routeList\": [\n                        {\n                            \"type\": \"audioVideo\",\n                            \"sourceKey\": \"laptop-1\",\n                            \"destinationKey\": \"$defaultAll\"\n                        }\n                    ]\n                },\n                \"source-3\": {\n                    \"order\": 7,\n                    \"icon\": \"\",\n                    \"altIcon\": \"Blank\",\n                    \"type\": \"route\",\n                    \"sourceKey\": \"wePresent-1\",\n                    \"includeInSourceList\": true,\n                    \"volumeControlKey\": \"$defaultAudio\",\n                    \"routeList\": [\n                        {\n                            \"type\": \"audioVideo\",\n                            \"sourceKey\": \"wePresent-1\",\n                            \"destinationKey\": \"$defaultAll\"\n                        }\n                    ]\n                },\n                \"source-1\": {\n                    \"order\": 5,\n                    \"icon\": \"\",\n                    \"altIcon\": \"Blank\",\n                    \"type\": \"route\",\n                    \"sourceKey\": \"inRoomPc-1\",\n                    \"includeInSourceList\": true,\n                    \"volumeControlKey\": \"$defaultAudio\",\n                    \"routeList\": [\n                        {\n                            \"type\": \"audioVideo\",\n                            \"sourceKey\": \"inRoomPc-1\",\n                            \"destinationKey\": \"$defaultAll\"\n                        }\n                    ]\n                },\n                \"roomOff\": {\n                    \"type\": \"off\",\n                    \"sourceKey\": \"$off\",\n                    \"routeList\": [\n                        {\n                            \"type\": \"audioVideo\",\n                            \"sourceKey\": \"$off\",\n                            \"destinationKey\": \"$defaultAll\"\n                        }\n                    ]\n                },\n                \"codecOsd\": {\n                    \"order\": 1,\n                    \"name\": \"None\",\n                    \"type\": \"route\",\n                    \"includeInSourceList\": true,\n                    \"sourceKey\": \"\",\n                    \"routeList\": [\n                        {\n                            \"type\": \"audioVideo\",\n                            \"sourceKey\": \"mockCodec-1[osd]\",\n                            \"destinationKey\": \"$defaultAll\"\n                        }\n                    ]\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials/Example Configuration/SIMPLBridging/SIMPLBridgeExample_configurationFile.json",
    "content": "{\n  \"system_url\": \"\",\n  \"template\": {\n    \"info\": {\n      \"comment\": \"\",\n      \"requiredControlSofwareVersion\": \"\",\n      \"systemType\": \"huddle\",\n      \"lastModifiedDate\": \"2018-07-09T20:00:47.873Z\",\n      \"lastUid\": 23,\n      \"processorType\": \"rmc3\"\n    },\n    \"devices\": [\n      {\n        \"key\": \"processor\",\n        \"group\": \"processor\",\n        \"uid\": 0,\n        \"supportsCompliance\": true,\n        \"type\": \"rmc3\",\n        \"properties\": {},\n        \"name\": \"RMC3\"\n      },\n      {\n        \"key\": \"comm-1\",\n        \"uid\": 1,\n        \"name\": \"Generic comm 1\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"comParams\": {\n              \"hardwareHandshake\": \"None\",\n              \"parity\": \"None\",\n              \"protocol\": \"RS232\",\n              \"baudRate\": 9600,\n              \"dataBits\": 8,\n              \"softwareHandshake\": \"None\",\n              \"stopBits\": 1\n            },\n            \"controlPortNumber\": 1,\n            \"controlPortDevKey\": \"processor\",\n            \"method\": \"Com\"\n          }\n        }\n      },\n      {\n        \"key\": \"tcp-1\",\n        \"uid\": 2,\n        \"name\": \"Generic TCP 1\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"tcpSshProperties\": {\n              \"username\": \"\",\n              \"autoReconnect\": true,\n              \"AutoReconnectIntervalMs\": 2000,\n              \"port\": 23,\n              \"address\": \"0.0.0.0\",\n              \"password\": \"\"\n            },\n            \"method\": \"Tcpip\"\n          }\n        }\n      },\n      {\n        \"key\": \"ssh-1\",\n        \"uid\": 3,\n        \"name\": \"Generic SSH 1\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"tcpSshProperties\": {\n              \"username\": \"crestron\",\n              \"autoReconnect\": true,\n              \"AutoReconnectIntervalMs\": 2000,\n              \"port\": 22,\n              \"address\": \"10.11.50.135\",\n              \"password\": \"2H3Zu&OvgXp6\"\n            },\n            \"method\": \"Ssh\"\n          }\n        }\n      },\n      {\n        \"key\": \"eisc-1A\",\n        \"uid\": 4,\n        \"type\": \"eiscApi\",\n        \"group\": \"api\",\n        \"properties\": {\n          \"control\": {\n            \"tcpSshProperties\": {\n              \"address\": \"127.0.0.2\",\n              \"port\": 0\n            },\n            \"ipId\": \"1A\"\n          },\n          \"devices\": [\n            {\n              \"deviceKey\": \"comm-1\",\n              \"joinStart\": 3001\n            },\n            {\n              \"deviceKey\": \"tcp-1\",\n              \"joinStart\": 3011\n            },\n            {\n              \"deviceKey\": \"ssh-1\",\n              \"joinStart\": 3021\n            },\n            {\n              \"deviceKey\": \"dmMd8x8-1\",\n              \"joinStart\": 1\n            },\n            {\n              \"deviceKey\": \"dmTx201C-1\",\n              \"joinStart\": 3051\n            },\n            {\n              \"deviceKey\": \"dmRmc4kScalerC-1\",\n              \"joinStart\": 3061\n            },\n            {\n              \"deviceKey\": \"dmRmc200C-1\",\n              \"joinStart\": 3071\n            },\n            {\n              \"deviceKey\": \"dmRmc100C-1\",\n              \"joinStart\": 3081\n            },\n            {\n              \"deviceKey\": \"comm-2\",\n              \"joinStart\": 2501\n            },\n            {\n              \"deviceKey\": \"comm-3\",\n              \"joinStart\": 2511\n            },\n            {\n              \"deviceKey\": \"comm-4\",\n              \"joinStart\": 2521\n            },\n            {\n              \"deviceKey\": \"cec-1\",\n              \"joinStart\": 2531\n            },\n            {\n              \"deviceKey\": \"cec-2\",\n              \"joinStart\": 2541\n            },\n            {\n              \"deviceKey\": \"cec-3\",\n              \"joinStart\": 2551\n            },\n            {\n              \"deviceKey\": \"cec-4\",\n              \"joinStart\": 2561\n            },\n            {\n              \"deviceKey\": \"cec-5\",\n              \"joinStart\": 2571\n            },\n            {\n              \"deviceKey\": \"cec-6\",\n              \"joinStart\": 2581\n            },\n            {\n              \"deviceKey\": \"cec-7\",\n              \"joinStart\": 2591\n            },\n            {\n              \"deviceKey\": \"gls-oir-1\",\n              \"joinStart\": 2701\n            },\n            {\n              \"deviceKey\": \"gls-odt-1\",\n              \"joinStart\": 2751\n            },\n            {\n\t\t\t\t\"deviceKey\": \"gls-part-1\",\n\t\t\t\t\"joinStart\": 2781\n            }\n          ]\n        }\n      },\n      {\n        \"key\": \"dmMd8x8-1\",\n        \"uid\": 5,\n        \"name\": \"DM-MD8x8 Chassis 1\",\n        \"type\": \"dmMd8x8\",\n        \"group\": \"dmChassis\",\n        \"properties\": {\n          \"control\": {\n            \"method\": \"ipid\",\n            \"ipid\": \"40\",\n            \"params\": {\n              \"endOfLineString\": \"\\n\",\n              \"deviceReadyResponsePattern\": \".*>\"\n            }\n          },\n          \"volumeControls\": {},\n          \"inputSlots\": {\n            \"1\": \"dmcHdDsp\",\n            \"2\": \"dmcHdDsp\",\n            \"3\": \"dmcDvi\",\n            \"4\": \"dmcDvi\",\n            \"5\": \"dmcC\",\n            \"6\": \"dmcCDsp\"\n          },\n          \"outputSlots\": {\n            \"1\": \"dmcCoHd\",\n            \"2\": \"dmcCoHd\"\n          },\n          \"inputNames\": {\n            \"1\": \"Input 1\",\n            \"2\": \"Input 2\",\n            \"3\": \"Input 3\",\n            \"4\": \"Input 4\",\n            \"5\": \"Input 5\",\n            \"6\": \"Input 6\"\n          },\n          \"parentDeviceKey\": \"processor\",\n          \"outputNames\": {\n            \"1\": \"Output 1\",\n            \"2\": \"Output 2\",\n            \"3\": \"Output 3\",\n            \"4\": \"Output 4\"\n          },\n          \"inputSlotSupportsHdcp2\":{\n            \"1\": \"false\",\n            \"2\": \"false\",\n            \"3\": \"false\",\n            \"4\": \"false\",\n            \"5\": \"false\",\n            \"6\": \"false\"\n          }\n        }\n      },\n      {\n        \"key\": \"dmTx201C-1\",\n        \"uid\": 6,\n        \"name\": \"DM-TX-201C 1\",\n        \"type\": \"dmTx201C\",\n        \"group\": \"dmEndpoint\",\n        \"properties\": {\n          \"control\": {\n            \"method\": \"ipid\",\n            \"ipid\": \"45\",\n            \"params\": {\n              \"endOfLineString\": \"\\n\",\n              \"deviceReadyResponsePattern\": \".*>\"\n            }\n          },\n          \"parentDeviceKey\": \"dmMd8x8-1\",\n          \"parentInputNumber\": \"5\"\n        }\n      },\n      {\n        \"key\": \"dmRmc4kScalerC-1\",\n        \"uid\": 7,\n        \"name\": \"DM-RMC-4K-SCALER-C Out 1\",\n        \"type\": \"dmRmc4kScalerC\",\n        \"group\": \"dmEndpoint\",\n        \"properties\": {\n          \"control\": {\n            \"method\": \"ipid\",\n            \"ipid\": \"61\",\n            \"params\": {\n              \"endOfLineString\": \"\\n\",\n              \"deviceReadyResponsePattern\": \".*>\"\n            }\n          },\n          \"parentDeviceKey\": \"dmMd8x8-1\",\n          \"parentOutputNumber\": \"1\"\n        }\n      },\n      {\n        \"key\": \"dmRmc200C-1\",\n        \"uid\": 8,\n        \"name\": \"DM-RMC-200-C Out 2\",\n        \"type\": \"dmRmc200C\",\n        \"group\": \"dmEndpoint\",\n        \"properties\": {\n          \"control\": {\n            \"method\": \"ipid\",\n            \"ipid\": \"62\",\n            \"params\": {\n              \"endOfLineString\": \"\\n\",\n              \"deviceReadyResponsePattern\": \".*>\"\n            }\n          },\n          \"parentDeviceKey\": \"dmMd8x8-1\",\n          \"parentOutputNumber\": \"2\"\n        }\n      },\n      {\n        \"key\": \"dmRmc100C-1\",\n        \"uid\": 9,\n        \"name\": \"DM-RMC-100-C Out 3\",\n        \"type\": \"dmRmc100C\",\n        \"group\": \"dmEndpoint\",\n        \"properties\": {\n          \"control\": {\n            \"method\": \"ipid\",\n            \"ipid\": \"63\",\n            \"params\": {\n              \"endOfLineString\": \"\\n\",\n              \"deviceReadyResponsePattern\": \".*>\"\n            }\n          },\n          \"parentDeviceKey\": \"dmMd8x8-1\",\n          \"parentOutputNumber\": \"3\"\n        }\n      },\n      {\n        \"key\": \"comm-2\",\n        \"uid\": 10,\n        \"name\": \"Rmc comm 1\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"comParams\": {\n              \"hardwareHandshake\": \"None\",\n              \"parity\": \"None\",\n              \"protocol\": \"RS232\",\n              \"baudRate\": 9600,\n              \"dataBits\": 8,\n              \"softwareHandshake\": \"None\",\n              \"stopBits\": 1\n            },\n            \"controlPortNumber\": 1,\n            \"controlPortDevKey\": \"dmRmc4kScalerC-1\",\n            \"method\": \"Com\"\n          }\n        }\n      },\n      {\n        \"key\": \"comm-3\",\n        \"uid\": 11,\n        \"name\": \"Rmc comm 2\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"comParams\": {\n              \"hardwareHandshake\": \"None\",\n              \"parity\": \"None\",\n              \"protocol\": \"RS232\",\n              \"baudRate\": 9600,\n              \"dataBits\": 8,\n              \"softwareHandshake\": \"None\",\n              \"stopBits\": 1\n            },\n            \"controlPortNumber\": 1,\n            \"controlPortDevKey\": \"dmRmc200C-1\",\n            \"method\": \"Com\"\n          }\n        }\n      },\n      {\n        \"key\": \"cec-1\",\n        \"uid\": 13,\n        \"name\": \"Tx 5 cec 1\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"controlPortName\": \"HdmiIn\",\n            \"controlPortDevKey\": \"dmTx201C-1\",\n            \"method\": \"Cec\"\n          }\n        }\n      },\n      {\n        \"key\": \"cec-5\",\n        \"uid\": 17,\n        \"name\": \"Rmc 1 cec 1\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"controlPortName\": \"HdmiOut\",\n            \"controlPortDevKey\": \"dmRmc4kScalerC-1\",\n            \"method\": \"Cec\"\n          }\n        }\n      },\n      {\n        \"key\": \"cec-6\",\n        \"uid\": 18,\n        \"name\": \"Dm Chassis In 1 cec 1\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"controlPortName\": \"inputCard1--hdmiIn\",\n            \"controlPortDevKey\": \"dmMd8x8-1\",\n            \"method\": \"Cec\"\n          }\n        }\n      },\n      {\n        \"key\": \"cec-7\",\n        \"uid\": 19,\n        \"name\": \"Dm Chassis Out 1 cec 1\",\n        \"type\": \"genericComm\",\n        \"group\": \"comm\",\n        \"properties\": {\n          \"control\": {\n            \"controlPortName\": \"outputCard1--hdmiOut1\",\n            \"controlPortDevKey\": \"dmMd8x8-1\",\n            \"method\": \"Cec\"\n          }\n        }\n      },\n      {\n        \"key\": \"gls-oir-1\",\n        \"uid\": 19,\n        \"name\": \"GLS-OIR-CN 1\",\n        \"type\": \"glsoirccn\",\n        \"group\": \"occupancy\",\n        \"properties\": {\n          \"control\": {\n            \"cresnetId\": \"41\",\n            \"method\": \"cresnet\"\n          }\n        }\n      },\n      {\n        \"key\": \"gls-odt-1\",\n        \"uid\": 19,\n        \"name\": \"GLS-ODT-CN 1\",\n        \"type\": \"glsodtccn\",\n        \"group\": \"occupancy\",\n        \"properties\": {\n          \"control\": {\n            \"cresnetId\": \"42\",\n            \"method\": \"cresnet\"\n          }\n        }\n      },\n      {\n        \"key\": \"gls-part-1\",\n        \"uid\": 19,\n        \"name\": \"GLS-PART-CN 1\",\n        \"type\": \"glspartcn\",\n        \"group\": \"partition\",\n        \"properties\": {\n          \"control\": {\n            \"cresnetId\": \"90\",\n            \"method\": \"cresnet\"\n          }\n        }\n      }\n    ],\n    \"rooms\": [],\n    \"sourceLists\": {},\n    \"tieLines\": []\n  },\n  \"template_url\": \"\",\n  \"system\": {\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials/Example Configuration/SIMPLBridging/configurationFile-dmps3300c-avRouting.json",
    "content": "{\n    \"system_url\": \"\",\n    \"template\": {    \n        \"info\": {\n            \"comment\": \"\",\n            \"requiredControlSofwareVersion\": \"\",\n            \"systemType\": \"huddle\",\n            \"lastModifiedDate\": \"2018-07-09T20:00:47.873Z\",\n            \"lastUid\": 23,\n            \"processorType\": \"dmps3300c\"\n        },\n        \"devices\": [\n            {\n                \"key\": \"processor\",\n                \"group\": \"processor\",\n                \"uid\": 0,\n                \"supportsCompliance\": true,\n                \"type\": \"dmps3300c\",\n                \"properties\": {\n                },\n                \"name\": \"DMPS3-300-C\"\n            },           \n            {\n                \"key\": \"eisc-A\",\n                \"uid\":4,\n                \"type\": \"eiscApi\",\n                \"group\":\"api\",\n                \"properties\": {\n                    \"control\":{\n                        \"tcpSshProperties\":{\n                            \"address\":\"127.0.0.2\",\n                            \"port\":0\n                        },\n                        \"ipId\":\"1A\"\n                    },\n                  \"devices\": [\n                    {\n                      \"deviceKey\":\"processor-avRouting\",\n                      \"joinStart\":1                    \n                    },\n                    {\n                        \"deviceKey\":\"processor-programAudioOutput\",\n                        \"joinStart\":3001\n                    },\n                    {\n                        \"deviceKey\":\"processor-aux1AudioOutput\",\n                        \"joinStart\":3011\n                    },\n                    {\n                        \"deviceKey\":\"processor-aux2AudioOutput\",\n                        \"joinStart\":3021\n                    }\n                  ] \n                }\n              }            \n        ],\n        \"rooms\": [\n            \n        ],\n        \"sourceLists\": {\n            \n        },\n        \"tieLines\": [\n        ]   \n    },\n    \"template_url\": \"\",\n    \"system\": {\n        \n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials/Factory/DeviceFactory.cs",
    "content": "﻿\n\nusing System;\nusing System.Linq;\nusing System.Reflection;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Responsible for loading all of the device types for this library\n    /// </summary>\n    public class DeviceFactory\n    {\n\n        /// <summary>\n        /// Initializes a new instance of the DeviceFactory class and loads all device type factories\n        /// </summary>\n        public DeviceFactory()\n        {\n            var assy = Assembly.GetExecutingAssembly();\n            PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy);\n\n            var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);\n\n            if (types != null)\n            {\n                foreach (var type in types)\n                {\n                    try\n                    {\n                        var factory = (IDeviceFactory)Activator.CreateInstance(type);\n                        LoadDeviceFactories(factory);\n                    }\n                    catch (Exception e)\n                    {\n                        Debug.LogMessage(Serilog.Events.LogEventLevel.Error, \"Unable to load type: '{exception}' DeviceFactory: {factoryName}\", e, type.Name);\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// Loads device factories from the specified plugin device factory and registers them for use.\n        /// </summary>\n        /// <remarks>This method retrieves metadata from the provided <paramref name=\"deviceFactory\"/>, including\n        /// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type\n        /// names are converted to lowercase for registration.</remarks>\n        /// <param name=\"deviceFactory\">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>\n        private static void LoadDeviceFactories(IDeviceFactory deviceFactory)\n        {\n            foreach (var typeName in deviceFactory.TypeNames)\n            {\n                string description = (deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0)\n                    ? descriptionAttribute[0].Description\n                    : \"No description available\";\n\n                Core.DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials/HttpLogoServer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharp.Net.Http;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// HTTP server for serving logo images and files\n    /// </summary>\n    public class HttpLogoServer\n    {\n        /// <summary>\n        /// The HTTP server instance\n        /// </summary>\n        readonly HttpServer _server;\n\n        /// <summary>\n        /// The directory containing files to serve\n        /// </summary>\n        readonly string _fileDirectory;\n\n        /// <summary>\n        /// Dictionary mapping file extensions to content types\n        /// </summary>\n        public static Dictionary<string, string> ExtensionContentTypes;\n\n        /// <summary>\n        /// Initializes a new instance of the HttpLogoServer class\n        /// </summary>\n        /// <param name=\"port\">Port number for the HTTP server</param>\n        /// <param name=\"directory\">Directory containing files to serve</param>\n        public HttpLogoServer(int port, string directory)\n        {\n            ExtensionContentTypes = new Dictionary<string, string>\n\t\t\t{\n                //{ \".css\", \"text/css\" },\n                //{ \".htm\", \"text/html\" },\n                //{ \".html\", \"text/html\" },\n\t\t\t\t{ \".jpg\", \"image/jpeg\" },\n\t\t\t\t{ \".jpeg\", \"image/jpeg\" },\n                //{ \".js\", \"application/javascript\" },\n                //{ \".json\", \"application/json\" },\n                //{ \".map\", \"application/x-navimap\" },\n\t\t\t\t{ \".pdf\", \"application/pdf\" },\n\t\t\t\t{ \".png\", \"image/png\" },\n                //{ \".txt\", \"text/plain\" },\n\t\t\t};\n\n            _server = new HttpServer {Port = port};\n            _fileDirectory = directory;\n            _server.OnHttpRequest += Server_OnHttpRequest;\n            _server.Open();\n\n            CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;\n        }\n\n        /// <summary>\n        /// Handles incoming HTTP requests and serves files from the configured directory\n        /// </summary>\n        /// <param name=\"sender\">The HTTP server instance</param>\n        /// <param name=\"args\">HTTP request arguments</param>\n        void Server_OnHttpRequest(object sender, OnHttpRequestArgs args)\n        {\n            var path = args.Request.Path;\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, \"HTTP Request with path: '{requestPath:l}'\", args.Request.Path);            \n\n            try\n            {\n                if (File.Exists(_fileDirectory + path))\n                {\n                    var filePath = path.Replace('/', '\\\\');\n                    var localPath = string.Format(@\"{0}{1}\", _fileDirectory, filePath);\n\n                    Debug.LogMessage(LogEventLevel.Verbose, \"HTTP Logo Server attempting to find file: '{localPath:l}'\", localPath);\n                    if (File.Exists(localPath))\n                    {\n                        args.Response.Header.ContentType = GetContentType(new FileInfo(localPath).Extension);\n                        args.Response.ContentStream = new FileStream(localPath, FileMode.Open, FileAccess.Read);\n                    }\n                    else\n                    {\n                        Debug.LogMessage(LogEventLevel.Verbose, \"HTTP Logo Server Cannot find file '{localPath:l}'\", localPath);\n                        args.Response.ContentString = string.Format(\"Not found: '{0}'\", filePath);\n                        args.Response.Code = 404;\n                    }\n                }\n                else\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"HTTP Logo Server: '{file:l}' does not exist\", _fileDirectory + path);\n                    args.Response.ContentString = string.Format(\"Not found: '{0}'\", _fileDirectory + path);\n                    args.Response.Code = 404;\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(LogEventLevel.Error, \"Exception getting file: {exception}\", ex.Message, ex.StackTrace);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Stack Trace: {stackTrace}\", ex.StackTrace);\n\n                args.Response.Code = 400;\n                args.Response.ContentString = string.Format(\"invalid request\");\n            }\n        }\n\n        /// <summary>\n        /// Handles program status events and closes the server when the program is stopping\n        /// </summary>\n        /// <param name=\"programEventType\">The program status event type</param>\n        void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping)\n                _server.Close();\n        }\n\n        /// <summary>\n        /// Gets the content type for a file based on its extension\n        /// </summary>\n        /// <param name=\"extension\">The file extension</param>\n        /// <returns>The corresponding content type string</returns>\n        /// <summary>\n        /// GetContentType method\n        /// </summary>\n        public static string GetContentType(string extension)\n        {\n            var type = ExtensionContentTypes.ContainsKey(extension) ? ExtensionContentTypes[extension] : \"text/plain\";\n            return type;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials/PepperDash.Essentials.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n  <PropertyGroup>\r\n    <ProjectType>Program</ProjectType>\r\n    <Configurations>Debug;Release;Debug 4.7.2</Configurations>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <RootNamespace>PepperDash.Essentials</RootNamespace>\r\n    <AssemblyName>PepperDashEssentials</AssemblyName>\r\n    <TargetFramework>net472</TargetFramework>\r\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\r\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\r\n    <Title>PepperDash Essentials</Title>\r\n    <PackageId>PepperDashEssentials</PackageId>\r\n    <InformationalVersion>$(Version)</InformationalVersion>\r\n    <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\r\n    <DebugType>full</DebugType>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug 4.7.2|AnyCPU'\">\r\n    <DebugType>full</DebugType>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\r\n    <DebugType>pdbonly</DebugType>\r\n    <DocumentationFile>bin\\$(Configuration)\\PepperDashEssentials.xml</DocumentationFile>\r\n  </PropertyGroup>\r\n  <ItemGroup>\r\n    <None Include=\"Example Configuration\\EssentialsHuddleSpaceRoom\\configurationFile-HuddleSpace-2-Source.json\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </None>\r\n    <None Include=\"Example Configuration\\EssentialsHuddleVtc1Room\\configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </None>\r\n    <None Include=\"Example Configuration\\SIMPLBridging\\configurationFile-dmps3300c-avRouting.json\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </None>\r\n    <None Include=\"Example Configuration\\SIMPLBridging\\SIMPLBridgeExample_configurationFile.json\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </None>\r\n    <None Include=\"SGD\\PepperDash Essentials iPad.sgd\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </None>\r\n    <None Include=\"SGD\\PepperDash Essentials TSW-560.sgd\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </None>\r\n    <None Include=\"SGD\\PepperDash Essentials TSW-760.sgd\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </None>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <PackageReference Include=\"Crestron.SimplSharp.SDK.Program\" Version=\"2.21.90\" />\r\n    <PackageReference Include=\"System.IO.Compression\" Version=\"4.0.0\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\PepperDash.Core\\PepperDash.Core.csproj\" />\r\n    <ProjectReference Include=\"..\\PepperDash.Essentials.Core\\PepperDash.Essentials.Core.csproj\" />\r\n    <ProjectReference Include=\"..\\PepperDash.Essentials.Devices.Common\\PepperDash.Essentials.Devices.Common.csproj\" />\r\n    <ProjectReference Include=\"..\\PepperDash.Essentials.MobileControl.Messengers\\PepperDash.Essentials.MobileControl.Messengers.csproj\" />\r\n    <ProjectReference Include=\"..\\PepperDash.Essentials.MobileControl\\PepperDash.Essentials.MobileControl.csproj\" />\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "src/PepperDash.Essentials/Properties/ControlSystem.cfg",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ControlSystem>\n  <Name>Test RMC3</Name>\n  <Address>auto 192.168.1.40;username crestron</Address>\n  <ProgramSlot>Program01</ProgramSlot>\n  <Storage>Internal Flash</Storage>\n</ControlSystem>"
  },
  {
    "path": "src/PepperDash.Essentials/Properties/UpdateAssemblyVersion.ps1",
    "content": "﻿function Update-SourceVersion\n{\n    Param ([string]$Version)\n    $fullVersion = $Version\n    $baseVersion = [regex]::Match($Version, \"(\\d+.\\d+.\\d+).*\").captures.groups[1].value\n    $NewAssemblyVersion = ‘AssemblyVersion(\"‘ + $baseVersion + ‘.*\")’\n    echo \"AssemblyVersion = $NewAssemblyVersion\"\n    $NewAssemblyInformationalVersion = ‘AssemblyInformationalVersion(\"‘ + $Version + ‘\")’\n    echo \"AssemblyInformationalVersion = $NewAssemblyInformationalVersion\"\n\n    foreach ($o in $input)\n   {\n        Write-output $o.FullName\n        $TmpFile = $o.FullName + “.tmp”\n        get-content $o.FullName |\n        %{\n           $_ -replace ‘AssemblyVersion\\(\".*\"\\)’, $NewAssemblyVersion} |\n        %{\n           $_ -replace ‘AssemblyInformationalVersion\\(\".*\"\\)’, $NewAssemblyInformationalVersion\n         }  > $TmpFile \n        move-item $TmpFile $o.FullName -force\n    }\n}\n\nfunction Update-AllAssemblyInfoFiles ( $version )\n{\n    foreach ($file in “AssemblyInfo.cs”, “AssemblyInfo.vb” )\n    {\n        get-childitem -recurse |? {$_.Name -eq $file} | Update-SourceVersion $version ;\n    }\n}\n\n# validate arguments\n$r= [System.Text.RegularExpressions.Regex]::Match($args[0], \"\\d+\\.\\d+\\.\\d+.*\");\nif ($r.Success)\n{\n    echo \"Updating Assembly Version to $args ...\";\n    Update-AllAssemblyInfoFiles $args[0];\n}\nelse\n{\n    echo ” “;\n    echo “Error: Input version does not match x.y.z format!”\n    echo ” “;\n    echo \"Unable to apply version to AssemblyInfo.cs files\";\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials/SGD/PepperDash Essentials TSW-560.sgd",
    "content": "[\nObjTp=FSgntr\nSgntr=SGD\nRelVrs=3\nVTProeVer=6.1.05\nSchema=1\nCRCGUID=6C002F1C-3D96-4EBF-A0D9-8A124BE8F48A\n]\n;================================================================================\n[\nObjTp=Hd\nProjectFile=PepperDash Essentials TSW-560.vtp\nVtpGuid=D8D5F125-CB35-42E9-8AE3-4142597FD2C5\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[C.AC] Keypad_AC DTMF Keypad_2.ced\nHint=AC DTMF Keypad_2 (Smart Object ID=1101)\nCode=1\nSGControlType=Simple Keypad\nSGControlName=AC DTMF Keypad_2\nGUID=F2A259FE-82AD-4085-B5A6-22D7B034458C\nSmplCName=PepperDash Essentials TSW-560_[C.AC] Keypad_AC DTMF Keypad_2.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=*\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=/#\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=1\nCedH=1\nSmartObjId=1101d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=1\nTp=1\nHD=TRUE\nDV=1101d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=1\nName=PepperDash Essentials TSW-560_[C.AC] Keypad_AC DTMF Keypad_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[D.VC] Directory_Dynamic Button List Vertical_2.ced\nHint=Dynamic Button List Vertical_2 (Smart Object ID=1202)\nCode=2\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical_2\nGUID=79060F3C-D3A4-4A8D-ACF0-CB2376A31702\nSmplCName=PepperDash Essentials TSW-560_[D.VC] Directory_Dynamic Button List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=Item 11 Selected\nInputSigType22=Digital\nInputCue23=Item 12 Selected\nInputSigType23=Digital\nInputCue24=Item 13 Selected\nInputSigType24=Digital\nInputCue25=Item 14 Selected\nInputSigType25=Digital\nInputCue26=Item 15 Selected\nInputSigType26=Digital\nInputCue27=Item 16 Selected\nInputSigType27=Digital\nInputCue28=Item 17 Selected\nInputSigType28=Digital\nInputCue29=Item 18 Selected\nInputSigType29=Digital\nInputCue30=Item 19 Selected\nInputSigType30=Digital\nInputCue31=Item 20 Selected\nInputSigType31=Digital\nInputCue32=Item 21 Selected\nInputSigType32=Digital\nInputCue33=Item 22 Selected\nInputSigType33=Digital\nInputCue34=Item 23 Selected\nInputSigType34=Digital\nInputCue35=Item 24 Selected\nInputSigType35=Digital\nInputCue36=Item 25 Selected\nInputSigType36=Digital\nInputCue37=Item 26 Selected\nInputSigType37=Digital\nInputCue38=Item 27 Selected\nInputSigType38=Digital\nInputCue39=Item 28 Selected\nInputSigType39=Digital\nInputCue40=Item 29 Selected\nInputSigType40=Digital\nInputCue41=Item 30 Selected\nInputSigType41=Digital\nInputCue42=Item 31 Selected\nInputSigType42=Digital\nInputCue43=Item 32 Selected\nInputSigType43=Digital\nInputCue44=Item 33 Selected\nInputSigType44=Digital\nInputCue45=Item 34 Selected\nInputSigType45=Digital\nInputCue46=Item 35 Selected\nInputSigType46=Digital\nInputCue47=Item 36 Selected\nInputSigType47=Digital\nInputCue48=Item 37 Selected\nInputSigType48=Digital\nInputCue49=Item 38 Selected\nInputSigType49=Digital\nInputCue50=Item 39 Selected\nInputSigType50=Digital\nInputCue51=Item 40 Selected\nInputSigType51=Digital\nInputCue52=Item 41 Selected\nInputSigType52=Digital\nInputCue53=Item 42 Selected\nInputSigType53=Digital\nInputCue54=Item 43 Selected\nInputSigType54=Digital\nInputCue55=Item 44 Selected\nInputSigType55=Digital\nInputCue56=Item 45 Selected\nInputSigType56=Digital\nInputCue57=Item 46 Selected\nInputSigType57=Digital\nInputCue58=Item 47 Selected\nInputSigType58=Digital\nInputCue59=Item 48 Selected\nInputSigType59=Digital\nInputCue60=Item 49 Selected\nInputSigType60=Digital\nInputCue61=Item 50 Selected\nInputSigType61=Digital\nInputCue62=Item 51 Selected\nInputSigType62=Digital\nInputCue63=Item 52 Selected\nInputSigType63=Digital\nInputCue64=Item 53 Selected\nInputSigType64=Digital\nInputCue65=Item 54 Selected\nInputSigType65=Digital\nInputCue66=Item 55 Selected\nInputSigType66=Digital\nInputCue67=Item 56 Selected\nInputSigType67=Digital\nInputCue68=Item 57 Selected\nInputSigType68=Digital\nInputCue69=Item 58 Selected\nInputSigType69=Digital\nInputCue70=Item 59 Selected\nInputSigType70=Digital\nInputCue71=Item 60 Selected\nInputSigType71=Digital\nInputCue72=Item 61 Selected\nInputSigType72=Digital\nInputCue73=Item 62 Selected\nInputSigType73=Digital\nInputCue74=Item 63 Selected\nInputSigType74=Digital\nInputCue75=Item 64 Selected\nInputSigType75=Digital\nInputCue76=Item 65 Selected\nInputSigType76=Digital\nInputCue77=Item 66 Selected\nInputSigType77=Digital\nInputCue78=Item 67 Selected\nInputSigType78=Digital\nInputCue79=Item 68 Selected\nInputSigType79=Digital\nInputCue80=Item 69 Selected\nInputSigType80=Digital\nInputCue81=Item 70 Selected\nInputSigType81=Digital\nInputCue82=Item 71 Selected\nInputSigType82=Digital\nInputCue83=Item 72 Selected\nInputSigType83=Digital\nInputCue84=Item 73 Selected\nInputSigType84=Digital\nInputCue85=Item 74 Selected\nInputSigType85=Digital\nInputCue86=Item 75 Selected\nInputSigType86=Digital\nInputCue87=Item 76 Selected\nInputSigType87=Digital\nInputCue88=Item 77 Selected\nInputSigType88=Digital\nInputCue89=Item 78 Selected\nInputSigType89=Digital\nInputCue90=Item 79 Selected\nInputSigType90=Digital\nInputCue91=Item 80 Selected\nInputSigType91=Digital\nInputCue92=Item 81 Selected\nInputSigType92=Digital\nInputCue93=Item 82 Selected\nInputSigType93=Digital\nInputCue94=Item 83 Selected\nInputSigType94=Digital\nInputCue95=Item 84 Selected\nInputSigType95=Digital\nInputCue96=Item 85 Selected\nInputSigType96=Digital\nInputCue97=Item 86 Selected\nInputSigType97=Digital\nInputCue98=Item 87 Selected\nInputSigType98=Digital\nInputCue99=Item 88 Selected\nInputSigType99=Digital\nInputCue100=Item 89 Selected\nInputSigType100=Digital\nInputCue101=Item 90 Selected\nInputSigType101=Digital\nInputCue102=Item 91 Selected\nInputSigType102=Digital\nInputCue103=Item 92 Selected\nInputSigType103=Digital\nInputCue104=Item 93 Selected\nInputSigType104=Digital\nInputCue105=Item 94 Selected\nInputSigType105=Digital\nInputCue106=Item 95 Selected\nInputSigType106=Digital\nInputCue107=Item 96 Selected\nInputSigType107=Digital\nInputCue108=Item 97 Selected\nInputSigType108=Digital\nInputCue109=Item 98 Selected\nInputSigType109=Digital\nInputCue110=Item 99 Selected\nInputSigType110=Digital\nInputCue111=Item 100 Selected\nInputSigType111=Digital\nInputCue112=Item 101 Selected\nInputSigType112=Digital\nInputCue113=Item 102 Selected\nInputSigType113=Digital\nInputCue114=Item 103 Selected\nInputSigType114=Digital\nInputCue115=Item 104 Selected\nInputSigType115=Digital\nInputCue116=Item 105 Selected\nInputSigType116=Digital\nInputCue117=Item 106 Selected\nInputSigType117=Digital\nInputCue118=Item 107 Selected\nInputSigType118=Digital\nInputCue119=Item 108 Selected\nInputSigType119=Digital\nInputCue120=Item 109 Selected\nInputSigType120=Digital\nInputCue121=Item 110 Selected\nInputSigType121=Digital\nInputCue122=Item 111 Selected\nInputSigType122=Digital\nInputCue123=Item 112 Selected\nInputSigType123=Digital\nInputCue124=Item 113 Selected\nInputSigType124=Digital\nInputCue125=Item 114 Selected\nInputSigType125=Digital\nInputCue126=Item 115 Selected\nInputSigType126=Digital\nInputCue127=Item 116 Selected\nInputSigType127=Digital\nInputCue128=Item 117 Selected\nInputSigType128=Digital\nInputCue129=Item 118 Selected\nInputSigType129=Digital\nInputCue130=Item 119 Selected\nInputSigType130=Digital\nInputCue131=Item 120 Selected\nInputSigType131=Digital\nInputCue132=Item 121 Selected\nInputSigType132=Digital\nInputCue133=Item 122 Selected\nInputSigType133=Digital\nInputCue134=Item 123 Selected\nInputSigType134=Digital\nInputCue135=Item 124 Selected\nInputSigType135=Digital\nInputCue136=Item 125 Selected\nInputSigType136=Digital\nInputCue137=Item 126 Selected\nInputSigType137=Digital\nInputCue138=Item 127 Selected\nInputSigType138=Digital\nInputCue139=Item 128 Selected\nInputSigType139=Digital\nInputCue140=Item 129 Selected\nInputSigType140=Digital\nInputCue141=Item 130 Selected\nInputSigType141=Digital\nInputCue142=Item 131 Selected\nInputSigType142=Digital\nInputCue143=Item 132 Selected\nInputSigType143=Digital\nInputCue144=Item 133 Selected\nInputSigType144=Digital\nInputCue145=Item 134 Selected\nInputSigType145=Digital\nInputCue146=Item 135 Selected\nInputSigType146=Digital\nInputCue147=Item 136 Selected\nInputSigType147=Digital\nInputCue148=Item 137 Selected\nInputSigType148=Digital\nInputCue149=Item 138 Selected\nInputSigType149=Digital\nInputCue150=Item 139 Selected\nInputSigType150=Digital\nInputCue151=Item 140 Selected\nInputSigType151=Digital\nInputCue152=Item 141 Selected\nInputSigType152=Digital\nInputCue153=Item 142 Selected\nInputSigType153=Digital\nInputCue154=Item 143 Selected\nInputSigType154=Digital\nInputCue155=Item 144 Selected\nInputSigType155=Digital\nInputCue156=Item 145 Selected\nInputSigType156=Digital\nInputCue157=Item 146 Selected\nInputSigType157=Digital\nInputCue158=Item 147 Selected\nInputSigType158=Digital\nInputCue159=Item 148 Selected\nInputSigType159=Digital\nInputCue160=Item 149 Selected\nInputSigType160=Digital\nInputCue161=Item 150 Selected\nInputSigType161=Digital\nInputCue162=Item 151 Selected\nInputSigType162=Digital\nInputCue163=Item 152 Selected\nInputSigType163=Digital\nInputCue164=Item 153 Selected\nInputSigType164=Digital\nInputCue165=Item 154 Selected\nInputSigType165=Digital\nInputCue166=Item 155 Selected\nInputSigType166=Digital\nInputCue167=Item 156 Selected\nInputSigType167=Digital\nInputCue168=Item 157 Selected\nInputSigType168=Digital\nInputCue169=Item 158 Selected\nInputSigType169=Digital\nInputCue170=Item 159 Selected\nInputSigType170=Digital\nInputCue171=Item 160 Selected\nInputSigType171=Digital\nInputCue172=Item 161 Selected\nInputSigType172=Digital\nInputCue173=Item 162 Selected\nInputSigType173=Digital\nInputCue174=Item 163 Selected\nInputSigType174=Digital\nInputCue175=Item 164 Selected\nInputSigType175=Digital\nInputCue176=Item 165 Selected\nInputSigType176=Digital\nInputCue177=Item 166 Selected\nInputSigType177=Digital\nInputCue178=Item 167 Selected\nInputSigType178=Digital\nInputCue179=Item 168 Selected\nInputSigType179=Digital\nInputCue180=Item 169 Selected\nInputSigType180=Digital\nInputCue181=Item 170 Selected\nInputSigType181=Digital\nInputCue182=Item 171 Selected\nInputSigType182=Digital\nInputCue183=Item 172 Selected\nInputSigType183=Digital\nInputCue184=Item 173 Selected\nInputSigType184=Digital\nInputCue185=Item 174 Selected\nInputSigType185=Digital\nInputCue186=Item 175 Selected\nInputSigType186=Digital\nInputCue187=Item 176 Selected\nInputSigType187=Digital\nInputCue188=Item 177 Selected\nInputSigType188=Digital\nInputCue189=Item 178 Selected\nInputSigType189=Digital\nInputCue190=Item 179 Selected\nInputSigType190=Digital\nInputCue191=Item 180 Selected\nInputSigType191=Digital\nInputCue192=Item 181 Selected\nInputSigType192=Digital\nInputCue193=Item 182 Selected\nInputSigType193=Digital\nInputCue194=Item 183 Selected\nInputSigType194=Digital\nInputCue195=Item 184 Selected\nInputSigType195=Digital\nInputCue196=Item 185 Selected\nInputSigType196=Digital\nInputCue197=Item 186 Selected\nInputSigType197=Digital\nInputCue198=Item 187 Selected\nInputSigType198=Digital\nInputCue199=Item 188 Selected\nInputSigType199=Digital\nInputCue200=Item 189 Selected\nInputSigType200=Digital\nInputCue201=Item 190 Selected\nInputSigType201=Digital\nInputCue202=Item 191 Selected\nInputSigType202=Digital\nInputCue203=Item 192 Selected\nInputSigType203=Digital\nInputCue204=Item 193 Selected\nInputSigType204=Digital\nInputCue205=Item 194 Selected\nInputSigType205=Digital\nInputCue206=Item 195 Selected\nInputSigType206=Digital\nInputCue207=Item 196 Selected\nInputSigType207=Digital\nInputCue208=Item 197 Selected\nInputSigType208=Digital\nInputCue209=Item 198 Selected\nInputSigType209=Digital\nInputCue210=Item 199 Selected\nInputSigType210=Digital\nInputCue211=Item 200 Selected\nInputSigType211=Digital\nInputCue212=Item 201 Selected\nInputSigType212=Digital\nInputCue213=Item 202 Selected\nInputSigType213=Digital\nInputCue214=Item 203 Selected\nInputSigType214=Digital\nInputCue215=Item 204 Selected\nInputSigType215=Digital\nInputCue216=Item 205 Selected\nInputSigType216=Digital\nInputCue217=Item 206 Selected\nInputSigType217=Digital\nInputCue218=Item 207 Selected\nInputSigType218=Digital\nInputCue219=Item 208 Selected\nInputSigType219=Digital\nInputCue220=Item 209 Selected\nInputSigType220=Digital\nInputCue221=Item 210 Selected\nInputSigType221=Digital\nInputCue222=Item 211 Selected\nInputSigType222=Digital\nInputCue223=Item 212 Selected\nInputSigType223=Digital\nInputCue224=Item 213 Selected\nInputSigType224=Digital\nInputCue225=Item 214 Selected\nInputSigType225=Digital\nInputCue226=Item 215 Selected\nInputSigType226=Digital\nInputCue227=Item 216 Selected\nInputSigType227=Digital\nInputCue228=Item 217 Selected\nInputSigType228=Digital\nInputCue229=Item 218 Selected\nInputSigType229=Digital\nInputCue230=Item 219 Selected\nInputSigType230=Digital\nInputCue231=Item 220 Selected\nInputSigType231=Digital\nInputCue232=Item 221 Selected\nInputSigType232=Digital\nInputCue233=Item 222 Selected\nInputSigType233=Digital\nInputCue234=Item 223 Selected\nInputSigType234=Digital\nInputCue235=Item 224 Selected\nInputSigType235=Digital\nInputCue236=Item 225 Selected\nInputSigType236=Digital\nInputCue237=Item 226 Selected\nInputSigType237=Digital\nInputCue238=Item 227 Selected\nInputSigType238=Digital\nInputCue239=Item 228 Selected\nInputSigType239=Digital\nInputCue240=Item 229 Selected\nInputSigType240=Digital\nInputCue241=Item 230 Selected\nInputSigType241=Digital\nInputCue242=Item 231 Selected\nInputSigType242=Digital\nInputCue243=Item 232 Selected\nInputSigType243=Digital\nInputCue244=Item 233 Selected\nInputSigType244=Digital\nInputCue245=Item 234 Selected\nInputSigType245=Digital\nInputCue246=Item 235 Selected\nInputSigType246=Digital\nInputCue247=Item 236 Selected\nInputSigType247=Digital\nInputCue248=Item 237 Selected\nInputSigType248=Digital\nInputCue249=Item 238 Selected\nInputSigType249=Digital\nInputCue250=Item 239 Selected\nInputSigType250=Digital\nInputCue251=Item 240 Selected\nInputSigType251=Digital\nInputCue252=Item 241 Selected\nInputSigType252=Digital\nInputCue253=Item 242 Selected\nInputSigType253=Digital\nInputCue254=Item 243 Selected\nInputSigType254=Digital\nInputCue255=Item 244 Selected\nInputSigType255=Digital\nInputCue256=Item 245 Selected\nInputSigType256=Digital\nInputCue257=Item 246 Selected\nInputSigType257=Digital\nInputCue258=Item 247 Selected\nInputSigType258=Digital\nInputCue259=Item 248 Selected\nInputSigType259=Digital\nInputCue260=Item 249 Selected\nInputSigType260=Digital\nInputCue261=Item 250 Selected\nInputSigType261=Digital\nInputCue262=Item 251 Selected\nInputSigType262=Digital\nInputCue263=Item 252 Selected\nInputSigType263=Digital\nInputCue264=Item 253 Selected\nInputSigType264=Digital\nInputCue265=Item 254 Selected\nInputSigType265=Digital\nInputCue266=Item 255 Selected\nInputSigType266=Digital\nInputCue267=[~UNUSED2~]\nInputSigType267=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=Item 11 Enabled\nInputSigType2024=Digital\nInputCue2025=Item 12 Enabled\nInputSigType2025=Digital\nInputCue2026=Item 13 Enabled\nInputSigType2026=Digital\nInputCue2027=Item 14 Enabled\nInputSigType2027=Digital\nInputCue2028=Item 15 Enabled\nInputSigType2028=Digital\nInputCue2029=Item 16 Enabled\nInputSigType2029=Digital\nInputCue2030=Item 17 Enabled\nInputSigType2030=Digital\nInputCue2031=Item 18 Enabled\nInputSigType2031=Digital\nInputCue2032=Item 19 Enabled\nInputSigType2032=Digital\nInputCue2033=Item 20 Enabled\nInputSigType2033=Digital\nInputCue2034=Item 21 Enabled\nInputSigType2034=Digital\nInputCue2035=Item 22 Enabled\nInputSigType2035=Digital\nInputCue2036=Item 23 Enabled\nInputSigType2036=Digital\nInputCue2037=Item 24 Enabled\nInputSigType2037=Digital\nInputCue2038=Item 25 Enabled\nInputSigType2038=Digital\nInputCue2039=Item 26 Enabled\nInputSigType2039=Digital\nInputCue2040=Item 27 Enabled\nInputSigType2040=Digital\nInputCue2041=Item 28 Enabled\nInputSigType2041=Digital\nInputCue2042=Item 29 Enabled\nInputSigType2042=Digital\nInputCue2043=Item 30 Enabled\nInputSigType2043=Digital\nInputCue2044=Item 31 Enabled\nInputSigType2044=Digital\nInputCue2045=Item 32 Enabled\nInputSigType2045=Digital\nInputCue2046=Item 33 Enabled\nInputSigType2046=Digital\nInputCue2047=Item 34 Enabled\nInputSigType2047=Digital\nInputCue2048=Item 35 Enabled\nInputSigType2048=Digital\nInputCue2049=Item 36 Enabled\nInputSigType2049=Digital\nInputCue2050=Item 37 Enabled\nInputSigType2050=Digital\nInputCue2051=Item 38 Enabled\nInputSigType2051=Digital\nInputCue2052=Item 39 Enabled\nInputSigType2052=Digital\nInputCue2053=Item 40 Enabled\nInputSigType2053=Digital\nInputCue2054=Item 41 Enabled\nInputSigType2054=Digital\nInputCue2055=Item 42 Enabled\nInputSigType2055=Digital\nInputCue2056=Item 43 Enabled\nInputSigType2056=Digital\nInputCue2057=Item 44 Enabled\nInputSigType2057=Digital\nInputCue2058=Item 45 Enabled\nInputSigType2058=Digital\nInputCue2059=Item 46 Enabled\nInputSigType2059=Digital\nInputCue2060=Item 47 Enabled\nInputSigType2060=Digital\nInputCue2061=Item 48 Enabled\nInputSigType2061=Digital\nInputCue2062=Item 49 Enabled\nInputSigType2062=Digital\nInputCue2063=Item 50 Enabled\nInputSigType2063=Digital\nInputCue2064=Item 51 Enabled\nInputSigType2064=Digital\nInputCue2065=Item 52 Enabled\nInputSigType2065=Digital\nInputCue2066=Item 53 Enabled\nInputSigType2066=Digital\nInputCue2067=Item 54 Enabled\nInputSigType2067=Digital\nInputCue2068=Item 55 Enabled\nInputSigType2068=Digital\nInputCue2069=Item 56 Enabled\nInputSigType2069=Digital\nInputCue2070=Item 57 Enabled\nInputSigType2070=Digital\nInputCue2071=Item 58 Enabled\nInputSigType2071=Digital\nInputCue2072=Item 59 Enabled\nInputSigType2072=Digital\nInputCue2073=Item 60 Enabled\nInputSigType2073=Digital\nInputCue2074=Item 61 Enabled\nInputSigType2074=Digital\nInputCue2075=Item 62 Enabled\nInputSigType2075=Digital\nInputCue2076=Item 63 Enabled\nInputSigType2076=Digital\nInputCue2077=Item 64 Enabled\nInputSigType2077=Digital\nInputCue2078=Item 65 Enabled\nInputSigType2078=Digital\nInputCue2079=Item 66 Enabled\nInputSigType2079=Digital\nInputCue2080=Item 67 Enabled\nInputSigType2080=Digital\nInputCue2081=Item 68 Enabled\nInputSigType2081=Digital\nInputCue2082=Item 69 Enabled\nInputSigType2082=Digital\nInputCue2083=Item 70 Enabled\nInputSigType2083=Digital\nInputCue2084=Item 71 Enabled\nInputSigType2084=Digital\nInputCue2085=Item 72 Enabled\nInputSigType2085=Digital\nInputCue2086=Item 73 Enabled\nInputSigType2086=Digital\nInputCue2087=Item 74 Enabled\nInputSigType2087=Digital\nInputCue2088=Item 75 Enabled\nInputSigType2088=Digital\nInputCue2089=Item 76 Enabled\nInputSigType2089=Digital\nInputCue2090=Item 77 Enabled\nInputSigType2090=Digital\nInputCue2091=Item 78 Enabled\nInputSigType2091=Digital\nInputCue2092=Item 79 Enabled\nInputSigType2092=Digital\nInputCue2093=Item 80 Enabled\nInputSigType2093=Digital\nInputCue2094=Item 81 Enabled\nInputSigType2094=Digital\nInputCue2095=Item 82 Enabled\nInputSigType2095=Digital\nInputCue2096=Item 83 Enabled\nInputSigType2096=Digital\nInputCue2097=Item 84 Enabled\nInputSigType2097=Digital\nInputCue2098=Item 85 Enabled\nInputSigType2098=Digital\nInputCue2099=Item 86 Enabled\nInputSigType2099=Digital\nInputCue2100=Item 87 Enabled\nInputSigType2100=Digital\nInputCue2101=Item 88 Enabled\nInputSigType2101=Digital\nInputCue2102=Item 89 Enabled\nInputSigType2102=Digital\nInputCue2103=Item 90 Enabled\nInputSigType2103=Digital\nInputCue2104=Item 91 Enabled\nInputSigType2104=Digital\nInputCue2105=Item 92 Enabled\nInputSigType2105=Digital\nInputCue2106=Item 93 Enabled\nInputSigType2106=Digital\nInputCue2107=Item 94 Enabled\nInputSigType2107=Digital\nInputCue2108=Item 95 Enabled\nInputSigType2108=Digital\nInputCue2109=Item 96 Enabled\nInputSigType2109=Digital\nInputCue2110=Item 97 Enabled\nInputSigType2110=Digital\nInputCue2111=Item 98 Enabled\nInputSigType2111=Digital\nInputCue2112=Item 99 Enabled\nInputSigType2112=Digital\nInputCue2113=Item 100 Enabled\nInputSigType2113=Digital\nInputCue2114=Item 101 Enabled\nInputSigType2114=Digital\nInputCue2115=Item 102 Enabled\nInputSigType2115=Digital\nInputCue2116=Item 103 Enabled\nInputSigType2116=Digital\nInputCue2117=Item 104 Enabled\nInputSigType2117=Digital\nInputCue2118=Item 105 Enabled\nInputSigType2118=Digital\nInputCue2119=Item 106 Enabled\nInputSigType2119=Digital\nInputCue2120=Item 107 Enabled\nInputSigType2120=Digital\nInputCue2121=Item 108 Enabled\nInputSigType2121=Digital\nInputCue2122=Item 109 Enabled\nInputSigType2122=Digital\nInputCue2123=Item 110 Enabled\nInputSigType2123=Digital\nInputCue2124=Item 111 Enabled\nInputSigType2124=Digital\nInputCue2125=Item 112 Enabled\nInputSigType2125=Digital\nInputCue2126=Item 113 Enabled\nInputSigType2126=Digital\nInputCue2127=Item 114 Enabled\nInputSigType2127=Digital\nInputCue2128=Item 115 Enabled\nInputSigType2128=Digital\nInputCue2129=Item 116 Enabled\nInputSigType2129=Digital\nInputCue2130=Item 117 Enabled\nInputSigType2130=Digital\nInputCue2131=Item 118 Enabled\nInputSigType2131=Digital\nInputCue2132=Item 119 Enabled\nInputSigType2132=Digital\nInputCue2133=Item 120 Enabled\nInputSigType2133=Digital\nInputCue2134=Item 121 Enabled\nInputSigType2134=Digital\nInputCue2135=Item 122 Enabled\nInputSigType2135=Digital\nInputCue2136=Item 123 Enabled\nInputSigType2136=Digital\nInputCue2137=Item 124 Enabled\nInputSigType2137=Digital\nInputCue2138=Item 125 Enabled\nInputSigType2138=Digital\nInputCue2139=Item 126 Enabled\nInputSigType2139=Digital\nInputCue2140=Item 127 Enabled\nInputSigType2140=Digital\nInputCue2141=Item 128 Enabled\nInputSigType2141=Digital\nInputCue2142=Item 129 Enabled\nInputSigType2142=Digital\nInputCue2143=Item 130 Enabled\nInputSigType2143=Digital\nInputCue2144=Item 131 Enabled\nInputSigType2144=Digital\nInputCue2145=Item 132 Enabled\nInputSigType2145=Digital\nInputCue2146=Item 133 Enabled\nInputSigType2146=Digital\nInputCue2147=Item 134 Enabled\nInputSigType2147=Digital\nInputCue2148=Item 135 Enabled\nInputSigType2148=Digital\nInputCue2149=Item 136 Enabled\nInputSigType2149=Digital\nInputCue2150=Item 137 Enabled\nInputSigType2150=Digital\nInputCue2151=Item 138 Enabled\nInputSigType2151=Digital\nInputCue2152=Item 139 Enabled\nInputSigType2152=Digital\nInputCue2153=Item 140 Enabled\nInputSigType2153=Digital\nInputCue2154=Item 141 Enabled\nInputSigType2154=Digital\nInputCue2155=Item 142 Enabled\nInputSigType2155=Digital\nInputCue2156=Item 143 Enabled\nInputSigType2156=Digital\nInputCue2157=Item 144 Enabled\nInputSigType2157=Digital\nInputCue2158=Item 145 Enabled\nInputSigType2158=Digital\nInputCue2159=Item 146 Enabled\nInputSigType2159=Digital\nInputCue2160=Item 147 Enabled\nInputSigType2160=Digital\nInputCue2161=Item 148 Enabled\nInputSigType2161=Digital\nInputCue2162=Item 149 Enabled\nInputSigType2162=Digital\nInputCue2163=Item 150 Enabled\nInputSigType2163=Digital\nInputCue2164=Item 151 Enabled\nInputSigType2164=Digital\nInputCue2165=Item 152 Enabled\nInputSigType2165=Digital\nInputCue2166=Item 153 Enabled\nInputSigType2166=Digital\nInputCue2167=Item 154 Enabled\nInputSigType2167=Digital\nInputCue2168=Item 155 Enabled\nInputSigType2168=Digital\nInputCue2169=Item 156 Enabled\nInputSigType2169=Digital\nInputCue2170=Item 157 Enabled\nInputSigType2170=Digital\nInputCue2171=Item 158 Enabled\nInputSigType2171=Digital\nInputCue2172=Item 159 Enabled\nInputSigType2172=Digital\nInputCue2173=Item 160 Enabled\nInputSigType2173=Digital\nInputCue2174=Item 161 Enabled\nInputSigType2174=Digital\nInputCue2175=Item 162 Enabled\nInputSigType2175=Digital\nInputCue2176=Item 163 Enabled\nInputSigType2176=Digital\nInputCue2177=Item 164 Enabled\nInputSigType2177=Digital\nInputCue2178=Item 165 Enabled\nInputSigType2178=Digital\nInputCue2179=Item 166 Enabled\nInputSigType2179=Digital\nInputCue2180=Item 167 Enabled\nInputSigType2180=Digital\nInputCue2181=Item 168 Enabled\nInputSigType2181=Digital\nInputCue2182=Item 169 Enabled\nInputSigType2182=Digital\nInputCue2183=Item 170 Enabled\nInputSigType2183=Digital\nInputCue2184=Item 171 Enabled\nInputSigType2184=Digital\nInputCue2185=Item 172 Enabled\nInputSigType2185=Digital\nInputCue2186=Item 173 Enabled\nInputSigType2186=Digital\nInputCue2187=Item 174 Enabled\nInputSigType2187=Digital\nInputCue2188=Item 175 Enabled\nInputSigType2188=Digital\nInputCue2189=Item 176 Enabled\nInputSigType2189=Digital\nInputCue2190=Item 177 Enabled\nInputSigType2190=Digital\nInputCue2191=Item 178 Enabled\nInputSigType2191=Digital\nInputCue2192=Item 179 Enabled\nInputSigType2192=Digital\nInputCue2193=Item 180 Enabled\nInputSigType2193=Digital\nInputCue2194=Item 181 Enabled\nInputSigType2194=Digital\nInputCue2195=Item 182 Enabled\nInputSigType2195=Digital\nInputCue2196=Item 183 Enabled\nInputSigType2196=Digital\nInputCue2197=Item 184 Enabled\nInputSigType2197=Digital\nInputCue2198=Item 185 Enabled\nInputSigType2198=Digital\nInputCue2199=Item 186 Enabled\nInputSigType2199=Digital\nInputCue2200=Item 187 Enabled\nInputSigType2200=Digital\nInputCue2201=Item 188 Enabled\nInputSigType2201=Digital\nInputCue2202=Item 189 Enabled\nInputSigType2202=Digital\nInputCue2203=Item 190 Enabled\nInputSigType2203=Digital\nInputCue2204=Item 191 Enabled\nInputSigType2204=Digital\nInputCue2205=Item 192 Enabled\nInputSigType2205=Digital\nInputCue2206=Item 193 Enabled\nInputSigType2206=Digital\nInputCue2207=Item 194 Enabled\nInputSigType2207=Digital\nInputCue2208=Item 195 Enabled\nInputSigType2208=Digital\nInputCue2209=Item 196 Enabled\nInputSigType2209=Digital\nInputCue2210=Item 197 Enabled\nInputSigType2210=Digital\nInputCue2211=Item 198 Enabled\nInputSigType2211=Digital\nInputCue2212=Item 199 Enabled\nInputSigType2212=Digital\nInputCue2213=Item 200 Enabled\nInputSigType2213=Digital\nInputCue2214=Item 201 Enabled\nInputSigType2214=Digital\nInputCue2215=Item 202 Enabled\nInputSigType2215=Digital\nInputCue2216=Item 203 Enabled\nInputSigType2216=Digital\nInputCue2217=Item 204 Enabled\nInputSigType2217=Digital\nInputCue2218=Item 205 Enabled\nInputSigType2218=Digital\nInputCue2219=Item 206 Enabled\nInputSigType2219=Digital\nInputCue2220=Item 207 Enabled\nInputSigType2220=Digital\nInputCue2221=Item 208 Enabled\nInputSigType2221=Digital\nInputCue2222=Item 209 Enabled\nInputSigType2222=Digital\nInputCue2223=Item 210 Enabled\nInputSigType2223=Digital\nInputCue2224=Item 211 Enabled\nInputSigType2224=Digital\nInputCue2225=Item 212 Enabled\nInputSigType2225=Digital\nInputCue2226=Item 213 Enabled\nInputSigType2226=Digital\nInputCue2227=Item 214 Enabled\nInputSigType2227=Digital\nInputCue2228=Item 215 Enabled\nInputSigType2228=Digital\nInputCue2229=Item 216 Enabled\nInputSigType2229=Digital\nInputCue2230=Item 217 Enabled\nInputSigType2230=Digital\nInputCue2231=Item 218 Enabled\nInputSigType2231=Digital\nInputCue2232=Item 219 Enabled\nInputSigType2232=Digital\nInputCue2233=Item 220 Enabled\nInputSigType2233=Digital\nInputCue2234=Item 221 Enabled\nInputSigType2234=Digital\nInputCue2235=Item 222 Enabled\nInputSigType2235=Digital\nInputCue2236=Item 223 Enabled\nInputSigType2236=Digital\nInputCue2237=Item 224 Enabled\nInputSigType2237=Digital\nInputCue2238=Item 225 Enabled\nInputSigType2238=Digital\nInputCue2239=Item 226 Enabled\nInputSigType2239=Digital\nInputCue2240=Item 227 Enabled\nInputSigType2240=Digital\nInputCue2241=Item 228 Enabled\nInputSigType2241=Digital\nInputCue2242=Item 229 Enabled\nInputSigType2242=Digital\nInputCue2243=Item 230 Enabled\nInputSigType2243=Digital\nInputCue2244=Item 231 Enabled\nInputSigType2244=Digital\nInputCue2245=Item 232 Enabled\nInputSigType2245=Digital\nInputCue2246=Item 233 Enabled\nInputSigType2246=Digital\nInputCue2247=Item 234 Enabled\nInputSigType2247=Digital\nInputCue2248=Item 235 Enabled\nInputSigType2248=Digital\nInputCue2249=Item 236 Enabled\nInputSigType2249=Digital\nInputCue2250=Item 237 Enabled\nInputSigType2250=Digital\nInputCue2251=Item 238 Enabled\nInputSigType2251=Digital\nInputCue2252=Item 239 Enabled\nInputSigType2252=Digital\nInputCue2253=Item 240 Enabled\nInputSigType2253=Digital\nInputCue2254=Item 241 Enabled\nInputSigType2254=Digital\nInputCue2255=Item 242 Enabled\nInputSigType2255=Digital\nInputCue2256=Item 243 Enabled\nInputSigType2256=Digital\nInputCue2257=Item 244 Enabled\nInputSigType2257=Digital\nInputCue2258=Item 245 Enabled\nInputSigType2258=Digital\nInputCue2259=Item 246 Enabled\nInputSigType2259=Digital\nInputCue2260=Item 247 Enabled\nInputSigType2260=Digital\nInputCue2261=Item 248 Enabled\nInputSigType2261=Digital\nInputCue2262=Item 249 Enabled\nInputSigType2262=Digital\nInputCue2263=Item 250 Enabled\nInputSigType2263=Digital\nInputCue2264=Item 251 Enabled\nInputSigType2264=Digital\nInputCue2265=Item 252 Enabled\nInputSigType2265=Digital\nInputCue2266=Item 253 Enabled\nInputSigType2266=Digital\nInputCue2267=Item 254 Enabled\nInputSigType2267=Digital\nInputCue2268=Item 255 Enabled\nInputSigType2268=Digital\nInputCue2269=[~UNUSED2~]\nInputSigType2269=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=Item 11 Visible\nInputSigType4026=Digital\nInputCue4027=Item 12 Visible\nInputSigType4027=Digital\nInputCue4028=Item 13 Visible\nInputSigType4028=Digital\nInputCue4029=Item 14 Visible\nInputSigType4029=Digital\nInputCue4030=Item 15 Visible\nInputSigType4030=Digital\nInputCue4031=Item 16 Visible\nInputSigType4031=Digital\nInputCue4032=Item 17 Visible\nInputSigType4032=Digital\nInputCue4033=Item 18 Visible\nInputSigType4033=Digital\nInputCue4034=Item 19 Visible\nInputSigType4034=Digital\nInputCue4035=Item 20 Visible\nInputSigType4035=Digital\nInputCue4036=Item 21 Visible\nInputSigType4036=Digital\nInputCue4037=Item 22 Visible\nInputSigType4037=Digital\nInputCue4038=Item 23 Visible\nInputSigType4038=Digital\nInputCue4039=Item 24 Visible\nInputSigType4039=Digital\nInputCue4040=Item 25 Visible\nInputSigType4040=Digital\nInputCue4041=Item 26 Visible\nInputSigType4041=Digital\nInputCue4042=Item 27 Visible\nInputSigType4042=Digital\nInputCue4043=Item 28 Visible\nInputSigType4043=Digital\nInputCue4044=Item 29 Visible\nInputSigType4044=Digital\nInputCue4045=Item 30 Visible\nInputSigType4045=Digital\nInputCue4046=Item 31 Visible\nInputSigType4046=Digital\nInputCue4047=Item 32 Visible\nInputSigType4047=Digital\nInputCue4048=Item 33 Visible\nInputSigType4048=Digital\nInputCue4049=Item 34 Visible\nInputSigType4049=Digital\nInputCue4050=Item 35 Visible\nInputSigType4050=Digital\nInputCue4051=Item 36 Visible\nInputSigType4051=Digital\nInputCue4052=Item 37 Visible\nInputSigType4052=Digital\nInputCue4053=Item 38 Visible\nInputSigType4053=Digital\nInputCue4054=Item 39 Visible\nInputSigType4054=Digital\nInputCue4055=Item 40 Visible\nInputSigType4055=Digital\nInputCue4056=Item 41 Visible\nInputSigType4056=Digital\nInputCue4057=Item 42 Visible\nInputSigType4057=Digital\nInputCue4058=Item 43 Visible\nInputSigType4058=Digital\nInputCue4059=Item 44 Visible\nInputSigType4059=Digital\nInputCue4060=Item 45 Visible\nInputSigType4060=Digital\nInputCue4061=Item 46 Visible\nInputSigType4061=Digital\nInputCue4062=Item 47 Visible\nInputSigType4062=Digital\nInputCue4063=Item 48 Visible\nInputSigType4063=Digital\nInputCue4064=Item 49 Visible\nInputSigType4064=Digital\nInputCue4065=Item 50 Visible\nInputSigType4065=Digital\nInputCue4066=Item 51 Visible\nInputSigType4066=Digital\nInputCue4067=Item 52 Visible\nInputSigType4067=Digital\nInputCue4068=Item 53 Visible\nInputSigType4068=Digital\nInputCue4069=Item 54 Visible\nInputSigType4069=Digital\nInputCue4070=Item 55 Visible\nInputSigType4070=Digital\nInputCue4071=Item 56 Visible\nInputSigType4071=Digital\nInputCue4072=Item 57 Visible\nInputSigType4072=Digital\nInputCue4073=Item 58 Visible\nInputSigType4073=Digital\nInputCue4074=Item 59 Visible\nInputSigType4074=Digital\nInputCue4075=Item 60 Visible\nInputSigType4075=Digital\nInputCue4076=Item 61 Visible\nInputSigType4076=Digital\nInputCue4077=Item 62 Visible\nInputSigType4077=Digital\nInputCue4078=Item 63 Visible\nInputSigType4078=Digital\nInputCue4079=Item 64 Visible\nInputSigType4079=Digital\nInputCue4080=Item 65 Visible\nInputSigType4080=Digital\nInputCue4081=Item 66 Visible\nInputSigType4081=Digital\nInputCue4082=Item 67 Visible\nInputSigType4082=Digital\nInputCue4083=Item 68 Visible\nInputSigType4083=Digital\nInputCue4084=Item 69 Visible\nInputSigType4084=Digital\nInputCue4085=Item 70 Visible\nInputSigType4085=Digital\nInputCue4086=Item 71 Visible\nInputSigType4086=Digital\nInputCue4087=Item 72 Visible\nInputSigType4087=Digital\nInputCue4088=Item 73 Visible\nInputSigType4088=Digital\nInputCue4089=Item 74 Visible\nInputSigType4089=Digital\nInputCue4090=Item 75 Visible\nInputSigType4090=Digital\nInputCue4091=Item 76 Visible\nInputSigType4091=Digital\nInputCue4092=Item 77 Visible\nInputSigType4092=Digital\nInputCue4093=Item 78 Visible\nInputSigType4093=Digital\nInputCue4094=Item 79 Visible\nInputSigType4094=Digital\nInputCue4095=Item 80 Visible\nInputSigType4095=Digital\nInputCue4096=Item 81 Visible\nInputSigType4096=Digital\nInputCue4097=Item 82 Visible\nInputSigType4097=Digital\nInputCue4098=Item 83 Visible\nInputSigType4098=Digital\nInputCue4099=Item 84 Visible\nInputSigType4099=Digital\nInputCue4100=Item 85 Visible\nInputSigType4100=Digital\nInputCue4101=Item 86 Visible\nInputSigType4101=Digital\nInputCue4102=Item 87 Visible\nInputSigType4102=Digital\nInputCue4103=Item 88 Visible\nInputSigType4103=Digital\nInputCue4104=Item 89 Visible\nInputSigType4104=Digital\nInputCue4105=Item 90 Visible\nInputSigType4105=Digital\nInputCue4106=Item 91 Visible\nInputSigType4106=Digital\nInputCue4107=Item 92 Visible\nInputSigType4107=Digital\nInputCue4108=Item 93 Visible\nInputSigType4108=Digital\nInputCue4109=Item 94 Visible\nInputSigType4109=Digital\nInputCue4110=Item 95 Visible\nInputSigType4110=Digital\nInputCue4111=Item 96 Visible\nInputSigType4111=Digital\nInputCue4112=Item 97 Visible\nInputSigType4112=Digital\nInputCue4113=Item 98 Visible\nInputSigType4113=Digital\nInputCue4114=Item 99 Visible\nInputSigType4114=Digital\nInputCue4115=Item 100 Visible\nInputSigType4115=Digital\nInputCue4116=Item 101 Visible\nInputSigType4116=Digital\nInputCue4117=Item 102 Visible\nInputSigType4117=Digital\nInputCue4118=Item 103 Visible\nInputSigType4118=Digital\nInputCue4119=Item 104 Visible\nInputSigType4119=Digital\nInputCue4120=Item 105 Visible\nInputSigType4120=Digital\nInputCue4121=Item 106 Visible\nInputSigType4121=Digital\nInputCue4122=Item 107 Visible\nInputSigType4122=Digital\nInputCue4123=Item 108 Visible\nInputSigType4123=Digital\nInputCue4124=Item 109 Visible\nInputSigType4124=Digital\nInputCue4125=Item 110 Visible\nInputSigType4125=Digital\nInputCue4126=Item 111 Visible\nInputSigType4126=Digital\nInputCue4127=Item 112 Visible\nInputSigType4127=Digital\nInputCue4128=Item 113 Visible\nInputSigType4128=Digital\nInputCue4129=Item 114 Visible\nInputSigType4129=Digital\nInputCue4130=Item 115 Visible\nInputSigType4130=Digital\nInputCue4131=Item 116 Visible\nInputSigType4131=Digital\nInputCue4132=Item 117 Visible\nInputSigType4132=Digital\nInputCue4133=Item 118 Visible\nInputSigType4133=Digital\nInputCue4134=Item 119 Visible\nInputSigType4134=Digital\nInputCue4135=Item 120 Visible\nInputSigType4135=Digital\nInputCue4136=Item 121 Visible\nInputSigType4136=Digital\nInputCue4137=Item 122 Visible\nInputSigType4137=Digital\nInputCue4138=Item 123 Visible\nInputSigType4138=Digital\nInputCue4139=Item 124 Visible\nInputSigType4139=Digital\nInputCue4140=Item 125 Visible\nInputSigType4140=Digital\nInputCue4141=Item 126 Visible\nInputSigType4141=Digital\nInputCue4142=Item 127 Visible\nInputSigType4142=Digital\nInputCue4143=Item 128 Visible\nInputSigType4143=Digital\nInputCue4144=Item 129 Visible\nInputSigType4144=Digital\nInputCue4145=Item 130 Visible\nInputSigType4145=Digital\nInputCue4146=Item 131 Visible\nInputSigType4146=Digital\nInputCue4147=Item 132 Visible\nInputSigType4147=Digital\nInputCue4148=Item 133 Visible\nInputSigType4148=Digital\nInputCue4149=Item 134 Visible\nInputSigType4149=Digital\nInputCue4150=Item 135 Visible\nInputSigType4150=Digital\nInputCue4151=Item 136 Visible\nInputSigType4151=Digital\nInputCue4152=Item 137 Visible\nInputSigType4152=Digital\nInputCue4153=Item 138 Visible\nInputSigType4153=Digital\nInputCue4154=Item 139 Visible\nInputSigType4154=Digital\nInputCue4155=Item 140 Visible\nInputSigType4155=Digital\nInputCue4156=Item 141 Visible\nInputSigType4156=Digital\nInputCue4157=Item 142 Visible\nInputSigType4157=Digital\nInputCue4158=Item 143 Visible\nInputSigType4158=Digital\nInputCue4159=Item 144 Visible\nInputSigType4159=Digital\nInputCue4160=Item 145 Visible\nInputSigType4160=Digital\nInputCue4161=Item 146 Visible\nInputSigType4161=Digital\nInputCue4162=Item 147 Visible\nInputSigType4162=Digital\nInputCue4163=Item 148 Visible\nInputSigType4163=Digital\nInputCue4164=Item 149 Visible\nInputSigType4164=Digital\nInputCue4165=Item 150 Visible\nInputSigType4165=Digital\nInputCue4166=Item 151 Visible\nInputSigType4166=Digital\nInputCue4167=Item 152 Visible\nInputSigType4167=Digital\nInputCue4168=Item 153 Visible\nInputSigType4168=Digital\nInputCue4169=Item 154 Visible\nInputSigType4169=Digital\nInputCue4170=Item 155 Visible\nInputSigType4170=Digital\nInputCue4171=Item 156 Visible\nInputSigType4171=Digital\nInputCue4172=Item 157 Visible\nInputSigType4172=Digital\nInputCue4173=Item 158 Visible\nInputSigType4173=Digital\nInputCue4174=Item 159 Visible\nInputSigType4174=Digital\nInputCue4175=Item 160 Visible\nInputSigType4175=Digital\nInputCue4176=Item 161 Visible\nInputSigType4176=Digital\nInputCue4177=Item 162 Visible\nInputSigType4177=Digital\nInputCue4178=Item 163 Visible\nInputSigType4178=Digital\nInputCue4179=Item 164 Visible\nInputSigType4179=Digital\nInputCue4180=Item 165 Visible\nInputSigType4180=Digital\nInputCue4181=Item 166 Visible\nInputSigType4181=Digital\nInputCue4182=Item 167 Visible\nInputSigType4182=Digital\nInputCue4183=Item 168 Visible\nInputSigType4183=Digital\nInputCue4184=Item 169 Visible\nInputSigType4184=Digital\nInputCue4185=Item 170 Visible\nInputSigType4185=Digital\nInputCue4186=Item 171 Visible\nInputSigType4186=Digital\nInputCue4187=Item 172 Visible\nInputSigType4187=Digital\nInputCue4188=Item 173 Visible\nInputSigType4188=Digital\nInputCue4189=Item 174 Visible\nInputSigType4189=Digital\nInputCue4190=Item 175 Visible\nInputSigType4190=Digital\nInputCue4191=Item 176 Visible\nInputSigType4191=Digital\nInputCue4192=Item 177 Visible\nInputSigType4192=Digital\nInputCue4193=Item 178 Visible\nInputSigType4193=Digital\nInputCue4194=Item 179 Visible\nInputSigType4194=Digital\nInputCue4195=Item 180 Visible\nInputSigType4195=Digital\nInputCue4196=Item 181 Visible\nInputSigType4196=Digital\nInputCue4197=Item 182 Visible\nInputSigType4197=Digital\nInputCue4198=Item 183 Visible\nInputSigType4198=Digital\nInputCue4199=Item 184 Visible\nInputSigType4199=Digital\nInputCue4200=Item 185 Visible\nInputSigType4200=Digital\nInputCue4201=Item 186 Visible\nInputSigType4201=Digital\nInputCue4202=Item 187 Visible\nInputSigType4202=Digital\nInputCue4203=Item 188 Visible\nInputSigType4203=Digital\nInputCue4204=Item 189 Visible\nInputSigType4204=Digital\nInputCue4205=Item 190 Visible\nInputSigType4205=Digital\nInputCue4206=Item 191 Visible\nInputSigType4206=Digital\nInputCue4207=Item 192 Visible\nInputSigType4207=Digital\nInputCue4208=Item 193 Visible\nInputSigType4208=Digital\nInputCue4209=Item 194 Visible\nInputSigType4209=Digital\nInputCue4210=Item 195 Visible\nInputSigType4210=Digital\nInputCue4211=Item 196 Visible\nInputSigType4211=Digital\nInputCue4212=Item 197 Visible\nInputSigType4212=Digital\nInputCue4213=Item 198 Visible\nInputSigType4213=Digital\nInputCue4214=Item 199 Visible\nInputSigType4214=Digital\nInputCue4215=Item 200 Visible\nInputSigType4215=Digital\nInputCue4216=Item 201 Visible\nInputSigType4216=Digital\nInputCue4217=Item 202 Visible\nInputSigType4217=Digital\nInputCue4218=Item 203 Visible\nInputSigType4218=Digital\nInputCue4219=Item 204 Visible\nInputSigType4219=Digital\nInputCue4220=Item 205 Visible\nInputSigType4220=Digital\nInputCue4221=Item 206 Visible\nInputSigType4221=Digital\nInputCue4222=Item 207 Visible\nInputSigType4222=Digital\nInputCue4223=Item 208 Visible\nInputSigType4223=Digital\nInputCue4224=Item 209 Visible\nInputSigType4224=Digital\nInputCue4225=Item 210 Visible\nInputSigType4225=Digital\nInputCue4226=Item 211 Visible\nInputSigType4226=Digital\nInputCue4227=Item 212 Visible\nInputSigType4227=Digital\nInputCue4228=Item 213 Visible\nInputSigType4228=Digital\nInputCue4229=Item 214 Visible\nInputSigType4229=Digital\nInputCue4230=Item 215 Visible\nInputSigType4230=Digital\nInputCue4231=Item 216 Visible\nInputSigType4231=Digital\nInputCue4232=Item 217 Visible\nInputSigType4232=Digital\nInputCue4233=Item 218 Visible\nInputSigType4233=Digital\nInputCue4234=Item 219 Visible\nInputSigType4234=Digital\nInputCue4235=Item 220 Visible\nInputSigType4235=Digital\nInputCue4236=Item 221 Visible\nInputSigType4236=Digital\nInputCue4237=Item 222 Visible\nInputSigType4237=Digital\nInputCue4238=Item 223 Visible\nInputSigType4238=Digital\nInputCue4239=Item 224 Visible\nInputSigType4239=Digital\nInputCue4240=Item 225 Visible\nInputSigType4240=Digital\nInputCue4241=Item 226 Visible\nInputSigType4241=Digital\nInputCue4242=Item 227 Visible\nInputSigType4242=Digital\nInputCue4243=Item 228 Visible\nInputSigType4243=Digital\nInputCue4244=Item 229 Visible\nInputSigType4244=Digital\nInputCue4245=Item 230 Visible\nInputSigType4245=Digital\nInputCue4246=Item 231 Visible\nInputSigType4246=Digital\nInputCue4247=Item 232 Visible\nInputSigType4247=Digital\nInputCue4248=Item 233 Visible\nInputSigType4248=Digital\nInputCue4249=Item 234 Visible\nInputSigType4249=Digital\nInputCue4250=Item 235 Visible\nInputSigType4250=Digital\nInputCue4251=Item 236 Visible\nInputSigType4251=Digital\nInputCue4252=Item 237 Visible\nInputSigType4252=Digital\nInputCue4253=Item 238 Visible\nInputSigType4253=Digital\nInputCue4254=Item 239 Visible\nInputSigType4254=Digital\nInputCue4255=Item 240 Visible\nInputSigType4255=Digital\nInputCue4256=Item 241 Visible\nInputSigType4256=Digital\nInputCue4257=Item 242 Visible\nInputSigType4257=Digital\nInputCue4258=Item 243 Visible\nInputSigType4258=Digital\nInputCue4259=Item 244 Visible\nInputSigType4259=Digital\nInputCue4260=Item 245 Visible\nInputSigType4260=Digital\nInputCue4261=Item 246 Visible\nInputSigType4261=Digital\nInputCue4262=Item 247 Visible\nInputSigType4262=Digital\nInputCue4263=Item 248 Visible\nInputSigType4263=Digital\nInputCue4264=Item 249 Visible\nInputSigType4264=Digital\nInputCue4265=Item 250 Visible\nInputSigType4265=Digital\nInputCue4266=Item 251 Visible\nInputSigType4266=Digital\nInputCue4267=Item 252 Visible\nInputSigType4267=Digital\nInputCue4268=Item 253 Visible\nInputSigType4268=Digital\nInputCue4269=Item 254 Visible\nInputSigType4269=Digital\nInputCue4270=Item 255 Visible\nInputSigType4270=Digital\nInputCue4271=[~UNUSED2~]\nInputSigType4271=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=Item 11 Pressed\nOutputSigType22=Digital\nOutputCue23=Item 12 Pressed\nOutputSigType23=Digital\nOutputCue24=Item 13 Pressed\nOutputSigType24=Digital\nOutputCue25=Item 14 Pressed\nOutputSigType25=Digital\nOutputCue26=Item 15 Pressed\nOutputSigType26=Digital\nOutputCue27=Item 16 Pressed\nOutputSigType27=Digital\nOutputCue28=Item 17 Pressed\nOutputSigType28=Digital\nOutputCue29=Item 18 Pressed\nOutputSigType29=Digital\nOutputCue30=Item 19 Pressed\nOutputSigType30=Digital\nOutputCue31=Item 20 Pressed\nOutputSigType31=Digital\nOutputCue32=Item 21 Pressed\nOutputSigType32=Digital\nOutputCue33=Item 22 Pressed\nOutputSigType33=Digital\nOutputCue34=Item 23 Pressed\nOutputSigType34=Digital\nOutputCue35=Item 24 Pressed\nOutputSigType35=Digital\nOutputCue36=Item 25 Pressed\nOutputSigType36=Digital\nOutputCue37=Item 26 Pressed\nOutputSigType37=Digital\nOutputCue38=Item 27 Pressed\nOutputSigType38=Digital\nOutputCue39=Item 28 Pressed\nOutputSigType39=Digital\nOutputCue40=Item 29 Pressed\nOutputSigType40=Digital\nOutputCue41=Item 30 Pressed\nOutputSigType41=Digital\nOutputCue42=Item 31 Pressed\nOutputSigType42=Digital\nOutputCue43=Item 32 Pressed\nOutputSigType43=Digital\nOutputCue44=Item 33 Pressed\nOutputSigType44=Digital\nOutputCue45=Item 34 Pressed\nOutputSigType45=Digital\nOutputCue46=Item 35 Pressed\nOutputSigType46=Digital\nOutputCue47=Item 36 Pressed\nOutputSigType47=Digital\nOutputCue48=Item 37 Pressed\nOutputSigType48=Digital\nOutputCue49=Item 38 Pressed\nOutputSigType49=Digital\nOutputCue50=Item 39 Pressed\nOutputSigType50=Digital\nOutputCue51=Item 40 Pressed\nOutputSigType51=Digital\nOutputCue52=Item 41 Pressed\nOutputSigType52=Digital\nOutputCue53=Item 42 Pressed\nOutputSigType53=Digital\nOutputCue54=Item 43 Pressed\nOutputSigType54=Digital\nOutputCue55=Item 44 Pressed\nOutputSigType55=Digital\nOutputCue56=Item 45 Pressed\nOutputSigType56=Digital\nOutputCue57=Item 46 Pressed\nOutputSigType57=Digital\nOutputCue58=Item 47 Pressed\nOutputSigType58=Digital\nOutputCue59=Item 48 Pressed\nOutputSigType59=Digital\nOutputCue60=Item 49 Pressed\nOutputSigType60=Digital\nOutputCue61=Item 50 Pressed\nOutputSigType61=Digital\nOutputCue62=Item 51 Pressed\nOutputSigType62=Digital\nOutputCue63=Item 52 Pressed\nOutputSigType63=Digital\nOutputCue64=Item 53 Pressed\nOutputSigType64=Digital\nOutputCue65=Item 54 Pressed\nOutputSigType65=Digital\nOutputCue66=Item 55 Pressed\nOutputSigType66=Digital\nOutputCue67=Item 56 Pressed\nOutputSigType67=Digital\nOutputCue68=Item 57 Pressed\nOutputSigType68=Digital\nOutputCue69=Item 58 Pressed\nOutputSigType69=Digital\nOutputCue70=Item 59 Pressed\nOutputSigType70=Digital\nOutputCue71=Item 60 Pressed\nOutputSigType71=Digital\nOutputCue72=Item 61 Pressed\nOutputSigType72=Digital\nOutputCue73=Item 62 Pressed\nOutputSigType73=Digital\nOutputCue74=Item 63 Pressed\nOutputSigType74=Digital\nOutputCue75=Item 64 Pressed\nOutputSigType75=Digital\nOutputCue76=Item 65 Pressed\nOutputSigType76=Digital\nOutputCue77=Item 66 Pressed\nOutputSigType77=Digital\nOutputCue78=Item 67 Pressed\nOutputSigType78=Digital\nOutputCue79=Item 68 Pressed\nOutputSigType79=Digital\nOutputCue80=Item 69 Pressed\nOutputSigType80=Digital\nOutputCue81=Item 70 Pressed\nOutputSigType81=Digital\nOutputCue82=Item 71 Pressed\nOutputSigType82=Digital\nOutputCue83=Item 72 Pressed\nOutputSigType83=Digital\nOutputCue84=Item 73 Pressed\nOutputSigType84=Digital\nOutputCue85=Item 74 Pressed\nOutputSigType85=Digital\nOutputCue86=Item 75 Pressed\nOutputSigType86=Digital\nOutputCue87=Item 76 Pressed\nOutputSigType87=Digital\nOutputCue88=Item 77 Pressed\nOutputSigType88=Digital\nOutputCue89=Item 78 Pressed\nOutputSigType89=Digital\nOutputCue90=Item 79 Pressed\nOutputSigType90=Digital\nOutputCue91=Item 80 Pressed\nOutputSigType91=Digital\nOutputCue92=Item 81 Pressed\nOutputSigType92=Digital\nOutputCue93=Item 82 Pressed\nOutputSigType93=Digital\nOutputCue94=Item 83 Pressed\nOutputSigType94=Digital\nOutputCue95=Item 84 Pressed\nOutputSigType95=Digital\nOutputCue96=Item 85 Pressed\nOutputSigType96=Digital\nOutputCue97=Item 86 Pressed\nOutputSigType97=Digital\nOutputCue98=Item 87 Pressed\nOutputSigType98=Digital\nOutputCue99=Item 88 Pressed\nOutputSigType99=Digital\nOutputCue100=Item 89 Pressed\nOutputSigType100=Digital\nOutputCue101=Item 90 Pressed\nOutputSigType101=Digital\nOutputCue102=Item 91 Pressed\nOutputSigType102=Digital\nOutputCue103=Item 92 Pressed\nOutputSigType103=Digital\nOutputCue104=Item 93 Pressed\nOutputSigType104=Digital\nOutputCue105=Item 94 Pressed\nOutputSigType105=Digital\nOutputCue106=Item 95 Pressed\nOutputSigType106=Digital\nOutputCue107=Item 96 Pressed\nOutputSigType107=Digital\nOutputCue108=Item 97 Pressed\nOutputSigType108=Digital\nOutputCue109=Item 98 Pressed\nOutputSigType109=Digital\nOutputCue110=Item 99 Pressed\nOutputSigType110=Digital\nOutputCue111=Item 100 Pressed\nOutputSigType111=Digital\nOutputCue112=Item 101 Pressed\nOutputSigType112=Digital\nOutputCue113=Item 102 Pressed\nOutputSigType113=Digital\nOutputCue114=Item 103 Pressed\nOutputSigType114=Digital\nOutputCue115=Item 104 Pressed\nOutputSigType115=Digital\nOutputCue116=Item 105 Pressed\nOutputSigType116=Digital\nOutputCue117=Item 106 Pressed\nOutputSigType117=Digital\nOutputCue118=Item 107 Pressed\nOutputSigType118=Digital\nOutputCue119=Item 108 Pressed\nOutputSigType119=Digital\nOutputCue120=Item 109 Pressed\nOutputSigType120=Digital\nOutputCue121=Item 110 Pressed\nOutputSigType121=Digital\nOutputCue122=Item 111 Pressed\nOutputSigType122=Digital\nOutputCue123=Item 112 Pressed\nOutputSigType123=Digital\nOutputCue124=Item 113 Pressed\nOutputSigType124=Digital\nOutputCue125=Item 114 Pressed\nOutputSigType125=Digital\nOutputCue126=Item 115 Pressed\nOutputSigType126=Digital\nOutputCue127=Item 116 Pressed\nOutputSigType127=Digital\nOutputCue128=Item 117 Pressed\nOutputSigType128=Digital\nOutputCue129=Item 118 Pressed\nOutputSigType129=Digital\nOutputCue130=Item 119 Pressed\nOutputSigType130=Digital\nOutputCue131=Item 120 Pressed\nOutputSigType131=Digital\nOutputCue132=Item 121 Pressed\nOutputSigType132=Digital\nOutputCue133=Item 122 Pressed\nOutputSigType133=Digital\nOutputCue134=Item 123 Pressed\nOutputSigType134=Digital\nOutputCue135=Item 124 Pressed\nOutputSigType135=Digital\nOutputCue136=Item 125 Pressed\nOutputSigType136=Digital\nOutputCue137=Item 126 Pressed\nOutputSigType137=Digital\nOutputCue138=Item 127 Pressed\nOutputSigType138=Digital\nOutputCue139=Item 128 Pressed\nOutputSigType139=Digital\nOutputCue140=Item 129 Pressed\nOutputSigType140=Digital\nOutputCue141=Item 130 Pressed\nOutputSigType141=Digital\nOutputCue142=Item 131 Pressed\nOutputSigType142=Digital\nOutputCue143=Item 132 Pressed\nOutputSigType143=Digital\nOutputCue144=Item 133 Pressed\nOutputSigType144=Digital\nOutputCue145=Item 134 Pressed\nOutputSigType145=Digital\nOutputCue146=Item 135 Pressed\nOutputSigType146=Digital\nOutputCue147=Item 136 Pressed\nOutputSigType147=Digital\nOutputCue148=Item 137 Pressed\nOutputSigType148=Digital\nOutputCue149=Item 138 Pressed\nOutputSigType149=Digital\nOutputCue150=Item 139 Pressed\nOutputSigType150=Digital\nOutputCue151=Item 140 Pressed\nOutputSigType151=Digital\nOutputCue152=Item 141 Pressed\nOutputSigType152=Digital\nOutputCue153=Item 142 Pressed\nOutputSigType153=Digital\nOutputCue154=Item 143 Pressed\nOutputSigType154=Digital\nOutputCue155=Item 144 Pressed\nOutputSigType155=Digital\nOutputCue156=Item 145 Pressed\nOutputSigType156=Digital\nOutputCue157=Item 146 Pressed\nOutputSigType157=Digital\nOutputCue158=Item 147 Pressed\nOutputSigType158=Digital\nOutputCue159=Item 148 Pressed\nOutputSigType159=Digital\nOutputCue160=Item 149 Pressed\nOutputSigType160=Digital\nOutputCue161=Item 150 Pressed\nOutputSigType161=Digital\nOutputCue162=Item 151 Pressed\nOutputSigType162=Digital\nOutputCue163=Item 152 Pressed\nOutputSigType163=Digital\nOutputCue164=Item 153 Pressed\nOutputSigType164=Digital\nOutputCue165=Item 154 Pressed\nOutputSigType165=Digital\nOutputCue166=Item 155 Pressed\nOutputSigType166=Digital\nOutputCue167=Item 156 Pressed\nOutputSigType167=Digital\nOutputCue168=Item 157 Pressed\nOutputSigType168=Digital\nOutputCue169=Item 158 Pressed\nOutputSigType169=Digital\nOutputCue170=Item 159 Pressed\nOutputSigType170=Digital\nOutputCue171=Item 160 Pressed\nOutputSigType171=Digital\nOutputCue172=Item 161 Pressed\nOutputSigType172=Digital\nOutputCue173=Item 162 Pressed\nOutputSigType173=Digital\nOutputCue174=Item 163 Pressed\nOutputSigType174=Digital\nOutputCue175=Item 164 Pressed\nOutputSigType175=Digital\nOutputCue176=Item 165 Pressed\nOutputSigType176=Digital\nOutputCue177=Item 166 Pressed\nOutputSigType177=Digital\nOutputCue178=Item 167 Pressed\nOutputSigType178=Digital\nOutputCue179=Item 168 Pressed\nOutputSigType179=Digital\nOutputCue180=Item 169 Pressed\nOutputSigType180=Digital\nOutputCue181=Item 170 Pressed\nOutputSigType181=Digital\nOutputCue182=Item 171 Pressed\nOutputSigType182=Digital\nOutputCue183=Item 172 Pressed\nOutputSigType183=Digital\nOutputCue184=Item 173 Pressed\nOutputSigType184=Digital\nOutputCue185=Item 174 Pressed\nOutputSigType185=Digital\nOutputCue186=Item 175 Pressed\nOutputSigType186=Digital\nOutputCue187=Item 176 Pressed\nOutputSigType187=Digital\nOutputCue188=Item 177 Pressed\nOutputSigType188=Digital\nOutputCue189=Item 178 Pressed\nOutputSigType189=Digital\nOutputCue190=Item 179 Pressed\nOutputSigType190=Digital\nOutputCue191=Item 180 Pressed\nOutputSigType191=Digital\nOutputCue192=Item 181 Pressed\nOutputSigType192=Digital\nOutputCue193=Item 182 Pressed\nOutputSigType193=Digital\nOutputCue194=Item 183 Pressed\nOutputSigType194=Digital\nOutputCue195=Item 184 Pressed\nOutputSigType195=Digital\nOutputCue196=Item 185 Pressed\nOutputSigType196=Digital\nOutputCue197=Item 186 Pressed\nOutputSigType197=Digital\nOutputCue198=Item 187 Pressed\nOutputSigType198=Digital\nOutputCue199=Item 188 Pressed\nOutputSigType199=Digital\nOutputCue200=Item 189 Pressed\nOutputSigType200=Digital\nOutputCue201=Item 190 Pressed\nOutputSigType201=Digital\nOutputCue202=Item 191 Pressed\nOutputSigType202=Digital\nOutputCue203=Item 192 Pressed\nOutputSigType203=Digital\nOutputCue204=Item 193 Pressed\nOutputSigType204=Digital\nOutputCue205=Item 194 Pressed\nOutputSigType205=Digital\nOutputCue206=Item 195 Pressed\nOutputSigType206=Digital\nOutputCue207=Item 196 Pressed\nOutputSigType207=Digital\nOutputCue208=Item 197 Pressed\nOutputSigType208=Digital\nOutputCue209=Item 198 Pressed\nOutputSigType209=Digital\nOutputCue210=Item 199 Pressed\nOutputSigType210=Digital\nOutputCue211=Item 200 Pressed\nOutputSigType211=Digital\nOutputCue212=Item 201 Pressed\nOutputSigType212=Digital\nOutputCue213=Item 202 Pressed\nOutputSigType213=Digital\nOutputCue214=Item 203 Pressed\nOutputSigType214=Digital\nOutputCue215=Item 204 Pressed\nOutputSigType215=Digital\nOutputCue216=Item 205 Pressed\nOutputSigType216=Digital\nOutputCue217=Item 206 Pressed\nOutputSigType217=Digital\nOutputCue218=Item 207 Pressed\nOutputSigType218=Digital\nOutputCue219=Item 208 Pressed\nOutputSigType219=Digital\nOutputCue220=Item 209 Pressed\nOutputSigType220=Digital\nOutputCue221=Item 210 Pressed\nOutputSigType221=Digital\nOutputCue222=Item 211 Pressed\nOutputSigType222=Digital\nOutputCue223=Item 212 Pressed\nOutputSigType223=Digital\nOutputCue224=Item 213 Pressed\nOutputSigType224=Digital\nOutputCue225=Item 214 Pressed\nOutputSigType225=Digital\nOutputCue226=Item 215 Pressed\nOutputSigType226=Digital\nOutputCue227=Item 216 Pressed\nOutputSigType227=Digital\nOutputCue228=Item 217 Pressed\nOutputSigType228=Digital\nOutputCue229=Item 218 Pressed\nOutputSigType229=Digital\nOutputCue230=Item 219 Pressed\nOutputSigType230=Digital\nOutputCue231=Item 220 Pressed\nOutputSigType231=Digital\nOutputCue232=Item 221 Pressed\nOutputSigType232=Digital\nOutputCue233=Item 222 Pressed\nOutputSigType233=Digital\nOutputCue234=Item 223 Pressed\nOutputSigType234=Digital\nOutputCue235=Item 224 Pressed\nOutputSigType235=Digital\nOutputCue236=Item 225 Pressed\nOutputSigType236=Digital\nOutputCue237=Item 226 Pressed\nOutputSigType237=Digital\nOutputCue238=Item 227 Pressed\nOutputSigType238=Digital\nOutputCue239=Item 228 Pressed\nOutputSigType239=Digital\nOutputCue240=Item 229 Pressed\nOutputSigType240=Digital\nOutputCue241=Item 230 Pressed\nOutputSigType241=Digital\nOutputCue242=Item 231 Pressed\nOutputSigType242=Digital\nOutputCue243=Item 232 Pressed\nOutputSigType243=Digital\nOutputCue244=Item 233 Pressed\nOutputSigType244=Digital\nOutputCue245=Item 234 Pressed\nOutputSigType245=Digital\nOutputCue246=Item 235 Pressed\nOutputSigType246=Digital\nOutputCue247=Item 236 Pressed\nOutputSigType247=Digital\nOutputCue248=Item 237 Pressed\nOutputSigType248=Digital\nOutputCue249=Item 238 Pressed\nOutputSigType249=Digital\nOutputCue250=Item 239 Pressed\nOutputSigType250=Digital\nOutputCue251=Item 240 Pressed\nOutputSigType251=Digital\nOutputCue252=Item 241 Pressed\nOutputSigType252=Digital\nOutputCue253=Item 242 Pressed\nOutputSigType253=Digital\nOutputCue254=Item 243 Pressed\nOutputSigType254=Digital\nOutputCue255=Item 244 Pressed\nOutputSigType255=Digital\nOutputCue256=Item 245 Pressed\nOutputSigType256=Digital\nOutputCue257=Item 246 Pressed\nOutputSigType257=Digital\nOutputCue258=Item 247 Pressed\nOutputSigType258=Digital\nOutputCue259=Item 248 Pressed\nOutputSigType259=Digital\nOutputCue260=Item 249 Pressed\nOutputSigType260=Digital\nOutputCue261=Item 250 Pressed\nOutputSigType261=Digital\nOutputCue262=Item 251 Pressed\nOutputSigType262=Digital\nOutputCue263=Item 252 Pressed\nOutputSigType263=Digital\nOutputCue264=Item 253 Pressed\nOutputSigType264=Digital\nOutputCue265=Item 254 Pressed\nOutputSigType265=Digital\nOutputCue266=Item 255 Pressed\nOutputSigType266=Digital\nOutputCue267=[~UNUSED2~]\nOutputSigType267=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED3~]\nOutputSigType2044=Digital\nOutputCue2045=[~UNUSED3~]\nOutputSigType2045=Digital\nOutputCue2046=[~UNUSED3~]\nOutputSigType2046=Digital\nOutputCue2047=[~UNUSED3~]\nOutputSigType2047=Digital\nOutputCue2048=[~UNUSED3~]\nOutputSigType2048=Digital\nOutputCue2049=[~UNUSED3~]\nOutputSigType2049=Digital\nOutputCue2050=[~UNUSED3~]\nOutputSigType2050=Digital\nOutputCue2051=[~UNUSED3~]\nOutputSigType2051=Digital\nOutputCue2052=[~UNUSED3~]\nOutputSigType2052=Digital\nOutputCue2053=[~UNUSED3~]\nOutputSigType2053=Digital\nOutputCue2054=[~UNUSED3~]\nOutputSigType2054=Digital\nOutputCue2055=[~UNUSED3~]\nOutputSigType2055=Digital\nOutputCue2056=[~UNUSED3~]\nOutputSigType2056=Digital\nOutputCue2057=[~UNUSED3~]\nOutputSigType2057=Digital\nOutputCue2058=[~UNUSED3~]\nOutputSigType2058=Digital\nOutputCue2059=[~UNUSED3~]\nOutputSigType2059=Digital\nOutputCue2060=[~UNUSED3~]\nOutputSigType2060=Digital\nOutputCue2061=[~UNUSED3~]\nOutputSigType2061=Digital\nOutputCue2062=[~UNUSED3~]\nOutputSigType2062=Digital\nOutputCue2063=[~UNUSED3~]\nOutputSigType2063=Digital\nOutputCue2064=[~UNUSED3~]\nOutputSigType2064=Digital\nOutputCue2065=[~UNUSED3~]\nOutputSigType2065=Digital\nOutputCue2066=[~UNUSED3~]\nOutputSigType2066=Digital\nOutputCue2067=[~UNUSED3~]\nOutputSigType2067=Digital\nOutputCue2068=[~UNUSED3~]\nOutputSigType2068=Digital\nOutputCue2069=[~UNUSED3~]\nOutputSigType2069=Digital\nOutputCue2070=[~UNUSED3~]\nOutputSigType2070=Digital\nOutputCue2071=[~UNUSED3~]\nOutputSigType2071=Digital\nOutputCue2072=[~UNUSED3~]\nOutputSigType2072=Digital\nOutputCue2073=[~UNUSED3~]\nOutputSigType2073=Digital\nOutputCue2074=[~UNUSED3~]\nOutputSigType2074=Digital\nOutputCue2075=[~UNUSED3~]\nOutputSigType2075=Digital\nOutputCue2076=[~UNUSED3~]\nOutputSigType2076=Digital\nOutputCue2077=[~UNUSED3~]\nOutputSigType2077=Digital\nOutputCue2078=[~UNUSED3~]\nOutputSigType2078=Digital\nOutputCue2079=[~UNUSED3~]\nOutputSigType2079=Digital\nOutputCue2080=[~UNUSED3~]\nOutputSigType2080=Digital\nOutputCue2081=[~UNUSED3~]\nOutputSigType2081=Digital\nOutputCue2082=[~UNUSED3~]\nOutputSigType2082=Digital\nOutputCue2083=[~UNUSED3~]\nOutputSigType2083=Digital\nOutputCue2084=[~UNUSED3~]\nOutputSigType2084=Digital\nOutputCue2085=[~UNUSED3~]\nOutputSigType2085=Digital\nOutputCue2086=[~UNUSED3~]\nOutputSigType2086=Digital\nOutputCue2087=[~UNUSED3~]\nOutputSigType2087=Digital\nOutputCue2088=[~UNUSED3~]\nOutputSigType2088=Digital\nOutputCue2089=[~UNUSED3~]\nOutputSigType2089=Digital\nOutputCue2090=[~UNUSED3~]\nOutputSigType2090=Digital\nOutputCue2091=[~UNUSED3~]\nOutputSigType2091=Digital\nOutputCue2092=[~UNUSED3~]\nOutputSigType2092=Digital\nOutputCue2093=[~UNUSED3~]\nOutputSigType2093=Digital\nOutputCue2094=[~UNUSED3~]\nOutputSigType2094=Digital\nOutputCue2095=[~UNUSED3~]\nOutputSigType2095=Digital\nOutputCue2096=[~UNUSED3~]\nOutputSigType2096=Digital\nOutputCue2097=[~UNUSED3~]\nOutputSigType2097=Digital\nOutputCue2098=[~UNUSED3~]\nOutputSigType2098=Digital\nOutputCue2099=[~UNUSED3~]\nOutputSigType2099=Digital\nOutputCue2100=[~UNUSED3~]\nOutputSigType2100=Digital\nOutputCue2101=[~UNUSED3~]\nOutputSigType2101=Digital\nOutputCue2102=[~UNUSED3~]\nOutputSigType2102=Digital\nOutputCue2103=[~UNUSED3~]\nOutputSigType2103=Digital\nOutputCue2104=[~UNUSED3~]\nOutputSigType2104=Digital\nOutputCue2105=[~UNUSED3~]\nOutputSigType2105=Digital\nOutputCue2106=[~UNUSED3~]\nOutputSigType2106=Digital\nOutputCue2107=[~UNUSED3~]\nOutputSigType2107=Digital\nOutputCue2108=[~UNUSED3~]\nOutputSigType2108=Digital\nOutputCue2109=[~UNUSED3~]\nOutputSigType2109=Digital\nOutputCue2110=[~UNUSED3~]\nOutputSigType2110=Digital\nOutputCue2111=[~UNUSED3~]\nOutputSigType2111=Digital\nOutputCue2112=[~UNUSED3~]\nOutputSigType2112=Digital\nOutputCue2113=[~UNUSED3~]\nOutputSigType2113=Digital\nOutputCue2114=[~UNUSED3~]\nOutputSigType2114=Digital\nOutputCue2115=[~UNUSED3~]\nOutputSigType2115=Digital\nOutputCue2116=[~UNUSED3~]\nOutputSigType2116=Digital\nOutputCue2117=[~UNUSED3~]\nOutputSigType2117=Digital\nOutputCue2118=[~UNUSED3~]\nOutputSigType2118=Digital\nOutputCue2119=[~UNUSED3~]\nOutputSigType2119=Digital\nOutputCue2120=[~UNUSED3~]\nOutputSigType2120=Digital\nOutputCue2121=[~UNUSED3~]\nOutputSigType2121=Digital\nOutputCue2122=[~UNUSED3~]\nOutputSigType2122=Digital\nOutputCue2123=[~UNUSED3~]\nOutputSigType2123=Digital\nOutputCue2124=[~UNUSED3~]\nOutputSigType2124=Digital\nOutputCue2125=[~UNUSED3~]\nOutputSigType2125=Digital\nOutputCue2126=[~UNUSED3~]\nOutputSigType2126=Digital\nOutputCue2127=[~UNUSED3~]\nOutputSigType2127=Digital\nOutputCue2128=[~UNUSED3~]\nOutputSigType2128=Digital\nOutputCue2129=[~UNUSED3~]\nOutputSigType2129=Digital\nOutputCue2130=[~UNUSED3~]\nOutputSigType2130=Digital\nOutputCue2131=[~UNUSED3~]\nOutputSigType2131=Digital\nOutputCue2132=[~UNUSED3~]\nOutputSigType2132=Digital\nOutputCue2133=[~UNUSED3~]\nOutputSigType2133=Digital\nOutputCue2134=[~UNUSED3~]\nOutputSigType2134=Digital\nOutputCue2135=[~UNUSED3~]\nOutputSigType2135=Digital\nOutputCue2136=[~UNUSED3~]\nOutputSigType2136=Digital\nOutputCue2137=[~UNUSED3~]\nOutputSigType2137=Digital\nOutputCue2138=[~UNUSED3~]\nOutputSigType2138=Digital\nOutputCue2139=[~UNUSED3~]\nOutputSigType2139=Digital\nOutputCue2140=[~UNUSED3~]\nOutputSigType2140=Digital\nOutputCue2141=[~UNUSED3~]\nOutputSigType2141=Digital\nOutputCue2142=[~UNUSED3~]\nOutputSigType2142=Digital\nOutputCue2143=[~UNUSED3~]\nOutputSigType2143=Digital\nOutputCue2144=[~UNUSED3~]\nOutputSigType2144=Digital\nOutputCue2145=[~UNUSED3~]\nOutputSigType2145=Digital\nOutputCue2146=[~UNUSED3~]\nOutputSigType2146=Digital\nOutputCue2147=[~UNUSED3~]\nOutputSigType2147=Digital\nOutputCue2148=[~UNUSED3~]\nOutputSigType2148=Digital\nOutputCue2149=[~UNUSED3~]\nOutputSigType2149=Digital\nOutputCue2150=[~UNUSED3~]\nOutputSigType2150=Digital\nOutputCue2151=[~UNUSED3~]\nOutputSigType2151=Digital\nOutputCue2152=[~UNUSED3~]\nOutputSigType2152=Digital\nOutputCue2153=[~UNUSED3~]\nOutputSigType2153=Digital\nOutputCue2154=[~UNUSED3~]\nOutputSigType2154=Digital\nOutputCue2155=[~UNUSED3~]\nOutputSigType2155=Digital\nOutputCue2156=[~UNUSED3~]\nOutputSigType2156=Digital\nOutputCue2157=[~UNUSED3~]\nOutputSigType2157=Digital\nOutputCue2158=[~UNUSED3~]\nOutputSigType2158=Digital\nOutputCue2159=[~UNUSED3~]\nOutputSigType2159=Digital\nOutputCue2160=[~UNUSED3~]\nOutputSigType2160=Digital\nOutputCue2161=[~UNUSED3~]\nOutputSigType2161=Digital\nOutputCue2162=[~UNUSED3~]\nOutputSigType2162=Digital\nOutputCue2163=[~UNUSED3~]\nOutputSigType2163=Digital\nOutputCue2164=[~UNUSED3~]\nOutputSigType2164=Digital\nOutputCue2165=[~UNUSED3~]\nOutputSigType2165=Digital\nOutputCue2166=[~UNUSED3~]\nOutputSigType2166=Digital\nOutputCue2167=[~UNUSED3~]\nOutputSigType2167=Digital\nOutputCue2168=[~UNUSED3~]\nOutputSigType2168=Digital\nOutputCue2169=[~UNUSED3~]\nOutputSigType2169=Digital\nOutputCue2170=[~UNUSED3~]\nOutputSigType2170=Digital\nOutputCue2171=[~UNUSED3~]\nOutputSigType2171=Digital\nOutputCue2172=[~UNUSED3~]\nOutputSigType2172=Digital\nOutputCue2173=[~UNUSED3~]\nOutputSigType2173=Digital\nOutputCue2174=[~UNUSED3~]\nOutputSigType2174=Digital\nOutputCue2175=[~UNUSED3~]\nOutputSigType2175=Digital\nOutputCue2176=[~UNUSED3~]\nOutputSigType2176=Digital\nOutputCue2177=[~UNUSED3~]\nOutputSigType2177=Digital\nOutputCue2178=[~UNUSED3~]\nOutputSigType2178=Digital\nOutputCue2179=[~UNUSED3~]\nOutputSigType2179=Digital\nOutputCue2180=[~UNUSED3~]\nOutputSigType2180=Digital\nOutputCue2181=[~UNUSED3~]\nOutputSigType2181=Digital\nOutputCue2182=[~UNUSED3~]\nOutputSigType2182=Digital\nOutputCue2183=[~UNUSED3~]\nOutputSigType2183=Digital\nOutputCue2184=[~UNUSED3~]\nOutputSigType2184=Digital\nOutputCue2185=[~UNUSED3~]\nOutputSigType2185=Digital\nOutputCue2186=[~UNUSED3~]\nOutputSigType2186=Digital\nOutputCue2187=[~UNUSED3~]\nOutputSigType2187=Digital\nOutputCue2188=[~UNUSED3~]\nOutputSigType2188=Digital\nOutputCue2189=[~UNUSED3~]\nOutputSigType2189=Digital\nOutputCue2190=[~UNUSED3~]\nOutputSigType2190=Digital\nOutputCue2191=[~UNUSED3~]\nOutputSigType2191=Digital\nOutputCue2192=[~UNUSED3~]\nOutputSigType2192=Digital\nOutputCue2193=[~UNUSED3~]\nOutputSigType2193=Digital\nOutputCue2194=[~UNUSED3~]\nOutputSigType2194=Digital\nOutputCue2195=[~UNUSED3~]\nOutputSigType2195=Digital\nOutputCue2196=[~UNUSED3~]\nOutputSigType2196=Digital\nOutputCue2197=[~UNUSED3~]\nOutputSigType2197=Digital\nOutputCue2198=[~UNUSED3~]\nOutputSigType2198=Digital\nOutputCue2199=[~UNUSED3~]\nOutputSigType2199=Digital\nOutputCue2200=[~UNUSED3~]\nOutputSigType2200=Digital\nOutputCue2201=[~UNUSED3~]\nOutputSigType2201=Digital\nOutputCue2202=[~UNUSED3~]\nOutputSigType2202=Digital\nOutputCue2203=[~UNUSED3~]\nOutputSigType2203=Digital\nOutputCue2204=[~UNUSED3~]\nOutputSigType2204=Digital\nOutputCue2205=[~UNUSED3~]\nOutputSigType2205=Digital\nOutputCue2206=[~UNUSED3~]\nOutputSigType2206=Digital\nOutputCue2207=[~UNUSED3~]\nOutputSigType2207=Digital\nOutputCue2208=[~UNUSED3~]\nOutputSigType2208=Digital\nOutputCue2209=[~UNUSED3~]\nOutputSigType2209=Digital\nOutputCue2210=[~UNUSED3~]\nOutputSigType2210=Digital\nOutputCue2211=[~UNUSED3~]\nOutputSigType2211=Digital\nOutputCue2212=[~UNUSED3~]\nOutputSigType2212=Digital\nOutputCue2213=[~UNUSED3~]\nOutputSigType2213=Digital\nOutputCue2214=[~UNUSED3~]\nOutputSigType2214=Digital\nOutputCue2215=[~UNUSED3~]\nOutputSigType2215=Digital\nOutputCue2216=[~UNUSED3~]\nOutputSigType2216=Digital\nOutputCue2217=[~UNUSED3~]\nOutputSigType2217=Digital\nOutputCue2218=[~UNUSED3~]\nOutputSigType2218=Digital\nOutputCue2219=[~UNUSED3~]\nOutputSigType2219=Digital\nOutputCue2220=[~UNUSED3~]\nOutputSigType2220=Digital\nOutputCue2221=[~UNUSED3~]\nOutputSigType2221=Digital\nOutputCue2222=[~UNUSED3~]\nOutputSigType2222=Digital\nOutputCue2223=[~UNUSED3~]\nOutputSigType2223=Digital\nOutputCue2224=[~UNUSED3~]\nOutputSigType2224=Digital\nOutputCue2225=[~UNUSED3~]\nOutputSigType2225=Digital\nOutputCue2226=[~UNUSED3~]\nOutputSigType2226=Digital\nOutputCue2227=[~UNUSED3~]\nOutputSigType2227=Digital\nOutputCue2228=[~UNUSED3~]\nOutputSigType2228=Digital\nOutputCue2229=[~UNUSED3~]\nOutputSigType2229=Digital\nOutputCue2230=[~UNUSED3~]\nOutputSigType2230=Digital\nOutputCue2231=[~UNUSED3~]\nOutputSigType2231=Digital\nOutputCue2232=[~UNUSED3~]\nOutputSigType2232=Digital\nOutputCue2233=[~UNUSED3~]\nOutputSigType2233=Digital\nOutputCue2234=[~UNUSED3~]\nOutputSigType2234=Digital\nOutputCue2235=[~UNUSED3~]\nOutputSigType2235=Digital\nOutputCue2236=[~UNUSED3~]\nOutputSigType2236=Digital\nOutputCue2237=[~UNUSED3~]\nOutputSigType2237=Digital\nOutputCue2238=[~UNUSED3~]\nOutputSigType2238=Digital\nOutputCue2239=[~UNUSED3~]\nOutputSigType2239=Digital\nOutputCue2240=[~UNUSED3~]\nOutputSigType2240=Digital\nOutputCue2241=[~UNUSED3~]\nOutputSigType2241=Digital\nOutputCue2242=[~UNUSED3~]\nOutputSigType2242=Digital\nOutputCue2243=[~UNUSED3~]\nOutputSigType2243=Digital\nOutputCue2244=[~UNUSED3~]\nOutputSigType2244=Digital\nOutputCue2245=[~UNUSED3~]\nOutputSigType2245=Digital\nOutputCue2246=[~UNUSED3~]\nOutputSigType2246=Digital\nOutputCue2247=[~UNUSED3~]\nOutputSigType2247=Digital\nOutputCue2248=[~UNUSED3~]\nOutputSigType2248=Digital\nOutputCue2249=[~UNUSED3~]\nOutputSigType2249=Digital\nOutputCue2250=[~UNUSED3~]\nOutputSigType2250=Digital\nOutputCue2251=[~UNUSED3~]\nOutputSigType2251=Digital\nOutputCue2252=[~UNUSED3~]\nOutputSigType2252=Digital\nOutputCue2253=[~UNUSED3~]\nOutputSigType2253=Digital\nOutputCue2254=[~UNUSED3~]\nOutputSigType2254=Digital\nOutputCue2255=[~UNUSED3~]\nOutputSigType2255=Digital\nOutputCue2256=[~UNUSED3~]\nOutputSigType2256=Digital\nOutputCue2257=[~UNUSED3~]\nOutputSigType2257=Digital\nOutputCue2258=[~UNUSED3~]\nOutputSigType2258=Digital\nOutputCue2259=[~UNUSED3~]\nOutputSigType2259=Digital\nOutputCue2260=[~UNUSED3~]\nOutputSigType2260=Digital\nOutputCue2261=[~UNUSED3~]\nOutputSigType2261=Digital\nOutputCue2262=[~UNUSED3~]\nOutputSigType2262=Digital\nOutputCue2263=[~UNUSED3~]\nOutputSigType2263=Digital\nOutputCue2264=[~UNUSED3~]\nOutputSigType2264=Digital\nOutputCue2265=[~UNUSED3~]\nOutputSigType2265=Digital\nOutputCue2266=[~UNUSED3~]\nOutputSigType2266=Digital\nOutputCue2267=[~UNUSED3~]\nOutputSigType2267=Digital\nOutputCue2268=[~UNUSED3~]\nOutputSigType2268=Digital\nOutputCue2269=[~UNUSED2~]\nOutputSigType2269=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED3~]\nOutputSigType4026=Digital\nOutputCue4027=[~UNUSED3~]\nOutputSigType4027=Digital\nOutputCue4028=[~UNUSED3~]\nOutputSigType4028=Digital\nOutputCue4029=[~UNUSED3~]\nOutputSigType4029=Digital\nOutputCue4030=[~UNUSED3~]\nOutputSigType4030=Digital\nOutputCue4031=[~UNUSED3~]\nOutputSigType4031=Digital\nOutputCue4032=[~UNUSED3~]\nOutputSigType4032=Digital\nOutputCue4033=[~UNUSED3~]\nOutputSigType4033=Digital\nOutputCue4034=[~UNUSED3~]\nOutputSigType4034=Digital\nOutputCue4035=[~UNUSED3~]\nOutputSigType4035=Digital\nOutputCue4036=[~UNUSED3~]\nOutputSigType4036=Digital\nOutputCue4037=[~UNUSED3~]\nOutputSigType4037=Digital\nOutputCue4038=[~UNUSED3~]\nOutputSigType4038=Digital\nOutputCue4039=[~UNUSED3~]\nOutputSigType4039=Digital\nOutputCue4040=[~UNUSED3~]\nOutputSigType4040=Digital\nOutputCue4041=[~UNUSED3~]\nOutputSigType4041=Digital\nOutputCue4042=[~UNUSED3~]\nOutputSigType4042=Digital\nOutputCue4043=[~UNUSED3~]\nOutputSigType4043=Digital\nOutputCue4044=[~UNUSED3~]\nOutputSigType4044=Digital\nOutputCue4045=[~UNUSED3~]\nOutputSigType4045=Digital\nOutputCue4046=[~UNUSED3~]\nOutputSigType4046=Digital\nOutputCue4047=[~UNUSED3~]\nOutputSigType4047=Digital\nOutputCue4048=[~UNUSED3~]\nOutputSigType4048=Digital\nOutputCue4049=[~UNUSED3~]\nOutputSigType4049=Digital\nOutputCue4050=[~UNUSED3~]\nOutputSigType4050=Digital\nOutputCue4051=[~UNUSED3~]\nOutputSigType4051=Digital\nOutputCue4052=[~UNUSED3~]\nOutputSigType4052=Digital\nOutputCue4053=[~UNUSED3~]\nOutputSigType4053=Digital\nOutputCue4054=[~UNUSED3~]\nOutputSigType4054=Digital\nOutputCue4055=[~UNUSED3~]\nOutputSigType4055=Digital\nOutputCue4056=[~UNUSED3~]\nOutputSigType4056=Digital\nOutputCue4057=[~UNUSED3~]\nOutputSigType4057=Digital\nOutputCue4058=[~UNUSED3~]\nOutputSigType4058=Digital\nOutputCue4059=[~UNUSED3~]\nOutputSigType4059=Digital\nOutputCue4060=[~UNUSED3~]\nOutputSigType4060=Digital\nOutputCue4061=[~UNUSED3~]\nOutputSigType4061=Digital\nOutputCue4062=[~UNUSED3~]\nOutputSigType4062=Digital\nOutputCue4063=[~UNUSED3~]\nOutputSigType4063=Digital\nOutputCue4064=[~UNUSED3~]\nOutputSigType4064=Digital\nOutputCue4065=[~UNUSED3~]\nOutputSigType4065=Digital\nOutputCue4066=[~UNUSED3~]\nOutputSigType4066=Digital\nOutputCue4067=[~UNUSED3~]\nOutputSigType4067=Digital\nOutputCue4068=[~UNUSED3~]\nOutputSigType4068=Digital\nOutputCue4069=[~UNUSED3~]\nOutputSigType4069=Digital\nOutputCue4070=[~UNUSED3~]\nOutputSigType4070=Digital\nOutputCue4071=[~UNUSED3~]\nOutputSigType4071=Digital\nOutputCue4072=[~UNUSED3~]\nOutputSigType4072=Digital\nOutputCue4073=[~UNUSED3~]\nOutputSigType4073=Digital\nOutputCue4074=[~UNUSED3~]\nOutputSigType4074=Digital\nOutputCue4075=[~UNUSED3~]\nOutputSigType4075=Digital\nOutputCue4076=[~UNUSED3~]\nOutputSigType4076=Digital\nOutputCue4077=[~UNUSED3~]\nOutputSigType4077=Digital\nOutputCue4078=[~UNUSED3~]\nOutputSigType4078=Digital\nOutputCue4079=[~UNUSED3~]\nOutputSigType4079=Digital\nOutputCue4080=[~UNUSED3~]\nOutputSigType4080=Digital\nOutputCue4081=[~UNUSED3~]\nOutputSigType4081=Digital\nOutputCue4082=[~UNUSED3~]\nOutputSigType4082=Digital\nOutputCue4083=[~UNUSED3~]\nOutputSigType4083=Digital\nOutputCue4084=[~UNUSED3~]\nOutputSigType4084=Digital\nOutputCue4085=[~UNUSED3~]\nOutputSigType4085=Digital\nOutputCue4086=[~UNUSED3~]\nOutputSigType4086=Digital\nOutputCue4087=[~UNUSED3~]\nOutputSigType4087=Digital\nOutputCue4088=[~UNUSED3~]\nOutputSigType4088=Digital\nOutputCue4089=[~UNUSED3~]\nOutputSigType4089=Digital\nOutputCue4090=[~UNUSED3~]\nOutputSigType4090=Digital\nOutputCue4091=[~UNUSED3~]\nOutputSigType4091=Digital\nOutputCue4092=[~UNUSED3~]\nOutputSigType4092=Digital\nOutputCue4093=[~UNUSED3~]\nOutputSigType4093=Digital\nOutputCue4094=[~UNUSED3~]\nOutputSigType4094=Digital\nOutputCue4095=[~UNUSED3~]\nOutputSigType4095=Digital\nOutputCue4096=[~UNUSED3~]\nOutputSigType4096=Digital\nOutputCue4097=[~UNUSED3~]\nOutputSigType4097=Digital\nOutputCue4098=[~UNUSED3~]\nOutputSigType4098=Digital\nOutputCue4099=[~UNUSED3~]\nOutputSigType4099=Digital\nOutputCue4100=[~UNUSED3~]\nOutputSigType4100=Digital\nOutputCue4101=[~UNUSED3~]\nOutputSigType4101=Digital\nOutputCue4102=[~UNUSED3~]\nOutputSigType4102=Digital\nOutputCue4103=[~UNUSED3~]\nOutputSigType4103=Digital\nOutputCue4104=[~UNUSED3~]\nOutputSigType4104=Digital\nOutputCue4105=[~UNUSED3~]\nOutputSigType4105=Digital\nOutputCue4106=[~UNUSED3~]\nOutputSigType4106=Digital\nOutputCue4107=[~UNUSED3~]\nOutputSigType4107=Digital\nOutputCue4108=[~UNUSED3~]\nOutputSigType4108=Digital\nOutputCue4109=[~UNUSED3~]\nOutputSigType4109=Digital\nOutputCue4110=[~UNUSED3~]\nOutputSigType4110=Digital\nOutputCue4111=[~UNUSED3~]\nOutputSigType4111=Digital\nOutputCue4112=[~UNUSED3~]\nOutputSigType4112=Digital\nOutputCue4113=[~UNUSED3~]\nOutputSigType4113=Digital\nOutputCue4114=[~UNUSED3~]\nOutputSigType4114=Digital\nOutputCue4115=[~UNUSED3~]\nOutputSigType4115=Digital\nOutputCue4116=[~UNUSED3~]\nOutputSigType4116=Digital\nOutputCue4117=[~UNUSED3~]\nOutputSigType4117=Digital\nOutputCue4118=[~UNUSED3~]\nOutputSigType4118=Digital\nOutputCue4119=[~UNUSED3~]\nOutputSigType4119=Digital\nOutputCue4120=[~UNUSED3~]\nOutputSigType4120=Digital\nOutputCue4121=[~UNUSED3~]\nOutputSigType4121=Digital\nOutputCue4122=[~UNUSED3~]\nOutputSigType4122=Digital\nOutputCue4123=[~UNUSED3~]\nOutputSigType4123=Digital\nOutputCue4124=[~UNUSED3~]\nOutputSigType4124=Digital\nOutputCue4125=[~UNUSED3~]\nOutputSigType4125=Digital\nOutputCue4126=[~UNUSED3~]\nOutputSigType4126=Digital\nOutputCue4127=[~UNUSED3~]\nOutputSigType4127=Digital\nOutputCue4128=[~UNUSED3~]\nOutputSigType4128=Digital\nOutputCue4129=[~UNUSED3~]\nOutputSigType4129=Digital\nOutputCue4130=[~UNUSED3~]\nOutputSigType4130=Digital\nOutputCue4131=[~UNUSED3~]\nOutputSigType4131=Digital\nOutputCue4132=[~UNUSED3~]\nOutputSigType4132=Digital\nOutputCue4133=[~UNUSED3~]\nOutputSigType4133=Digital\nOutputCue4134=[~UNUSED3~]\nOutputSigType4134=Digital\nOutputCue4135=[~UNUSED3~]\nOutputSigType4135=Digital\nOutputCue4136=[~UNUSED3~]\nOutputSigType4136=Digital\nOutputCue4137=[~UNUSED3~]\nOutputSigType4137=Digital\nOutputCue4138=[~UNUSED3~]\nOutputSigType4138=Digital\nOutputCue4139=[~UNUSED3~]\nOutputSigType4139=Digital\nOutputCue4140=[~UNUSED3~]\nOutputSigType4140=Digital\nOutputCue4141=[~UNUSED3~]\nOutputSigType4141=Digital\nOutputCue4142=[~UNUSED3~]\nOutputSigType4142=Digital\nOutputCue4143=[~UNUSED3~]\nOutputSigType4143=Digital\nOutputCue4144=[~UNUSED3~]\nOutputSigType4144=Digital\nOutputCue4145=[~UNUSED3~]\nOutputSigType4145=Digital\nOutputCue4146=[~UNUSED3~]\nOutputSigType4146=Digital\nOutputCue4147=[~UNUSED3~]\nOutputSigType4147=Digital\nOutputCue4148=[~UNUSED3~]\nOutputSigType4148=Digital\nOutputCue4149=[~UNUSED3~]\nOutputSigType4149=Digital\nOutputCue4150=[~UNUSED3~]\nOutputSigType4150=Digital\nOutputCue4151=[~UNUSED3~]\nOutputSigType4151=Digital\nOutputCue4152=[~UNUSED3~]\nOutputSigType4152=Digital\nOutputCue4153=[~UNUSED3~]\nOutputSigType4153=Digital\nOutputCue4154=[~UNUSED3~]\nOutputSigType4154=Digital\nOutputCue4155=[~UNUSED3~]\nOutputSigType4155=Digital\nOutputCue4156=[~UNUSED3~]\nOutputSigType4156=Digital\nOutputCue4157=[~UNUSED3~]\nOutputSigType4157=Digital\nOutputCue4158=[~UNUSED3~]\nOutputSigType4158=Digital\nOutputCue4159=[~UNUSED3~]\nOutputSigType4159=Digital\nOutputCue4160=[~UNUSED3~]\nOutputSigType4160=Digital\nOutputCue4161=[~UNUSED3~]\nOutputSigType4161=Digital\nOutputCue4162=[~UNUSED3~]\nOutputSigType4162=Digital\nOutputCue4163=[~UNUSED3~]\nOutputSigType4163=Digital\nOutputCue4164=[~UNUSED3~]\nOutputSigType4164=Digital\nOutputCue4165=[~UNUSED3~]\nOutputSigType4165=Digital\nOutputCue4166=[~UNUSED3~]\nOutputSigType4166=Digital\nOutputCue4167=[~UNUSED3~]\nOutputSigType4167=Digital\nOutputCue4168=[~UNUSED3~]\nOutputSigType4168=Digital\nOutputCue4169=[~UNUSED3~]\nOutputSigType4169=Digital\nOutputCue4170=[~UNUSED3~]\nOutputSigType4170=Digital\nOutputCue4171=[~UNUSED3~]\nOutputSigType4171=Digital\nOutputCue4172=[~UNUSED3~]\nOutputSigType4172=Digital\nOutputCue4173=[~UNUSED3~]\nOutputSigType4173=Digital\nOutputCue4174=[~UNUSED3~]\nOutputSigType4174=Digital\nOutputCue4175=[~UNUSED3~]\nOutputSigType4175=Digital\nOutputCue4176=[~UNUSED3~]\nOutputSigType4176=Digital\nOutputCue4177=[~UNUSED3~]\nOutputSigType4177=Digital\nOutputCue4178=[~UNUSED3~]\nOutputSigType4178=Digital\nOutputCue4179=[~UNUSED3~]\nOutputSigType4179=Digital\nOutputCue4180=[~UNUSED3~]\nOutputSigType4180=Digital\nOutputCue4181=[~UNUSED3~]\nOutputSigType4181=Digital\nOutputCue4182=[~UNUSED3~]\nOutputSigType4182=Digital\nOutputCue4183=[~UNUSED3~]\nOutputSigType4183=Digital\nOutputCue4184=[~UNUSED3~]\nOutputSigType4184=Digital\nOutputCue4185=[~UNUSED3~]\nOutputSigType4185=Digital\nOutputCue4186=[~UNUSED3~]\nOutputSigType4186=Digital\nOutputCue4187=[~UNUSED3~]\nOutputSigType4187=Digital\nOutputCue4188=[~UNUSED3~]\nOutputSigType4188=Digital\nOutputCue4189=[~UNUSED3~]\nOutputSigType4189=Digital\nOutputCue4190=[~UNUSED3~]\nOutputSigType4190=Digital\nOutputCue4191=[~UNUSED3~]\nOutputSigType4191=Digital\nOutputCue4192=[~UNUSED3~]\nOutputSigType4192=Digital\nOutputCue4193=[~UNUSED3~]\nOutputSigType4193=Digital\nOutputCue4194=[~UNUSED3~]\nOutputSigType4194=Digital\nOutputCue4195=[~UNUSED3~]\nOutputSigType4195=Digital\nOutputCue4196=[~UNUSED3~]\nOutputSigType4196=Digital\nOutputCue4197=[~UNUSED3~]\nOutputSigType4197=Digital\nOutputCue4198=[~UNUSED3~]\nOutputSigType4198=Digital\nOutputCue4199=[~UNUSED3~]\nOutputSigType4199=Digital\nOutputCue4200=[~UNUSED3~]\nOutputSigType4200=Digital\nOutputCue4201=[~UNUSED3~]\nOutputSigType4201=Digital\nOutputCue4202=[~UNUSED3~]\nOutputSigType4202=Digital\nOutputCue4203=[~UNUSED3~]\nOutputSigType4203=Digital\nOutputCue4204=[~UNUSED3~]\nOutputSigType4204=Digital\nOutputCue4205=[~UNUSED3~]\nOutputSigType4205=Digital\nOutputCue4206=[~UNUSED3~]\nOutputSigType4206=Digital\nOutputCue4207=[~UNUSED3~]\nOutputSigType4207=Digital\nOutputCue4208=[~UNUSED3~]\nOutputSigType4208=Digital\nOutputCue4209=[~UNUSED3~]\nOutputSigType4209=Digital\nOutputCue4210=[~UNUSED3~]\nOutputSigType4210=Digital\nOutputCue4211=[~UNUSED3~]\nOutputSigType4211=Digital\nOutputCue4212=[~UNUSED3~]\nOutputSigType4212=Digital\nOutputCue4213=[~UNUSED3~]\nOutputSigType4213=Digital\nOutputCue4214=[~UNUSED3~]\nOutputSigType4214=Digital\nOutputCue4215=[~UNUSED3~]\nOutputSigType4215=Digital\nOutputCue4216=[~UNUSED3~]\nOutputSigType4216=Digital\nOutputCue4217=[~UNUSED3~]\nOutputSigType4217=Digital\nOutputCue4218=[~UNUSED3~]\nOutputSigType4218=Digital\nOutputCue4219=[~UNUSED3~]\nOutputSigType4219=Digital\nOutputCue4220=[~UNUSED3~]\nOutputSigType4220=Digital\nOutputCue4221=[~UNUSED3~]\nOutputSigType4221=Digital\nOutputCue4222=[~UNUSED3~]\nOutputSigType4222=Digital\nOutputCue4223=[~UNUSED3~]\nOutputSigType4223=Digital\nOutputCue4224=[~UNUSED3~]\nOutputSigType4224=Digital\nOutputCue4225=[~UNUSED3~]\nOutputSigType4225=Digital\nOutputCue4226=[~UNUSED3~]\nOutputSigType4226=Digital\nOutputCue4227=[~UNUSED3~]\nOutputSigType4227=Digital\nOutputCue4228=[~UNUSED3~]\nOutputSigType4228=Digital\nOutputCue4229=[~UNUSED3~]\nOutputSigType4229=Digital\nOutputCue4230=[~UNUSED3~]\nOutputSigType4230=Digital\nOutputCue4231=[~UNUSED3~]\nOutputSigType4231=Digital\nOutputCue4232=[~UNUSED3~]\nOutputSigType4232=Digital\nOutputCue4233=[~UNUSED3~]\nOutputSigType4233=Digital\nOutputCue4234=[~UNUSED3~]\nOutputSigType4234=Digital\nOutputCue4235=[~UNUSED3~]\nOutputSigType4235=Digital\nOutputCue4236=[~UNUSED3~]\nOutputSigType4236=Digital\nOutputCue4237=[~UNUSED3~]\nOutputSigType4237=Digital\nOutputCue4238=[~UNUSED3~]\nOutputSigType4238=Digital\nOutputCue4239=[~UNUSED3~]\nOutputSigType4239=Digital\nOutputCue4240=[~UNUSED3~]\nOutputSigType4240=Digital\nOutputCue4241=[~UNUSED3~]\nOutputSigType4241=Digital\nOutputCue4242=[~UNUSED3~]\nOutputSigType4242=Digital\nOutputCue4243=[~UNUSED3~]\nOutputSigType4243=Digital\nOutputCue4244=[~UNUSED3~]\nOutputSigType4244=Digital\nOutputCue4245=[~UNUSED3~]\nOutputSigType4245=Digital\nOutputCue4246=[~UNUSED3~]\nOutputSigType4246=Digital\nOutputCue4247=[~UNUSED3~]\nOutputSigType4247=Digital\nOutputCue4248=[~UNUSED3~]\nOutputSigType4248=Digital\nOutputCue4249=[~UNUSED3~]\nOutputSigType4249=Digital\nOutputCue4250=[~UNUSED3~]\nOutputSigType4250=Digital\nOutputCue4251=[~UNUSED3~]\nOutputSigType4251=Digital\nOutputCue4252=[~UNUSED3~]\nOutputSigType4252=Digital\nOutputCue4253=[~UNUSED3~]\nOutputSigType4253=Digital\nOutputCue4254=[~UNUSED3~]\nOutputSigType4254=Digital\nOutputCue4255=[~UNUSED3~]\nOutputSigType4255=Digital\nOutputCue4256=[~UNUSED3~]\nOutputSigType4256=Digital\nOutputCue4257=[~UNUSED3~]\nOutputSigType4257=Digital\nOutputCue4258=[~UNUSED3~]\nOutputSigType4258=Digital\nOutputCue4259=[~UNUSED3~]\nOutputSigType4259=Digital\nOutputCue4260=[~UNUSED3~]\nOutputSigType4260=Digital\nOutputCue4261=[~UNUSED3~]\nOutputSigType4261=Digital\nOutputCue4262=[~UNUSED3~]\nOutputSigType4262=Digital\nOutputCue4263=[~UNUSED3~]\nOutputSigType4263=Digital\nOutputCue4264=[~UNUSED3~]\nOutputSigType4264=Digital\nOutputCue4265=[~UNUSED3~]\nOutputSigType4265=Digital\nOutputCue4266=[~UNUSED3~]\nOutputSigType4266=Digital\nOutputCue4267=[~UNUSED3~]\nOutputSigType4267=Digital\nOutputCue4268=[~UNUSED3~]\nOutputSigType4268=Digital\nOutputCue4269=[~UNUSED3~]\nOutputSigType4269=Digital\nOutputCue4270=[~UNUSED3~]\nOutputSigType4270=Digital\nOutputCue4271=[~UNUSED2~]\nOutputSigType4271=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=Set Item 11 Icon Analog\nInputList2SigType22=Analog\nInputList2Cue23=Set Item 12 Icon Analog\nInputList2SigType23=Analog\nInputList2Cue24=Set Item 13 Icon Analog\nInputList2SigType24=Analog\nInputList2Cue25=Set Item 14 Icon Analog\nInputList2SigType25=Analog\nInputList2Cue26=Set Item 15 Icon Analog\nInputList2SigType26=Analog\nInputList2Cue27=Set Item 16 Icon Analog\nInputList2SigType27=Analog\nInputList2Cue28=Set Item 17 Icon Analog\nInputList2SigType28=Analog\nInputList2Cue29=Set Item 18 Icon Analog\nInputList2SigType29=Analog\nInputList2Cue30=Set Item 19 Icon Analog\nInputList2SigType30=Analog\nInputList2Cue31=Set Item 20 Icon Analog\nInputList2SigType31=Analog\nInputList2Cue32=Set Item 21 Icon Analog\nInputList2SigType32=Analog\nInputList2Cue33=Set Item 22 Icon Analog\nInputList2SigType33=Analog\nInputList2Cue34=Set Item 23 Icon Analog\nInputList2SigType34=Analog\nInputList2Cue35=Set Item 24 Icon Analog\nInputList2SigType35=Analog\nInputList2Cue36=Set Item 25 Icon Analog\nInputList2SigType36=Analog\nInputList2Cue37=Set Item 26 Icon Analog\nInputList2SigType37=Analog\nInputList2Cue38=Set Item 27 Icon Analog\nInputList2SigType38=Analog\nInputList2Cue39=Set Item 28 Icon Analog\nInputList2SigType39=Analog\nInputList2Cue40=Set Item 29 Icon Analog\nInputList2SigType40=Analog\nInputList2Cue41=Set Item 30 Icon Analog\nInputList2SigType41=Analog\nInputList2Cue42=Set Item 31 Icon Analog\nInputList2SigType42=Analog\nInputList2Cue43=Set Item 32 Icon Analog\nInputList2SigType43=Analog\nInputList2Cue44=Set Item 33 Icon Analog\nInputList2SigType44=Analog\nInputList2Cue45=Set Item 34 Icon Analog\nInputList2SigType45=Analog\nInputList2Cue46=Set Item 35 Icon Analog\nInputList2SigType46=Analog\nInputList2Cue47=Set Item 36 Icon Analog\nInputList2SigType47=Analog\nInputList2Cue48=Set Item 37 Icon Analog\nInputList2SigType48=Analog\nInputList2Cue49=Set Item 38 Icon Analog\nInputList2SigType49=Analog\nInputList2Cue50=Set Item 39 Icon Analog\nInputList2SigType50=Analog\nInputList2Cue51=Set Item 40 Icon Analog\nInputList2SigType51=Analog\nInputList2Cue52=Set Item 41 Icon Analog\nInputList2SigType52=Analog\nInputList2Cue53=Set Item 42 Icon Analog\nInputList2SigType53=Analog\nInputList2Cue54=Set Item 43 Icon Analog\nInputList2SigType54=Analog\nInputList2Cue55=Set Item 44 Icon Analog\nInputList2SigType55=Analog\nInputList2Cue56=Set Item 45 Icon Analog\nInputList2SigType56=Analog\nInputList2Cue57=Set Item 46 Icon Analog\nInputList2SigType57=Analog\nInputList2Cue58=Set Item 47 Icon Analog\nInputList2SigType58=Analog\nInputList2Cue59=Set Item 48 Icon Analog\nInputList2SigType59=Analog\nInputList2Cue60=Set Item 49 Icon Analog\nInputList2SigType60=Analog\nInputList2Cue61=Set Item 50 Icon Analog\nInputList2SigType61=Analog\nInputList2Cue62=Set Item 51 Icon Analog\nInputList2SigType62=Analog\nInputList2Cue63=Set Item 52 Icon Analog\nInputList2SigType63=Analog\nInputList2Cue64=Set Item 53 Icon Analog\nInputList2SigType64=Analog\nInputList2Cue65=Set Item 54 Icon Analog\nInputList2SigType65=Analog\nInputList2Cue66=Set Item 55 Icon Analog\nInputList2SigType66=Analog\nInputList2Cue67=Set Item 56 Icon Analog\nInputList2SigType67=Analog\nInputList2Cue68=Set Item 57 Icon Analog\nInputList2SigType68=Analog\nInputList2Cue69=Set Item 58 Icon Analog\nInputList2SigType69=Analog\nInputList2Cue70=Set Item 59 Icon Analog\nInputList2SigType70=Analog\nInputList2Cue71=Set Item 60 Icon Analog\nInputList2SigType71=Analog\nInputList2Cue72=Set Item 61 Icon Analog\nInputList2SigType72=Analog\nInputList2Cue73=Set Item 62 Icon Analog\nInputList2SigType73=Analog\nInputList2Cue74=Set Item 63 Icon Analog\nInputList2SigType74=Analog\nInputList2Cue75=Set Item 64 Icon Analog\nInputList2SigType75=Analog\nInputList2Cue76=Set Item 65 Icon Analog\nInputList2SigType76=Analog\nInputList2Cue77=Set Item 66 Icon Analog\nInputList2SigType77=Analog\nInputList2Cue78=Set Item 67 Icon Analog\nInputList2SigType78=Analog\nInputList2Cue79=Set Item 68 Icon Analog\nInputList2SigType79=Analog\nInputList2Cue80=Set Item 69 Icon Analog\nInputList2SigType80=Analog\nInputList2Cue81=Set Item 70 Icon Analog\nInputList2SigType81=Analog\nInputList2Cue82=Set Item 71 Icon Analog\nInputList2SigType82=Analog\nInputList2Cue83=Set Item 72 Icon Analog\nInputList2SigType83=Analog\nInputList2Cue84=Set Item 73 Icon Analog\nInputList2SigType84=Analog\nInputList2Cue85=Set Item 74 Icon Analog\nInputList2SigType85=Analog\nInputList2Cue86=Set Item 75 Icon Analog\nInputList2SigType86=Analog\nInputList2Cue87=Set Item 76 Icon Analog\nInputList2SigType87=Analog\nInputList2Cue88=Set Item 77 Icon Analog\nInputList2SigType88=Analog\nInputList2Cue89=Set Item 78 Icon Analog\nInputList2SigType89=Analog\nInputList2Cue90=Set Item 79 Icon Analog\nInputList2SigType90=Analog\nInputList2Cue91=Set Item 80 Icon Analog\nInputList2SigType91=Analog\nInputList2Cue92=Set Item 81 Icon Analog\nInputList2SigType92=Analog\nInputList2Cue93=Set Item 82 Icon Analog\nInputList2SigType93=Analog\nInputList2Cue94=Set Item 83 Icon Analog\nInputList2SigType94=Analog\nInputList2Cue95=Set Item 84 Icon Analog\nInputList2SigType95=Analog\nInputList2Cue96=Set Item 85 Icon Analog\nInputList2SigType96=Analog\nInputList2Cue97=Set Item 86 Icon Analog\nInputList2SigType97=Analog\nInputList2Cue98=Set Item 87 Icon Analog\nInputList2SigType98=Analog\nInputList2Cue99=Set Item 88 Icon Analog\nInputList2SigType99=Analog\nInputList2Cue100=Set Item 89 Icon Analog\nInputList2SigType100=Analog\nInputList2Cue101=Set Item 90 Icon Analog\nInputList2SigType101=Analog\nInputList2Cue102=Set Item 91 Icon Analog\nInputList2SigType102=Analog\nInputList2Cue103=Set Item 92 Icon Analog\nInputList2SigType103=Analog\nInputList2Cue104=Set Item 93 Icon Analog\nInputList2SigType104=Analog\nInputList2Cue105=Set Item 94 Icon Analog\nInputList2SigType105=Analog\nInputList2Cue106=Set Item 95 Icon Analog\nInputList2SigType106=Analog\nInputList2Cue107=Set Item 96 Icon Analog\nInputList2SigType107=Analog\nInputList2Cue108=Set Item 97 Icon Analog\nInputList2SigType108=Analog\nInputList2Cue109=Set Item 98 Icon Analog\nInputList2SigType109=Analog\nInputList2Cue110=Set Item 99 Icon Analog\nInputList2SigType110=Analog\nInputList2Cue111=Set Item 100 Icon Analog\nInputList2SigType111=Analog\nInputList2Cue112=Set Item 101 Icon Analog\nInputList2SigType112=Analog\nInputList2Cue113=Set Item 102 Icon Analog\nInputList2SigType113=Analog\nInputList2Cue114=Set Item 103 Icon Analog\nInputList2SigType114=Analog\nInputList2Cue115=Set Item 104 Icon Analog\nInputList2SigType115=Analog\nInputList2Cue116=Set Item 105 Icon Analog\nInputList2SigType116=Analog\nInputList2Cue117=Set Item 106 Icon Analog\nInputList2SigType117=Analog\nInputList2Cue118=Set Item 107 Icon Analog\nInputList2SigType118=Analog\nInputList2Cue119=Set Item 108 Icon Analog\nInputList2SigType119=Analog\nInputList2Cue120=Set Item 109 Icon Analog\nInputList2SigType120=Analog\nInputList2Cue121=Set Item 110 Icon Analog\nInputList2SigType121=Analog\nInputList2Cue122=Set Item 111 Icon Analog\nInputList2SigType122=Analog\nInputList2Cue123=Set Item 112 Icon Analog\nInputList2SigType123=Analog\nInputList2Cue124=Set Item 113 Icon Analog\nInputList2SigType124=Analog\nInputList2Cue125=Set Item 114 Icon Analog\nInputList2SigType125=Analog\nInputList2Cue126=Set Item 115 Icon Analog\nInputList2SigType126=Analog\nInputList2Cue127=Set Item 116 Icon Analog\nInputList2SigType127=Analog\nInputList2Cue128=Set Item 117 Icon Analog\nInputList2SigType128=Analog\nInputList2Cue129=Set Item 118 Icon Analog\nInputList2SigType129=Analog\nInputList2Cue130=Set Item 119 Icon Analog\nInputList2SigType130=Analog\nInputList2Cue131=Set Item 120 Icon Analog\nInputList2SigType131=Analog\nInputList2Cue132=Set Item 121 Icon Analog\nInputList2SigType132=Analog\nInputList2Cue133=Set Item 122 Icon Analog\nInputList2SigType133=Analog\nInputList2Cue134=Set Item 123 Icon Analog\nInputList2SigType134=Analog\nInputList2Cue135=Set Item 124 Icon Analog\nInputList2SigType135=Analog\nInputList2Cue136=Set Item 125 Icon Analog\nInputList2SigType136=Analog\nInputList2Cue137=Set Item 126 Icon Analog\nInputList2SigType137=Analog\nInputList2Cue138=Set Item 127 Icon Analog\nInputList2SigType138=Analog\nInputList2Cue139=Set Item 128 Icon Analog\nInputList2SigType139=Analog\nInputList2Cue140=Set Item 129 Icon Analog\nInputList2SigType140=Analog\nInputList2Cue141=Set Item 130 Icon Analog\nInputList2SigType141=Analog\nInputList2Cue142=Set Item 131 Icon Analog\nInputList2SigType142=Analog\nInputList2Cue143=Set Item 132 Icon Analog\nInputList2SigType143=Analog\nInputList2Cue144=Set Item 133 Icon Analog\nInputList2SigType144=Analog\nInputList2Cue145=Set Item 134 Icon Analog\nInputList2SigType145=Analog\nInputList2Cue146=Set Item 135 Icon Analog\nInputList2SigType146=Analog\nInputList2Cue147=Set Item 136 Icon Analog\nInputList2SigType147=Analog\nInputList2Cue148=Set Item 137 Icon Analog\nInputList2SigType148=Analog\nInputList2Cue149=Set Item 138 Icon Analog\nInputList2SigType149=Analog\nInputList2Cue150=Set Item 139 Icon Analog\nInputList2SigType150=Analog\nInputList2Cue151=Set Item 140 Icon Analog\nInputList2SigType151=Analog\nInputList2Cue152=Set Item 141 Icon Analog\nInputList2SigType152=Analog\nInputList2Cue153=Set Item 142 Icon Analog\nInputList2SigType153=Analog\nInputList2Cue154=Set Item 143 Icon Analog\nInputList2SigType154=Analog\nInputList2Cue155=Set Item 144 Icon Analog\nInputList2SigType155=Analog\nInputList2Cue156=Set Item 145 Icon Analog\nInputList2SigType156=Analog\nInputList2Cue157=Set Item 146 Icon Analog\nInputList2SigType157=Analog\nInputList2Cue158=Set Item 147 Icon Analog\nInputList2SigType158=Analog\nInputList2Cue159=Set Item 148 Icon Analog\nInputList2SigType159=Analog\nInputList2Cue160=Set Item 149 Icon Analog\nInputList2SigType160=Analog\nInputList2Cue161=Set Item 150 Icon Analog\nInputList2SigType161=Analog\nInputList2Cue162=Set Item 151 Icon Analog\nInputList2SigType162=Analog\nInputList2Cue163=Set Item 152 Icon Analog\nInputList2SigType163=Analog\nInputList2Cue164=Set Item 153 Icon Analog\nInputList2SigType164=Analog\nInputList2Cue165=Set Item 154 Icon Analog\nInputList2SigType165=Analog\nInputList2Cue166=Set Item 155 Icon Analog\nInputList2SigType166=Analog\nInputList2Cue167=Set Item 156 Icon Analog\nInputList2SigType167=Analog\nInputList2Cue168=Set Item 157 Icon Analog\nInputList2SigType168=Analog\nInputList2Cue169=Set Item 158 Icon Analog\nInputList2SigType169=Analog\nInputList2Cue170=Set Item 159 Icon Analog\nInputList2SigType170=Analog\nInputList2Cue171=Set Item 160 Icon Analog\nInputList2SigType171=Analog\nInputList2Cue172=Set Item 161 Icon Analog\nInputList2SigType172=Analog\nInputList2Cue173=Set Item 162 Icon Analog\nInputList2SigType173=Analog\nInputList2Cue174=Set Item 163 Icon Analog\nInputList2SigType174=Analog\nInputList2Cue175=Set Item 164 Icon Analog\nInputList2SigType175=Analog\nInputList2Cue176=Set Item 165 Icon Analog\nInputList2SigType176=Analog\nInputList2Cue177=Set Item 166 Icon Analog\nInputList2SigType177=Analog\nInputList2Cue178=Set Item 167 Icon Analog\nInputList2SigType178=Analog\nInputList2Cue179=Set Item 168 Icon Analog\nInputList2SigType179=Analog\nInputList2Cue180=Set Item 169 Icon Analog\nInputList2SigType180=Analog\nInputList2Cue181=Set Item 170 Icon Analog\nInputList2SigType181=Analog\nInputList2Cue182=Set Item 171 Icon Analog\nInputList2SigType182=Analog\nInputList2Cue183=Set Item 172 Icon Analog\nInputList2SigType183=Analog\nInputList2Cue184=Set Item 173 Icon Analog\nInputList2SigType184=Analog\nInputList2Cue185=Set Item 174 Icon Analog\nInputList2SigType185=Analog\nInputList2Cue186=Set Item 175 Icon Analog\nInputList2SigType186=Analog\nInputList2Cue187=Set Item 176 Icon Analog\nInputList2SigType187=Analog\nInputList2Cue188=Set Item 177 Icon Analog\nInputList2SigType188=Analog\nInputList2Cue189=Set Item 178 Icon Analog\nInputList2SigType189=Analog\nInputList2Cue190=Set Item 179 Icon Analog\nInputList2SigType190=Analog\nInputList2Cue191=Set Item 180 Icon Analog\nInputList2SigType191=Analog\nInputList2Cue192=Set Item 181 Icon Analog\nInputList2SigType192=Analog\nInputList2Cue193=Set Item 182 Icon Analog\nInputList2SigType193=Analog\nInputList2Cue194=Set Item 183 Icon Analog\nInputList2SigType194=Analog\nInputList2Cue195=Set Item 184 Icon Analog\nInputList2SigType195=Analog\nInputList2Cue196=Set Item 185 Icon Analog\nInputList2SigType196=Analog\nInputList2Cue197=Set Item 186 Icon Analog\nInputList2SigType197=Analog\nInputList2Cue198=Set Item 187 Icon Analog\nInputList2SigType198=Analog\nInputList2Cue199=Set Item 188 Icon Analog\nInputList2SigType199=Analog\nInputList2Cue200=Set Item 189 Icon Analog\nInputList2SigType200=Analog\nInputList2Cue201=Set Item 190 Icon Analog\nInputList2SigType201=Analog\nInputList2Cue202=Set Item 191 Icon Analog\nInputList2SigType202=Analog\nInputList2Cue203=Set Item 192 Icon Analog\nInputList2SigType203=Analog\nInputList2Cue204=Set Item 193 Icon Analog\nInputList2SigType204=Analog\nInputList2Cue205=Set Item 194 Icon Analog\nInputList2SigType205=Analog\nInputList2Cue206=Set Item 195 Icon Analog\nInputList2SigType206=Analog\nInputList2Cue207=Set Item 196 Icon Analog\nInputList2SigType207=Analog\nInputList2Cue208=Set Item 197 Icon Analog\nInputList2SigType208=Analog\nInputList2Cue209=Set Item 198 Icon Analog\nInputList2SigType209=Analog\nInputList2Cue210=Set Item 199 Icon Analog\nInputList2SigType210=Analog\nInputList2Cue211=Set Item 200 Icon Analog\nInputList2SigType211=Analog\nInputList2Cue212=Set Item 201 Icon Analog\nInputList2SigType212=Analog\nInputList2Cue213=Set Item 202 Icon Analog\nInputList2SigType213=Analog\nInputList2Cue214=Set Item 203 Icon Analog\nInputList2SigType214=Analog\nInputList2Cue215=Set Item 204 Icon Analog\nInputList2SigType215=Analog\nInputList2Cue216=Set Item 205 Icon Analog\nInputList2SigType216=Analog\nInputList2Cue217=Set Item 206 Icon Analog\nInputList2SigType217=Analog\nInputList2Cue218=Set Item 207 Icon Analog\nInputList2SigType218=Analog\nInputList2Cue219=Set Item 208 Icon Analog\nInputList2SigType219=Analog\nInputList2Cue220=Set Item 209 Icon Analog\nInputList2SigType220=Analog\nInputList2Cue221=Set Item 210 Icon Analog\nInputList2SigType221=Analog\nInputList2Cue222=Set Item 211 Icon Analog\nInputList2SigType222=Analog\nInputList2Cue223=Set Item 212 Icon Analog\nInputList2SigType223=Analog\nInputList2Cue224=Set Item 213 Icon Analog\nInputList2SigType224=Analog\nInputList2Cue225=Set Item 214 Icon Analog\nInputList2SigType225=Analog\nInputList2Cue226=Set Item 215 Icon Analog\nInputList2SigType226=Analog\nInputList2Cue227=Set Item 216 Icon Analog\nInputList2SigType227=Analog\nInputList2Cue228=Set Item 217 Icon Analog\nInputList2SigType228=Analog\nInputList2Cue229=Set Item 218 Icon Analog\nInputList2SigType229=Analog\nInputList2Cue230=Set Item 219 Icon Analog\nInputList2SigType230=Analog\nInputList2Cue231=Set Item 220 Icon Analog\nInputList2SigType231=Analog\nInputList2Cue232=Set Item 221 Icon Analog\nInputList2SigType232=Analog\nInputList2Cue233=Set Item 222 Icon Analog\nInputList2SigType233=Analog\nInputList2Cue234=Set Item 223 Icon Analog\nInputList2SigType234=Analog\nInputList2Cue235=Set Item 224 Icon Analog\nInputList2SigType235=Analog\nInputList2Cue236=Set Item 225 Icon Analog\nInputList2SigType236=Analog\nInputList2Cue237=Set Item 226 Icon Analog\nInputList2SigType237=Analog\nInputList2Cue238=Set Item 227 Icon Analog\nInputList2SigType238=Analog\nInputList2Cue239=Set Item 228 Icon Analog\nInputList2SigType239=Analog\nInputList2Cue240=Set Item 229 Icon Analog\nInputList2SigType240=Analog\nInputList2Cue241=Set Item 230 Icon Analog\nInputList2SigType241=Analog\nInputList2Cue242=Set Item 231 Icon Analog\nInputList2SigType242=Analog\nInputList2Cue243=Set Item 232 Icon Analog\nInputList2SigType243=Analog\nInputList2Cue244=Set Item 233 Icon Analog\nInputList2SigType244=Analog\nInputList2Cue245=Set Item 234 Icon Analog\nInputList2SigType245=Analog\nInputList2Cue246=Set Item 235 Icon Analog\nInputList2SigType246=Analog\nInputList2Cue247=Set Item 236 Icon Analog\nInputList2SigType247=Analog\nInputList2Cue248=Set Item 237 Icon Analog\nInputList2SigType248=Analog\nInputList2Cue249=Set Item 238 Icon Analog\nInputList2SigType249=Analog\nInputList2Cue250=Set Item 239 Icon Analog\nInputList2SigType250=Analog\nInputList2Cue251=Set Item 240 Icon Analog\nInputList2SigType251=Analog\nInputList2Cue252=Set Item 241 Icon Analog\nInputList2SigType252=Analog\nInputList2Cue253=Set Item 242 Icon Analog\nInputList2SigType253=Analog\nInputList2Cue254=Set Item 243 Icon Analog\nInputList2SigType254=Analog\nInputList2Cue255=Set Item 244 Icon Analog\nInputList2SigType255=Analog\nInputList2Cue256=Set Item 245 Icon Analog\nInputList2SigType256=Analog\nInputList2Cue257=Set Item 246 Icon Analog\nInputList2SigType257=Analog\nInputList2Cue258=Set Item 247 Icon Analog\nInputList2SigType258=Analog\nInputList2Cue259=Set Item 248 Icon Analog\nInputList2SigType259=Analog\nInputList2Cue260=Set Item 249 Icon Analog\nInputList2SigType260=Analog\nInputList2Cue261=Set Item 250 Icon Analog\nInputList2SigType261=Analog\nInputList2Cue262=Set Item 251 Icon Analog\nInputList2SigType262=Analog\nInputList2Cue263=Set Item 252 Icon Analog\nInputList2SigType263=Analog\nInputList2Cue264=Set Item 253 Icon Analog\nInputList2SigType264=Analog\nInputList2Cue265=Set Item 254 Icon Analog\nInputList2SigType265=Analog\nInputList2Cue266=Set Item 255 Icon Analog\nInputList2SigType266=Analog\nInputList2Cue267=[~UNUSED2~]\nInputList2SigType267=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED3~]\nOutputList2SigType22=Analog\nOutputList2Cue23=[~UNUSED3~]\nOutputList2SigType23=Analog\nOutputList2Cue24=[~UNUSED3~]\nOutputList2SigType24=Analog\nOutputList2Cue25=[~UNUSED3~]\nOutputList2SigType25=Analog\nOutputList2Cue26=[~UNUSED3~]\nOutputList2SigType26=Analog\nOutputList2Cue27=[~UNUSED3~]\nOutputList2SigType27=Analog\nOutputList2Cue28=[~UNUSED3~]\nOutputList2SigType28=Analog\nOutputList2Cue29=[~UNUSED3~]\nOutputList2SigType29=Analog\nOutputList2Cue30=[~UNUSED3~]\nOutputList2SigType30=Analog\nOutputList2Cue31=[~UNUSED3~]\nOutputList2SigType31=Analog\nOutputList2Cue32=[~UNUSED3~]\nOutputList2SigType32=Analog\nOutputList2Cue33=[~UNUSED3~]\nOutputList2SigType33=Analog\nOutputList2Cue34=[~UNUSED3~]\nOutputList2SigType34=Analog\nOutputList2Cue35=[~UNUSED3~]\nOutputList2SigType35=Analog\nOutputList2Cue36=[~UNUSED3~]\nOutputList2SigType36=Analog\nOutputList2Cue37=[~UNUSED3~]\nOutputList2SigType37=Analog\nOutputList2Cue38=[~UNUSED3~]\nOutputList2SigType38=Analog\nOutputList2Cue39=[~UNUSED3~]\nOutputList2SigType39=Analog\nOutputList2Cue40=[~UNUSED3~]\nOutputList2SigType40=Analog\nOutputList2Cue41=[~UNUSED3~]\nOutputList2SigType41=Analog\nOutputList2Cue42=[~UNUSED3~]\nOutputList2SigType42=Analog\nOutputList2Cue43=[~UNUSED3~]\nOutputList2SigType43=Analog\nOutputList2Cue44=[~UNUSED3~]\nOutputList2SigType44=Analog\nOutputList2Cue45=[~UNUSED3~]\nOutputList2SigType45=Analog\nOutputList2Cue46=[~UNUSED3~]\nOutputList2SigType46=Analog\nOutputList2Cue47=[~UNUSED3~]\nOutputList2SigType47=Analog\nOutputList2Cue48=[~UNUSED3~]\nOutputList2SigType48=Analog\nOutputList2Cue49=[~UNUSED3~]\nOutputList2SigType49=Analog\nOutputList2Cue50=[~UNUSED3~]\nOutputList2SigType50=Analog\nOutputList2Cue51=[~UNUSED3~]\nOutputList2SigType51=Analog\nOutputList2Cue52=[~UNUSED3~]\nOutputList2SigType52=Analog\nOutputList2Cue53=[~UNUSED3~]\nOutputList2SigType53=Analog\nOutputList2Cue54=[~UNUSED3~]\nOutputList2SigType54=Analog\nOutputList2Cue55=[~UNUSED3~]\nOutputList2SigType55=Analog\nOutputList2Cue56=[~UNUSED3~]\nOutputList2SigType56=Analog\nOutputList2Cue57=[~UNUSED3~]\nOutputList2SigType57=Analog\nOutputList2Cue58=[~UNUSED3~]\nOutputList2SigType58=Analog\nOutputList2Cue59=[~UNUSED3~]\nOutputList2SigType59=Analog\nOutputList2Cue60=[~UNUSED3~]\nOutputList2SigType60=Analog\nOutputList2Cue61=[~UNUSED3~]\nOutputList2SigType61=Analog\nOutputList2Cue62=[~UNUSED3~]\nOutputList2SigType62=Analog\nOutputList2Cue63=[~UNUSED3~]\nOutputList2SigType63=Analog\nOutputList2Cue64=[~UNUSED3~]\nOutputList2SigType64=Analog\nOutputList2Cue65=[~UNUSED3~]\nOutputList2SigType65=Analog\nOutputList2Cue66=[~UNUSED3~]\nOutputList2SigType66=Analog\nOutputList2Cue67=[~UNUSED3~]\nOutputList2SigType67=Analog\nOutputList2Cue68=[~UNUSED3~]\nOutputList2SigType68=Analog\nOutputList2Cue69=[~UNUSED3~]\nOutputList2SigType69=Analog\nOutputList2Cue70=[~UNUSED3~]\nOutputList2SigType70=Analog\nOutputList2Cue71=[~UNUSED3~]\nOutputList2SigType71=Analog\nOutputList2Cue72=[~UNUSED3~]\nOutputList2SigType72=Analog\nOutputList2Cue73=[~UNUSED3~]\nOutputList2SigType73=Analog\nOutputList2Cue74=[~UNUSED3~]\nOutputList2SigType74=Analog\nOutputList2Cue75=[~UNUSED3~]\nOutputList2SigType75=Analog\nOutputList2Cue76=[~UNUSED3~]\nOutputList2SigType76=Analog\nOutputList2Cue77=[~UNUSED3~]\nOutputList2SigType77=Analog\nOutputList2Cue78=[~UNUSED3~]\nOutputList2SigType78=Analog\nOutputList2Cue79=[~UNUSED3~]\nOutputList2SigType79=Analog\nOutputList2Cue80=[~UNUSED3~]\nOutputList2SigType80=Analog\nOutputList2Cue81=[~UNUSED3~]\nOutputList2SigType81=Analog\nOutputList2Cue82=[~UNUSED3~]\nOutputList2SigType82=Analog\nOutputList2Cue83=[~UNUSED3~]\nOutputList2SigType83=Analog\nOutputList2Cue84=[~UNUSED3~]\nOutputList2SigType84=Analog\nOutputList2Cue85=[~UNUSED3~]\nOutputList2SigType85=Analog\nOutputList2Cue86=[~UNUSED3~]\nOutputList2SigType86=Analog\nOutputList2Cue87=[~UNUSED3~]\nOutputList2SigType87=Analog\nOutputList2Cue88=[~UNUSED3~]\nOutputList2SigType88=Analog\nOutputList2Cue89=[~UNUSED3~]\nOutputList2SigType89=Analog\nOutputList2Cue90=[~UNUSED3~]\nOutputList2SigType90=Analog\nOutputList2Cue91=[~UNUSED3~]\nOutputList2SigType91=Analog\nOutputList2Cue92=[~UNUSED3~]\nOutputList2SigType92=Analog\nOutputList2Cue93=[~UNUSED3~]\nOutputList2SigType93=Analog\nOutputList2Cue94=[~UNUSED3~]\nOutputList2SigType94=Analog\nOutputList2Cue95=[~UNUSED3~]\nOutputList2SigType95=Analog\nOutputList2Cue96=[~UNUSED3~]\nOutputList2SigType96=Analog\nOutputList2Cue97=[~UNUSED3~]\nOutputList2SigType97=Analog\nOutputList2Cue98=[~UNUSED3~]\nOutputList2SigType98=Analog\nOutputList2Cue99=[~UNUSED3~]\nOutputList2SigType99=Analog\nOutputList2Cue100=[~UNUSED3~]\nOutputList2SigType100=Analog\nOutputList2Cue101=[~UNUSED3~]\nOutputList2SigType101=Analog\nOutputList2Cue102=[~UNUSED3~]\nOutputList2SigType102=Analog\nOutputList2Cue103=[~UNUSED3~]\nOutputList2SigType103=Analog\nOutputList2Cue104=[~UNUSED3~]\nOutputList2SigType104=Analog\nOutputList2Cue105=[~UNUSED3~]\nOutputList2SigType105=Analog\nOutputList2Cue106=[~UNUSED3~]\nOutputList2SigType106=Analog\nOutputList2Cue107=[~UNUSED3~]\nOutputList2SigType107=Analog\nOutputList2Cue108=[~UNUSED3~]\nOutputList2SigType108=Analog\nOutputList2Cue109=[~UNUSED3~]\nOutputList2SigType109=Analog\nOutputList2Cue110=[~UNUSED3~]\nOutputList2SigType110=Analog\nOutputList2Cue111=[~UNUSED3~]\nOutputList2SigType111=Analog\nOutputList2Cue112=[~UNUSED3~]\nOutputList2SigType112=Analog\nOutputList2Cue113=[~UNUSED3~]\nOutputList2SigType113=Analog\nOutputList2Cue114=[~UNUSED3~]\nOutputList2SigType114=Analog\nOutputList2Cue115=[~UNUSED3~]\nOutputList2SigType115=Analog\nOutputList2Cue116=[~UNUSED3~]\nOutputList2SigType116=Analog\nOutputList2Cue117=[~UNUSED3~]\nOutputList2SigType117=Analog\nOutputList2Cue118=[~UNUSED3~]\nOutputList2SigType118=Analog\nOutputList2Cue119=[~UNUSED3~]\nOutputList2SigType119=Analog\nOutputList2Cue120=[~UNUSED3~]\nOutputList2SigType120=Analog\nOutputList2Cue121=[~UNUSED3~]\nOutputList2SigType121=Analog\nOutputList2Cue122=[~UNUSED3~]\nOutputList2SigType122=Analog\nOutputList2Cue123=[~UNUSED3~]\nOutputList2SigType123=Analog\nOutputList2Cue124=[~UNUSED3~]\nOutputList2SigType124=Analog\nOutputList2Cue125=[~UNUSED3~]\nOutputList2SigType125=Analog\nOutputList2Cue126=[~UNUSED3~]\nOutputList2SigType126=Analog\nOutputList2Cue127=[~UNUSED3~]\nOutputList2SigType127=Analog\nOutputList2Cue128=[~UNUSED3~]\nOutputList2SigType128=Analog\nOutputList2Cue129=[~UNUSED3~]\nOutputList2SigType129=Analog\nOutputList2Cue130=[~UNUSED3~]\nOutputList2SigType130=Analog\nOutputList2Cue131=[~UNUSED3~]\nOutputList2SigType131=Analog\nOutputList2Cue132=[~UNUSED3~]\nOutputList2SigType132=Analog\nOutputList2Cue133=[~UNUSED3~]\nOutputList2SigType133=Analog\nOutputList2Cue134=[~UNUSED3~]\nOutputList2SigType134=Analog\nOutputList2Cue135=[~UNUSED3~]\nOutputList2SigType135=Analog\nOutputList2Cue136=[~UNUSED3~]\nOutputList2SigType136=Analog\nOutputList2Cue137=[~UNUSED3~]\nOutputList2SigType137=Analog\nOutputList2Cue138=[~UNUSED3~]\nOutputList2SigType138=Analog\nOutputList2Cue139=[~UNUSED3~]\nOutputList2SigType139=Analog\nOutputList2Cue140=[~UNUSED3~]\nOutputList2SigType140=Analog\nOutputList2Cue141=[~UNUSED3~]\nOutputList2SigType141=Analog\nOutputList2Cue142=[~UNUSED3~]\nOutputList2SigType142=Analog\nOutputList2Cue143=[~UNUSED3~]\nOutputList2SigType143=Analog\nOutputList2Cue144=[~UNUSED3~]\nOutputList2SigType144=Analog\nOutputList2Cue145=[~UNUSED3~]\nOutputList2SigType145=Analog\nOutputList2Cue146=[~UNUSED3~]\nOutputList2SigType146=Analog\nOutputList2Cue147=[~UNUSED3~]\nOutputList2SigType147=Analog\nOutputList2Cue148=[~UNUSED3~]\nOutputList2SigType148=Analog\nOutputList2Cue149=[~UNUSED3~]\nOutputList2SigType149=Analog\nOutputList2Cue150=[~UNUSED3~]\nOutputList2SigType150=Analog\nOutputList2Cue151=[~UNUSED3~]\nOutputList2SigType151=Analog\nOutputList2Cue152=[~UNUSED3~]\nOutputList2SigType152=Analog\nOutputList2Cue153=[~UNUSED3~]\nOutputList2SigType153=Analog\nOutputList2Cue154=[~UNUSED3~]\nOutputList2SigType154=Analog\nOutputList2Cue155=[~UNUSED3~]\nOutputList2SigType155=Analog\nOutputList2Cue156=[~UNUSED3~]\nOutputList2SigType156=Analog\nOutputList2Cue157=[~UNUSED3~]\nOutputList2SigType157=Analog\nOutputList2Cue158=[~UNUSED3~]\nOutputList2SigType158=Analog\nOutputList2Cue159=[~UNUSED3~]\nOutputList2SigType159=Analog\nOutputList2Cue160=[~UNUSED3~]\nOutputList2SigType160=Analog\nOutputList2Cue161=[~UNUSED3~]\nOutputList2SigType161=Analog\nOutputList2Cue162=[~UNUSED3~]\nOutputList2SigType162=Analog\nOutputList2Cue163=[~UNUSED3~]\nOutputList2SigType163=Analog\nOutputList2Cue164=[~UNUSED3~]\nOutputList2SigType164=Analog\nOutputList2Cue165=[~UNUSED3~]\nOutputList2SigType165=Analog\nOutputList2Cue166=[~UNUSED3~]\nOutputList2SigType166=Analog\nOutputList2Cue167=[~UNUSED3~]\nOutputList2SigType167=Analog\nOutputList2Cue168=[~UNUSED3~]\nOutputList2SigType168=Analog\nOutputList2Cue169=[~UNUSED3~]\nOutputList2SigType169=Analog\nOutputList2Cue170=[~UNUSED3~]\nOutputList2SigType170=Analog\nOutputList2Cue171=[~UNUSED3~]\nOutputList2SigType171=Analog\nOutputList2Cue172=[~UNUSED3~]\nOutputList2SigType172=Analog\nOutputList2Cue173=[~UNUSED3~]\nOutputList2SigType173=Analog\nOutputList2Cue174=[~UNUSED3~]\nOutputList2SigType174=Analog\nOutputList2Cue175=[~UNUSED3~]\nOutputList2SigType175=Analog\nOutputList2Cue176=[~UNUSED3~]\nOutputList2SigType176=Analog\nOutputList2Cue177=[~UNUSED3~]\nOutputList2SigType177=Analog\nOutputList2Cue178=[~UNUSED3~]\nOutputList2SigType178=Analog\nOutputList2Cue179=[~UNUSED3~]\nOutputList2SigType179=Analog\nOutputList2Cue180=[~UNUSED3~]\nOutputList2SigType180=Analog\nOutputList2Cue181=[~UNUSED3~]\nOutputList2SigType181=Analog\nOutputList2Cue182=[~UNUSED3~]\nOutputList2SigType182=Analog\nOutputList2Cue183=[~UNUSED3~]\nOutputList2SigType183=Analog\nOutputList2Cue184=[~UNUSED3~]\nOutputList2SigType184=Analog\nOutputList2Cue185=[~UNUSED3~]\nOutputList2SigType185=Analog\nOutputList2Cue186=[~UNUSED3~]\nOutputList2SigType186=Analog\nOutputList2Cue187=[~UNUSED3~]\nOutputList2SigType187=Analog\nOutputList2Cue188=[~UNUSED3~]\nOutputList2SigType188=Analog\nOutputList2Cue189=[~UNUSED3~]\nOutputList2SigType189=Analog\nOutputList2Cue190=[~UNUSED3~]\nOutputList2SigType190=Analog\nOutputList2Cue191=[~UNUSED3~]\nOutputList2SigType191=Analog\nOutputList2Cue192=[~UNUSED3~]\nOutputList2SigType192=Analog\nOutputList2Cue193=[~UNUSED3~]\nOutputList2SigType193=Analog\nOutputList2Cue194=[~UNUSED3~]\nOutputList2SigType194=Analog\nOutputList2Cue195=[~UNUSED3~]\nOutputList2SigType195=Analog\nOutputList2Cue196=[~UNUSED3~]\nOutputList2SigType196=Analog\nOutputList2Cue197=[~UNUSED3~]\nOutputList2SigType197=Analog\nOutputList2Cue198=[~UNUSED3~]\nOutputList2SigType198=Analog\nOutputList2Cue199=[~UNUSED3~]\nOutputList2SigType199=Analog\nOutputList2Cue200=[~UNUSED3~]\nOutputList2SigType200=Analog\nOutputList2Cue201=[~UNUSED3~]\nOutputList2SigType201=Analog\nOutputList2Cue202=[~UNUSED3~]\nOutputList2SigType202=Analog\nOutputList2Cue203=[~UNUSED3~]\nOutputList2SigType203=Analog\nOutputList2Cue204=[~UNUSED3~]\nOutputList2SigType204=Analog\nOutputList2Cue205=[~UNUSED3~]\nOutputList2SigType205=Analog\nOutputList2Cue206=[~UNUSED3~]\nOutputList2SigType206=Analog\nOutputList2Cue207=[~UNUSED3~]\nOutputList2SigType207=Analog\nOutputList2Cue208=[~UNUSED3~]\nOutputList2SigType208=Analog\nOutputList2Cue209=[~UNUSED3~]\nOutputList2SigType209=Analog\nOutputList2Cue210=[~UNUSED3~]\nOutputList2SigType210=Analog\nOutputList2Cue211=[~UNUSED3~]\nOutputList2SigType211=Analog\nOutputList2Cue212=[~UNUSED3~]\nOutputList2SigType212=Analog\nOutputList2Cue213=[~UNUSED3~]\nOutputList2SigType213=Analog\nOutputList2Cue214=[~UNUSED3~]\nOutputList2SigType214=Analog\nOutputList2Cue215=[~UNUSED3~]\nOutputList2SigType215=Analog\nOutputList2Cue216=[~UNUSED3~]\nOutputList2SigType216=Analog\nOutputList2Cue217=[~UNUSED3~]\nOutputList2SigType217=Analog\nOutputList2Cue218=[~UNUSED3~]\nOutputList2SigType218=Analog\nOutputList2Cue219=[~UNUSED3~]\nOutputList2SigType219=Analog\nOutputList2Cue220=[~UNUSED3~]\nOutputList2SigType220=Analog\nOutputList2Cue221=[~UNUSED3~]\nOutputList2SigType221=Analog\nOutputList2Cue222=[~UNUSED3~]\nOutputList2SigType222=Analog\nOutputList2Cue223=[~UNUSED3~]\nOutputList2SigType223=Analog\nOutputList2Cue224=[~UNUSED3~]\nOutputList2SigType224=Analog\nOutputList2Cue225=[~UNUSED3~]\nOutputList2SigType225=Analog\nOutputList2Cue226=[~UNUSED3~]\nOutputList2SigType226=Analog\nOutputList2Cue227=[~UNUSED3~]\nOutputList2SigType227=Analog\nOutputList2Cue228=[~UNUSED3~]\nOutputList2SigType228=Analog\nOutputList2Cue229=[~UNUSED3~]\nOutputList2SigType229=Analog\nOutputList2Cue230=[~UNUSED3~]\nOutputList2SigType230=Analog\nOutputList2Cue231=[~UNUSED3~]\nOutputList2SigType231=Analog\nOutputList2Cue232=[~UNUSED3~]\nOutputList2SigType232=Analog\nOutputList2Cue233=[~UNUSED3~]\nOutputList2SigType233=Analog\nOutputList2Cue234=[~UNUSED3~]\nOutputList2SigType234=Analog\nOutputList2Cue235=[~UNUSED3~]\nOutputList2SigType235=Analog\nOutputList2Cue236=[~UNUSED3~]\nOutputList2SigType236=Analog\nOutputList2Cue237=[~UNUSED3~]\nOutputList2SigType237=Analog\nOutputList2Cue238=[~UNUSED3~]\nOutputList2SigType238=Analog\nOutputList2Cue239=[~UNUSED3~]\nOutputList2SigType239=Analog\nOutputList2Cue240=[~UNUSED3~]\nOutputList2SigType240=Analog\nOutputList2Cue241=[~UNUSED3~]\nOutputList2SigType241=Analog\nOutputList2Cue242=[~UNUSED3~]\nOutputList2SigType242=Analog\nOutputList2Cue243=[~UNUSED3~]\nOutputList2SigType243=Analog\nOutputList2Cue244=[~UNUSED3~]\nOutputList2SigType244=Analog\nOutputList2Cue245=[~UNUSED3~]\nOutputList2SigType245=Analog\nOutputList2Cue246=[~UNUSED3~]\nOutputList2SigType246=Analog\nOutputList2Cue247=[~UNUSED3~]\nOutputList2SigType247=Analog\nOutputList2Cue248=[~UNUSED3~]\nOutputList2SigType248=Analog\nOutputList2Cue249=[~UNUSED3~]\nOutputList2SigType249=Analog\nOutputList2Cue250=[~UNUSED3~]\nOutputList2SigType250=Analog\nOutputList2Cue251=[~UNUSED3~]\nOutputList2SigType251=Analog\nOutputList2Cue252=[~UNUSED3~]\nOutputList2SigType252=Analog\nOutputList2Cue253=[~UNUSED3~]\nOutputList2SigType253=Analog\nOutputList2Cue254=[~UNUSED3~]\nOutputList2SigType254=Analog\nOutputList2Cue255=[~UNUSED3~]\nOutputList2SigType255=Analog\nOutputList2Cue256=[~UNUSED3~]\nOutputList2SigType256=Analog\nOutputList2Cue257=[~UNUSED3~]\nOutputList2SigType257=Analog\nOutputList2Cue258=[~UNUSED3~]\nOutputList2SigType258=Analog\nOutputList2Cue259=[~UNUSED3~]\nOutputList2SigType259=Analog\nOutputList2Cue260=[~UNUSED3~]\nOutputList2SigType260=Analog\nOutputList2Cue261=[~UNUSED3~]\nOutputList2SigType261=Analog\nOutputList2Cue262=[~UNUSED3~]\nOutputList2SigType262=Analog\nOutputList2Cue263=[~UNUSED3~]\nOutputList2SigType263=Analog\nOutputList2Cue264=[~UNUSED3~]\nOutputList2SigType264=Analog\nOutputList2Cue265=[~UNUSED3~]\nOutputList2SigType265=Analog\nOutputList2Cue266=[~UNUSED3~]\nOutputList2SigType266=Analog\nOutputList2Cue267=[~UNUSED2~]\nOutputList2SigType267=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=Set Item 11 Text\nInputList3SigType22=Serial\nInputList3Cue23=Set Item 12 Text\nInputList3SigType23=Serial\nInputList3Cue24=Set Item 13 Text\nInputList3SigType24=Serial\nInputList3Cue25=Set Item 14 Text\nInputList3SigType25=Serial\nInputList3Cue26=Set Item 15 Text\nInputList3SigType26=Serial\nInputList3Cue27=Set Item 16 Text\nInputList3SigType27=Serial\nInputList3Cue28=Set Item 17 Text\nInputList3SigType28=Serial\nInputList3Cue29=Set Item 18 Text\nInputList3SigType29=Serial\nInputList3Cue30=Set Item 19 Text\nInputList3SigType30=Serial\nInputList3Cue31=Set Item 20 Text\nInputList3SigType31=Serial\nInputList3Cue32=Set Item 21 Text\nInputList3SigType32=Serial\nInputList3Cue33=Set Item 22 Text\nInputList3SigType33=Serial\nInputList3Cue34=Set Item 23 Text\nInputList3SigType34=Serial\nInputList3Cue35=Set Item 24 Text\nInputList3SigType35=Serial\nInputList3Cue36=Set Item 25 Text\nInputList3SigType36=Serial\nInputList3Cue37=Set Item 26 Text\nInputList3SigType37=Serial\nInputList3Cue38=Set Item 27 Text\nInputList3SigType38=Serial\nInputList3Cue39=Set Item 28 Text\nInputList3SigType39=Serial\nInputList3Cue40=Set Item 29 Text\nInputList3SigType40=Serial\nInputList3Cue41=Set Item 30 Text\nInputList3SigType41=Serial\nInputList3Cue42=Set Item 31 Text\nInputList3SigType42=Serial\nInputList3Cue43=Set Item 32 Text\nInputList3SigType43=Serial\nInputList3Cue44=Set Item 33 Text\nInputList3SigType44=Serial\nInputList3Cue45=Set Item 34 Text\nInputList3SigType45=Serial\nInputList3Cue46=Set Item 35 Text\nInputList3SigType46=Serial\nInputList3Cue47=Set Item 36 Text\nInputList3SigType47=Serial\nInputList3Cue48=Set Item 37 Text\nInputList3SigType48=Serial\nInputList3Cue49=Set Item 38 Text\nInputList3SigType49=Serial\nInputList3Cue50=Set Item 39 Text\nInputList3SigType50=Serial\nInputList3Cue51=Set Item 40 Text\nInputList3SigType51=Serial\nInputList3Cue52=Set Item 41 Text\nInputList3SigType52=Serial\nInputList3Cue53=Set Item 42 Text\nInputList3SigType53=Serial\nInputList3Cue54=Set Item 43 Text\nInputList3SigType54=Serial\nInputList3Cue55=Set Item 44 Text\nInputList3SigType55=Serial\nInputList3Cue56=Set Item 45 Text\nInputList3SigType56=Serial\nInputList3Cue57=Set Item 46 Text\nInputList3SigType57=Serial\nInputList3Cue58=Set Item 47 Text\nInputList3SigType58=Serial\nInputList3Cue59=Set Item 48 Text\nInputList3SigType59=Serial\nInputList3Cue60=Set Item 49 Text\nInputList3SigType60=Serial\nInputList3Cue61=Set Item 50 Text\nInputList3SigType61=Serial\nInputList3Cue62=Set Item 51 Text\nInputList3SigType62=Serial\nInputList3Cue63=Set Item 52 Text\nInputList3SigType63=Serial\nInputList3Cue64=Set Item 53 Text\nInputList3SigType64=Serial\nInputList3Cue65=Set Item 54 Text\nInputList3SigType65=Serial\nInputList3Cue66=Set Item 55 Text\nInputList3SigType66=Serial\nInputList3Cue67=Set Item 56 Text\nInputList3SigType67=Serial\nInputList3Cue68=Set Item 57 Text\nInputList3SigType68=Serial\nInputList3Cue69=Set Item 58 Text\nInputList3SigType69=Serial\nInputList3Cue70=Set Item 59 Text\nInputList3SigType70=Serial\nInputList3Cue71=Set Item 60 Text\nInputList3SigType71=Serial\nInputList3Cue72=Set Item 61 Text\nInputList3SigType72=Serial\nInputList3Cue73=Set Item 62 Text\nInputList3SigType73=Serial\nInputList3Cue74=Set Item 63 Text\nInputList3SigType74=Serial\nInputList3Cue75=Set Item 64 Text\nInputList3SigType75=Serial\nInputList3Cue76=Set Item 65 Text\nInputList3SigType76=Serial\nInputList3Cue77=Set Item 66 Text\nInputList3SigType77=Serial\nInputList3Cue78=Set Item 67 Text\nInputList3SigType78=Serial\nInputList3Cue79=Set Item 68 Text\nInputList3SigType79=Serial\nInputList3Cue80=Set Item 69 Text\nInputList3SigType80=Serial\nInputList3Cue81=Set Item 70 Text\nInputList3SigType81=Serial\nInputList3Cue82=Set Item 71 Text\nInputList3SigType82=Serial\nInputList3Cue83=Set Item 72 Text\nInputList3SigType83=Serial\nInputList3Cue84=Set Item 73 Text\nInputList3SigType84=Serial\nInputList3Cue85=Set Item 74 Text\nInputList3SigType85=Serial\nInputList3Cue86=Set Item 75 Text\nInputList3SigType86=Serial\nInputList3Cue87=Set Item 76 Text\nInputList3SigType87=Serial\nInputList3Cue88=Set Item 77 Text\nInputList3SigType88=Serial\nInputList3Cue89=Set Item 78 Text\nInputList3SigType89=Serial\nInputList3Cue90=Set Item 79 Text\nInputList3SigType90=Serial\nInputList3Cue91=Set Item 80 Text\nInputList3SigType91=Serial\nInputList3Cue92=Set Item 81 Text\nInputList3SigType92=Serial\nInputList3Cue93=Set Item 82 Text\nInputList3SigType93=Serial\nInputList3Cue94=Set Item 83 Text\nInputList3SigType94=Serial\nInputList3Cue95=Set Item 84 Text\nInputList3SigType95=Serial\nInputList3Cue96=Set Item 85 Text\nInputList3SigType96=Serial\nInputList3Cue97=Set Item 86 Text\nInputList3SigType97=Serial\nInputList3Cue98=Set Item 87 Text\nInputList3SigType98=Serial\nInputList3Cue99=Set Item 88 Text\nInputList3SigType99=Serial\nInputList3Cue100=Set Item 89 Text\nInputList3SigType100=Serial\nInputList3Cue101=Set Item 90 Text\nInputList3SigType101=Serial\nInputList3Cue102=Set Item 91 Text\nInputList3SigType102=Serial\nInputList3Cue103=Set Item 92 Text\nInputList3SigType103=Serial\nInputList3Cue104=Set Item 93 Text\nInputList3SigType104=Serial\nInputList3Cue105=Set Item 94 Text\nInputList3SigType105=Serial\nInputList3Cue106=Set Item 95 Text\nInputList3SigType106=Serial\nInputList3Cue107=Set Item 96 Text\nInputList3SigType107=Serial\nInputList3Cue108=Set Item 97 Text\nInputList3SigType108=Serial\nInputList3Cue109=Set Item 98 Text\nInputList3SigType109=Serial\nInputList3Cue110=Set Item 99 Text\nInputList3SigType110=Serial\nInputList3Cue111=Set Item 100 Text\nInputList3SigType111=Serial\nInputList3Cue112=Set Item 101 Text\nInputList3SigType112=Serial\nInputList3Cue113=Set Item 102 Text\nInputList3SigType113=Serial\nInputList3Cue114=Set Item 103 Text\nInputList3SigType114=Serial\nInputList3Cue115=Set Item 104 Text\nInputList3SigType115=Serial\nInputList3Cue116=Set Item 105 Text\nInputList3SigType116=Serial\nInputList3Cue117=Set Item 106 Text\nInputList3SigType117=Serial\nInputList3Cue118=Set Item 107 Text\nInputList3SigType118=Serial\nInputList3Cue119=Set Item 108 Text\nInputList3SigType119=Serial\nInputList3Cue120=Set Item 109 Text\nInputList3SigType120=Serial\nInputList3Cue121=Set Item 110 Text\nInputList3SigType121=Serial\nInputList3Cue122=Set Item 111 Text\nInputList3SigType122=Serial\nInputList3Cue123=Set Item 112 Text\nInputList3SigType123=Serial\nInputList3Cue124=Set Item 113 Text\nInputList3SigType124=Serial\nInputList3Cue125=Set Item 114 Text\nInputList3SigType125=Serial\nInputList3Cue126=Set Item 115 Text\nInputList3SigType126=Serial\nInputList3Cue127=Set Item 116 Text\nInputList3SigType127=Serial\nInputList3Cue128=Set Item 117 Text\nInputList3SigType128=Serial\nInputList3Cue129=Set Item 118 Text\nInputList3SigType129=Serial\nInputList3Cue130=Set Item 119 Text\nInputList3SigType130=Serial\nInputList3Cue131=Set Item 120 Text\nInputList3SigType131=Serial\nInputList3Cue132=Set Item 121 Text\nInputList3SigType132=Serial\nInputList3Cue133=Set Item 122 Text\nInputList3SigType133=Serial\nInputList3Cue134=Set Item 123 Text\nInputList3SigType134=Serial\nInputList3Cue135=Set Item 124 Text\nInputList3SigType135=Serial\nInputList3Cue136=Set Item 125 Text\nInputList3SigType136=Serial\nInputList3Cue137=Set Item 126 Text\nInputList3SigType137=Serial\nInputList3Cue138=Set Item 127 Text\nInputList3SigType138=Serial\nInputList3Cue139=Set Item 128 Text\nInputList3SigType139=Serial\nInputList3Cue140=Set Item 129 Text\nInputList3SigType140=Serial\nInputList3Cue141=Set Item 130 Text\nInputList3SigType141=Serial\nInputList3Cue142=Set Item 131 Text\nInputList3SigType142=Serial\nInputList3Cue143=Set Item 132 Text\nInputList3SigType143=Serial\nInputList3Cue144=Set Item 133 Text\nInputList3SigType144=Serial\nInputList3Cue145=Set Item 134 Text\nInputList3SigType145=Serial\nInputList3Cue146=Set Item 135 Text\nInputList3SigType146=Serial\nInputList3Cue147=Set Item 136 Text\nInputList3SigType147=Serial\nInputList3Cue148=Set Item 137 Text\nInputList3SigType148=Serial\nInputList3Cue149=Set Item 138 Text\nInputList3SigType149=Serial\nInputList3Cue150=Set Item 139 Text\nInputList3SigType150=Serial\nInputList3Cue151=Set Item 140 Text\nInputList3SigType151=Serial\nInputList3Cue152=Set Item 141 Text\nInputList3SigType152=Serial\nInputList3Cue153=Set Item 142 Text\nInputList3SigType153=Serial\nInputList3Cue154=Set Item 143 Text\nInputList3SigType154=Serial\nInputList3Cue155=Set Item 144 Text\nInputList3SigType155=Serial\nInputList3Cue156=Set Item 145 Text\nInputList3SigType156=Serial\nInputList3Cue157=Set Item 146 Text\nInputList3SigType157=Serial\nInputList3Cue158=Set Item 147 Text\nInputList3SigType158=Serial\nInputList3Cue159=Set Item 148 Text\nInputList3SigType159=Serial\nInputList3Cue160=Set Item 149 Text\nInputList3SigType160=Serial\nInputList3Cue161=Set Item 150 Text\nInputList3SigType161=Serial\nInputList3Cue162=Set Item 151 Text\nInputList3SigType162=Serial\nInputList3Cue163=Set Item 152 Text\nInputList3SigType163=Serial\nInputList3Cue164=Set Item 153 Text\nInputList3SigType164=Serial\nInputList3Cue165=Set Item 154 Text\nInputList3SigType165=Serial\nInputList3Cue166=Set Item 155 Text\nInputList3SigType166=Serial\nInputList3Cue167=Set Item 156 Text\nInputList3SigType167=Serial\nInputList3Cue168=Set Item 157 Text\nInputList3SigType168=Serial\nInputList3Cue169=Set Item 158 Text\nInputList3SigType169=Serial\nInputList3Cue170=Set Item 159 Text\nInputList3SigType170=Serial\nInputList3Cue171=Set Item 160 Text\nInputList3SigType171=Serial\nInputList3Cue172=Set Item 161 Text\nInputList3SigType172=Serial\nInputList3Cue173=Set Item 162 Text\nInputList3SigType173=Serial\nInputList3Cue174=Set Item 163 Text\nInputList3SigType174=Serial\nInputList3Cue175=Set Item 164 Text\nInputList3SigType175=Serial\nInputList3Cue176=Set Item 165 Text\nInputList3SigType176=Serial\nInputList3Cue177=Set Item 166 Text\nInputList3SigType177=Serial\nInputList3Cue178=Set Item 167 Text\nInputList3SigType178=Serial\nInputList3Cue179=Set Item 168 Text\nInputList3SigType179=Serial\nInputList3Cue180=Set Item 169 Text\nInputList3SigType180=Serial\nInputList3Cue181=Set Item 170 Text\nInputList3SigType181=Serial\nInputList3Cue182=Set Item 171 Text\nInputList3SigType182=Serial\nInputList3Cue183=Set Item 172 Text\nInputList3SigType183=Serial\nInputList3Cue184=Set Item 173 Text\nInputList3SigType184=Serial\nInputList3Cue185=Set Item 174 Text\nInputList3SigType185=Serial\nInputList3Cue186=Set Item 175 Text\nInputList3SigType186=Serial\nInputList3Cue187=Set Item 176 Text\nInputList3SigType187=Serial\nInputList3Cue188=Set Item 177 Text\nInputList3SigType188=Serial\nInputList3Cue189=Set Item 178 Text\nInputList3SigType189=Serial\nInputList3Cue190=Set Item 179 Text\nInputList3SigType190=Serial\nInputList3Cue191=Set Item 180 Text\nInputList3SigType191=Serial\nInputList3Cue192=Set Item 181 Text\nInputList3SigType192=Serial\nInputList3Cue193=Set Item 182 Text\nInputList3SigType193=Serial\nInputList3Cue194=Set Item 183 Text\nInputList3SigType194=Serial\nInputList3Cue195=Set Item 184 Text\nInputList3SigType195=Serial\nInputList3Cue196=Set Item 185 Text\nInputList3SigType196=Serial\nInputList3Cue197=Set Item 186 Text\nInputList3SigType197=Serial\nInputList3Cue198=Set Item 187 Text\nInputList3SigType198=Serial\nInputList3Cue199=Set Item 188 Text\nInputList3SigType199=Serial\nInputList3Cue200=Set Item 189 Text\nInputList3SigType200=Serial\nInputList3Cue201=Set Item 190 Text\nInputList3SigType201=Serial\nInputList3Cue202=Set Item 191 Text\nInputList3SigType202=Serial\nInputList3Cue203=Set Item 192 Text\nInputList3SigType203=Serial\nInputList3Cue204=Set Item 193 Text\nInputList3SigType204=Serial\nInputList3Cue205=Set Item 194 Text\nInputList3SigType205=Serial\nInputList3Cue206=Set Item 195 Text\nInputList3SigType206=Serial\nInputList3Cue207=Set Item 196 Text\nInputList3SigType207=Serial\nInputList3Cue208=Set Item 197 Text\nInputList3SigType208=Serial\nInputList3Cue209=Set Item 198 Text\nInputList3SigType209=Serial\nInputList3Cue210=Set Item 199 Text\nInputList3SigType210=Serial\nInputList3Cue211=Set Item 200 Text\nInputList3SigType211=Serial\nInputList3Cue212=Set Item 201 Text\nInputList3SigType212=Serial\nInputList3Cue213=Set Item 202 Text\nInputList3SigType213=Serial\nInputList3Cue214=Set Item 203 Text\nInputList3SigType214=Serial\nInputList3Cue215=Set Item 204 Text\nInputList3SigType215=Serial\nInputList3Cue216=Set Item 205 Text\nInputList3SigType216=Serial\nInputList3Cue217=Set Item 206 Text\nInputList3SigType217=Serial\nInputList3Cue218=Set Item 207 Text\nInputList3SigType218=Serial\nInputList3Cue219=Set Item 208 Text\nInputList3SigType219=Serial\nInputList3Cue220=Set Item 209 Text\nInputList3SigType220=Serial\nInputList3Cue221=Set Item 210 Text\nInputList3SigType221=Serial\nInputList3Cue222=Set Item 211 Text\nInputList3SigType222=Serial\nInputList3Cue223=Set Item 212 Text\nInputList3SigType223=Serial\nInputList3Cue224=Set Item 213 Text\nInputList3SigType224=Serial\nInputList3Cue225=Set Item 214 Text\nInputList3SigType225=Serial\nInputList3Cue226=Set Item 215 Text\nInputList3SigType226=Serial\nInputList3Cue227=Set Item 216 Text\nInputList3SigType227=Serial\nInputList3Cue228=Set Item 217 Text\nInputList3SigType228=Serial\nInputList3Cue229=Set Item 218 Text\nInputList3SigType229=Serial\nInputList3Cue230=Set Item 219 Text\nInputList3SigType230=Serial\nInputList3Cue231=Set Item 220 Text\nInputList3SigType231=Serial\nInputList3Cue232=Set Item 221 Text\nInputList3SigType232=Serial\nInputList3Cue233=Set Item 222 Text\nInputList3SigType233=Serial\nInputList3Cue234=Set Item 223 Text\nInputList3SigType234=Serial\nInputList3Cue235=Set Item 224 Text\nInputList3SigType235=Serial\nInputList3Cue236=Set Item 225 Text\nInputList3SigType236=Serial\nInputList3Cue237=Set Item 226 Text\nInputList3SigType237=Serial\nInputList3Cue238=Set Item 227 Text\nInputList3SigType238=Serial\nInputList3Cue239=Set Item 228 Text\nInputList3SigType239=Serial\nInputList3Cue240=Set Item 229 Text\nInputList3SigType240=Serial\nInputList3Cue241=Set Item 230 Text\nInputList3SigType241=Serial\nInputList3Cue242=Set Item 231 Text\nInputList3SigType242=Serial\nInputList3Cue243=Set Item 232 Text\nInputList3SigType243=Serial\nInputList3Cue244=Set Item 233 Text\nInputList3SigType244=Serial\nInputList3Cue245=Set Item 234 Text\nInputList3SigType245=Serial\nInputList3Cue246=Set Item 235 Text\nInputList3SigType246=Serial\nInputList3Cue247=Set Item 236 Text\nInputList3SigType247=Serial\nInputList3Cue248=Set Item 237 Text\nInputList3SigType248=Serial\nInputList3Cue249=Set Item 238 Text\nInputList3SigType249=Serial\nInputList3Cue250=Set Item 239 Text\nInputList3SigType250=Serial\nInputList3Cue251=Set Item 240 Text\nInputList3SigType251=Serial\nInputList3Cue252=Set Item 241 Text\nInputList3SigType252=Serial\nInputList3Cue253=Set Item 242 Text\nInputList3SigType253=Serial\nInputList3Cue254=Set Item 243 Text\nInputList3SigType254=Serial\nInputList3Cue255=Set Item 244 Text\nInputList3SigType255=Serial\nInputList3Cue256=Set Item 245 Text\nInputList3SigType256=Serial\nInputList3Cue257=Set Item 246 Text\nInputList3SigType257=Serial\nInputList3Cue258=Set Item 247 Text\nInputList3SigType258=Serial\nInputList3Cue259=Set Item 248 Text\nInputList3SigType259=Serial\nInputList3Cue260=Set Item 249 Text\nInputList3SigType260=Serial\nInputList3Cue261=Set Item 250 Text\nInputList3SigType261=Serial\nInputList3Cue262=Set Item 251 Text\nInputList3SigType262=Serial\nInputList3Cue263=Set Item 252 Text\nInputList3SigType263=Serial\nInputList3Cue264=Set Item 253 Text\nInputList3SigType264=Serial\nInputList3Cue265=Set Item 254 Text\nInputList3SigType265=Serial\nInputList3Cue266=Set Item 255 Text\nInputList3SigType266=Serial\nInputList3Cue267=[~UNUSED2~]\nInputList3SigType267=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=Set Item 11 Icon Serial\nInputList3SigType2024=Serial\nInputList3Cue2025=Set Item 12 Icon Serial\nInputList3SigType2025=Serial\nInputList3Cue2026=Set Item 13 Icon Serial\nInputList3SigType2026=Serial\nInputList3Cue2027=Set Item 14 Icon Serial\nInputList3SigType2027=Serial\nInputList3Cue2028=Set Item 15 Icon Serial\nInputList3SigType2028=Serial\nInputList3Cue2029=Set Item 16 Icon Serial\nInputList3SigType2029=Serial\nInputList3Cue2030=Set Item 17 Icon Serial\nInputList3SigType2030=Serial\nInputList3Cue2031=Set Item 18 Icon Serial\nInputList3SigType2031=Serial\nInputList3Cue2032=Set Item 19 Icon Serial\nInputList3SigType2032=Serial\nInputList3Cue2033=Set Item 20 Icon Serial\nInputList3SigType2033=Serial\nInputList3Cue2034=Set Item 21 Icon Serial\nInputList3SigType2034=Serial\nInputList3Cue2035=Set Item 22 Icon Serial\nInputList3SigType2035=Serial\nInputList3Cue2036=Set Item 23 Icon Serial\nInputList3SigType2036=Serial\nInputList3Cue2037=Set Item 24 Icon Serial\nInputList3SigType2037=Serial\nInputList3Cue2038=Set Item 25 Icon Serial\nInputList3SigType2038=Serial\nInputList3Cue2039=Set Item 26 Icon Serial\nInputList3SigType2039=Serial\nInputList3Cue2040=Set Item 27 Icon Serial\nInputList3SigType2040=Serial\nInputList3Cue2041=Set Item 28 Icon Serial\nInputList3SigType2041=Serial\nInputList3Cue2042=Set Item 29 Icon Serial\nInputList3SigType2042=Serial\nInputList3Cue2043=Set Item 30 Icon Serial\nInputList3SigType2043=Serial\nInputList3Cue2044=Set Item 31 Icon Serial\nInputList3SigType2044=Serial\nInputList3Cue2045=Set Item 32 Icon Serial\nInputList3SigType2045=Serial\nInputList3Cue2046=Set Item 33 Icon Serial\nInputList3SigType2046=Serial\nInputList3Cue2047=Set Item 34 Icon Serial\nInputList3SigType2047=Serial\nInputList3Cue2048=Set Item 35 Icon Serial\nInputList3SigType2048=Serial\nInputList3Cue2049=Set Item 36 Icon Serial\nInputList3SigType2049=Serial\nInputList3Cue2050=Set Item 37 Icon Serial\nInputList3SigType2050=Serial\nInputList3Cue2051=Set Item 38 Icon Serial\nInputList3SigType2051=Serial\nInputList3Cue2052=Set Item 39 Icon Serial\nInputList3SigType2052=Serial\nInputList3Cue2053=Set Item 40 Icon Serial\nInputList3SigType2053=Serial\nInputList3Cue2054=Set Item 41 Icon Serial\nInputList3SigType2054=Serial\nInputList3Cue2055=Set Item 42 Icon Serial\nInputList3SigType2055=Serial\nInputList3Cue2056=Set Item 43 Icon Serial\nInputList3SigType2056=Serial\nInputList3Cue2057=Set Item 44 Icon Serial\nInputList3SigType2057=Serial\nInputList3Cue2058=Set Item 45 Icon Serial\nInputList3SigType2058=Serial\nInputList3Cue2059=Set Item 46 Icon Serial\nInputList3SigType2059=Serial\nInputList3Cue2060=Set Item 47 Icon Serial\nInputList3SigType2060=Serial\nInputList3Cue2061=Set Item 48 Icon Serial\nInputList3SigType2061=Serial\nInputList3Cue2062=Set Item 49 Icon Serial\nInputList3SigType2062=Serial\nInputList3Cue2063=Set Item 50 Icon Serial\nInputList3SigType2063=Serial\nInputList3Cue2064=Set Item 51 Icon Serial\nInputList3SigType2064=Serial\nInputList3Cue2065=Set Item 52 Icon Serial\nInputList3SigType2065=Serial\nInputList3Cue2066=Set Item 53 Icon Serial\nInputList3SigType2066=Serial\nInputList3Cue2067=Set Item 54 Icon Serial\nInputList3SigType2067=Serial\nInputList3Cue2068=Set Item 55 Icon Serial\nInputList3SigType2068=Serial\nInputList3Cue2069=Set Item 56 Icon Serial\nInputList3SigType2069=Serial\nInputList3Cue2070=Set Item 57 Icon Serial\nInputList3SigType2070=Serial\nInputList3Cue2071=Set Item 58 Icon Serial\nInputList3SigType2071=Serial\nInputList3Cue2072=Set Item 59 Icon Serial\nInputList3SigType2072=Serial\nInputList3Cue2073=Set Item 60 Icon Serial\nInputList3SigType2073=Serial\nInputList3Cue2074=Set Item 61 Icon Serial\nInputList3SigType2074=Serial\nInputList3Cue2075=Set Item 62 Icon Serial\nInputList3SigType2075=Serial\nInputList3Cue2076=Set Item 63 Icon Serial\nInputList3SigType2076=Serial\nInputList3Cue2077=Set Item 64 Icon Serial\nInputList3SigType2077=Serial\nInputList3Cue2078=Set Item 65 Icon Serial\nInputList3SigType2078=Serial\nInputList3Cue2079=Set Item 66 Icon Serial\nInputList3SigType2079=Serial\nInputList3Cue2080=Set Item 67 Icon Serial\nInputList3SigType2080=Serial\nInputList3Cue2081=Set Item 68 Icon Serial\nInputList3SigType2081=Serial\nInputList3Cue2082=Set Item 69 Icon Serial\nInputList3SigType2082=Serial\nInputList3Cue2083=Set Item 70 Icon Serial\nInputList3SigType2083=Serial\nInputList3Cue2084=Set Item 71 Icon Serial\nInputList3SigType2084=Serial\nInputList3Cue2085=Set Item 72 Icon Serial\nInputList3SigType2085=Serial\nInputList3Cue2086=Set Item 73 Icon Serial\nInputList3SigType2086=Serial\nInputList3Cue2087=Set Item 74 Icon Serial\nInputList3SigType2087=Serial\nInputList3Cue2088=Set Item 75 Icon Serial\nInputList3SigType2088=Serial\nInputList3Cue2089=Set Item 76 Icon Serial\nInputList3SigType2089=Serial\nInputList3Cue2090=Set Item 77 Icon Serial\nInputList3SigType2090=Serial\nInputList3Cue2091=Set Item 78 Icon Serial\nInputList3SigType2091=Serial\nInputList3Cue2092=Set Item 79 Icon Serial\nInputList3SigType2092=Serial\nInputList3Cue2093=Set Item 80 Icon Serial\nInputList3SigType2093=Serial\nInputList3Cue2094=Set Item 81 Icon Serial\nInputList3SigType2094=Serial\nInputList3Cue2095=Set Item 82 Icon Serial\nInputList3SigType2095=Serial\nInputList3Cue2096=Set Item 83 Icon Serial\nInputList3SigType2096=Serial\nInputList3Cue2097=Set Item 84 Icon Serial\nInputList3SigType2097=Serial\nInputList3Cue2098=Set Item 85 Icon Serial\nInputList3SigType2098=Serial\nInputList3Cue2099=Set Item 86 Icon Serial\nInputList3SigType2099=Serial\nInputList3Cue2100=Set Item 87 Icon Serial\nInputList3SigType2100=Serial\nInputList3Cue2101=Set Item 88 Icon Serial\nInputList3SigType2101=Serial\nInputList3Cue2102=Set Item 89 Icon Serial\nInputList3SigType2102=Serial\nInputList3Cue2103=Set Item 90 Icon Serial\nInputList3SigType2103=Serial\nInputList3Cue2104=Set Item 91 Icon Serial\nInputList3SigType2104=Serial\nInputList3Cue2105=Set Item 92 Icon Serial\nInputList3SigType2105=Serial\nInputList3Cue2106=Set Item 93 Icon Serial\nInputList3SigType2106=Serial\nInputList3Cue2107=Set Item 94 Icon Serial\nInputList3SigType2107=Serial\nInputList3Cue2108=Set Item 95 Icon Serial\nInputList3SigType2108=Serial\nInputList3Cue2109=Set Item 96 Icon Serial\nInputList3SigType2109=Serial\nInputList3Cue2110=Set Item 97 Icon Serial\nInputList3SigType2110=Serial\nInputList3Cue2111=Set Item 98 Icon Serial\nInputList3SigType2111=Serial\nInputList3Cue2112=Set Item 99 Icon Serial\nInputList3SigType2112=Serial\nInputList3Cue2113=Set Item 100 Icon Serial\nInputList3SigType2113=Serial\nInputList3Cue2114=Set Item 101 Icon Serial\nInputList3SigType2114=Serial\nInputList3Cue2115=Set Item 102 Icon Serial\nInputList3SigType2115=Serial\nInputList3Cue2116=Set Item 103 Icon Serial\nInputList3SigType2116=Serial\nInputList3Cue2117=Set Item 104 Icon Serial\nInputList3SigType2117=Serial\nInputList3Cue2118=Set Item 105 Icon Serial\nInputList3SigType2118=Serial\nInputList3Cue2119=Set Item 106 Icon Serial\nInputList3SigType2119=Serial\nInputList3Cue2120=Set Item 107 Icon Serial\nInputList3SigType2120=Serial\nInputList3Cue2121=Set Item 108 Icon Serial\nInputList3SigType2121=Serial\nInputList3Cue2122=Set Item 109 Icon Serial\nInputList3SigType2122=Serial\nInputList3Cue2123=Set Item 110 Icon Serial\nInputList3SigType2123=Serial\nInputList3Cue2124=Set Item 111 Icon Serial\nInputList3SigType2124=Serial\nInputList3Cue2125=Set Item 112 Icon Serial\nInputList3SigType2125=Serial\nInputList3Cue2126=Set Item 113 Icon Serial\nInputList3SigType2126=Serial\nInputList3Cue2127=Set Item 114 Icon Serial\nInputList3SigType2127=Serial\nInputList3Cue2128=Set Item 115 Icon Serial\nInputList3SigType2128=Serial\nInputList3Cue2129=Set Item 116 Icon Serial\nInputList3SigType2129=Serial\nInputList3Cue2130=Set Item 117 Icon Serial\nInputList3SigType2130=Serial\nInputList3Cue2131=Set Item 118 Icon Serial\nInputList3SigType2131=Serial\nInputList3Cue2132=Set Item 119 Icon Serial\nInputList3SigType2132=Serial\nInputList3Cue2133=Set Item 120 Icon Serial\nInputList3SigType2133=Serial\nInputList3Cue2134=Set Item 121 Icon Serial\nInputList3SigType2134=Serial\nInputList3Cue2135=Set Item 122 Icon Serial\nInputList3SigType2135=Serial\nInputList3Cue2136=Set Item 123 Icon Serial\nInputList3SigType2136=Serial\nInputList3Cue2137=Set Item 124 Icon Serial\nInputList3SigType2137=Serial\nInputList3Cue2138=Set Item 125 Icon Serial\nInputList3SigType2138=Serial\nInputList3Cue2139=Set Item 126 Icon Serial\nInputList3SigType2139=Serial\nInputList3Cue2140=Set Item 127 Icon Serial\nInputList3SigType2140=Serial\nInputList3Cue2141=Set Item 128 Icon Serial\nInputList3SigType2141=Serial\nInputList3Cue2142=Set Item 129 Icon Serial\nInputList3SigType2142=Serial\nInputList3Cue2143=Set Item 130 Icon Serial\nInputList3SigType2143=Serial\nInputList3Cue2144=Set Item 131 Icon Serial\nInputList3SigType2144=Serial\nInputList3Cue2145=Set Item 132 Icon Serial\nInputList3SigType2145=Serial\nInputList3Cue2146=Set Item 133 Icon Serial\nInputList3SigType2146=Serial\nInputList3Cue2147=Set Item 134 Icon Serial\nInputList3SigType2147=Serial\nInputList3Cue2148=Set Item 135 Icon Serial\nInputList3SigType2148=Serial\nInputList3Cue2149=Set Item 136 Icon Serial\nInputList3SigType2149=Serial\nInputList3Cue2150=Set Item 137 Icon Serial\nInputList3SigType2150=Serial\nInputList3Cue2151=Set Item 138 Icon Serial\nInputList3SigType2151=Serial\nInputList3Cue2152=Set Item 139 Icon Serial\nInputList3SigType2152=Serial\nInputList3Cue2153=Set Item 140 Icon Serial\nInputList3SigType2153=Serial\nInputList3Cue2154=Set Item 141 Icon Serial\nInputList3SigType2154=Serial\nInputList3Cue2155=Set Item 142 Icon Serial\nInputList3SigType2155=Serial\nInputList3Cue2156=Set Item 143 Icon Serial\nInputList3SigType2156=Serial\nInputList3Cue2157=Set Item 144 Icon Serial\nInputList3SigType2157=Serial\nInputList3Cue2158=Set Item 145 Icon Serial\nInputList3SigType2158=Serial\nInputList3Cue2159=Set Item 146 Icon Serial\nInputList3SigType2159=Serial\nInputList3Cue2160=Set Item 147 Icon Serial\nInputList3SigType2160=Serial\nInputList3Cue2161=Set Item 148 Icon Serial\nInputList3SigType2161=Serial\nInputList3Cue2162=Set Item 149 Icon Serial\nInputList3SigType2162=Serial\nInputList3Cue2163=Set Item 150 Icon Serial\nInputList3SigType2163=Serial\nInputList3Cue2164=Set Item 151 Icon Serial\nInputList3SigType2164=Serial\nInputList3Cue2165=Set Item 152 Icon Serial\nInputList3SigType2165=Serial\nInputList3Cue2166=Set Item 153 Icon Serial\nInputList3SigType2166=Serial\nInputList3Cue2167=Set Item 154 Icon Serial\nInputList3SigType2167=Serial\nInputList3Cue2168=Set Item 155 Icon Serial\nInputList3SigType2168=Serial\nInputList3Cue2169=Set Item 156 Icon Serial\nInputList3SigType2169=Serial\nInputList3Cue2170=Set Item 157 Icon Serial\nInputList3SigType2170=Serial\nInputList3Cue2171=Set Item 158 Icon Serial\nInputList3SigType2171=Serial\nInputList3Cue2172=Set Item 159 Icon Serial\nInputList3SigType2172=Serial\nInputList3Cue2173=Set Item 160 Icon Serial\nInputList3SigType2173=Serial\nInputList3Cue2174=Set Item 161 Icon Serial\nInputList3SigType2174=Serial\nInputList3Cue2175=Set Item 162 Icon Serial\nInputList3SigType2175=Serial\nInputList3Cue2176=Set Item 163 Icon Serial\nInputList3SigType2176=Serial\nInputList3Cue2177=Set Item 164 Icon Serial\nInputList3SigType2177=Serial\nInputList3Cue2178=Set Item 165 Icon Serial\nInputList3SigType2178=Serial\nInputList3Cue2179=Set Item 166 Icon Serial\nInputList3SigType2179=Serial\nInputList3Cue2180=Set Item 167 Icon Serial\nInputList3SigType2180=Serial\nInputList3Cue2181=Set Item 168 Icon Serial\nInputList3SigType2181=Serial\nInputList3Cue2182=Set Item 169 Icon Serial\nInputList3SigType2182=Serial\nInputList3Cue2183=Set Item 170 Icon Serial\nInputList3SigType2183=Serial\nInputList3Cue2184=Set Item 171 Icon Serial\nInputList3SigType2184=Serial\nInputList3Cue2185=Set Item 172 Icon Serial\nInputList3SigType2185=Serial\nInputList3Cue2186=Set Item 173 Icon Serial\nInputList3SigType2186=Serial\nInputList3Cue2187=Set Item 174 Icon Serial\nInputList3SigType2187=Serial\nInputList3Cue2188=Set Item 175 Icon Serial\nInputList3SigType2188=Serial\nInputList3Cue2189=Set Item 176 Icon Serial\nInputList3SigType2189=Serial\nInputList3Cue2190=Set Item 177 Icon Serial\nInputList3SigType2190=Serial\nInputList3Cue2191=Set Item 178 Icon Serial\nInputList3SigType2191=Serial\nInputList3Cue2192=Set Item 179 Icon Serial\nInputList3SigType2192=Serial\nInputList3Cue2193=Set Item 180 Icon Serial\nInputList3SigType2193=Serial\nInputList3Cue2194=Set Item 181 Icon Serial\nInputList3SigType2194=Serial\nInputList3Cue2195=Set Item 182 Icon Serial\nInputList3SigType2195=Serial\nInputList3Cue2196=Set Item 183 Icon Serial\nInputList3SigType2196=Serial\nInputList3Cue2197=Set Item 184 Icon Serial\nInputList3SigType2197=Serial\nInputList3Cue2198=Set Item 185 Icon Serial\nInputList3SigType2198=Serial\nInputList3Cue2199=Set Item 186 Icon Serial\nInputList3SigType2199=Serial\nInputList3Cue2200=Set Item 187 Icon Serial\nInputList3SigType2200=Serial\nInputList3Cue2201=Set Item 188 Icon Serial\nInputList3SigType2201=Serial\nInputList3Cue2202=Set Item 189 Icon Serial\nInputList3SigType2202=Serial\nInputList3Cue2203=Set Item 190 Icon Serial\nInputList3SigType2203=Serial\nInputList3Cue2204=Set Item 191 Icon Serial\nInputList3SigType2204=Serial\nInputList3Cue2205=Set Item 192 Icon Serial\nInputList3SigType2205=Serial\nInputList3Cue2206=Set Item 193 Icon Serial\nInputList3SigType2206=Serial\nInputList3Cue2207=Set Item 194 Icon Serial\nInputList3SigType2207=Serial\nInputList3Cue2208=Set Item 195 Icon Serial\nInputList3SigType2208=Serial\nInputList3Cue2209=Set Item 196 Icon Serial\nInputList3SigType2209=Serial\nInputList3Cue2210=Set Item 197 Icon Serial\nInputList3SigType2210=Serial\nInputList3Cue2211=Set Item 198 Icon Serial\nInputList3SigType2211=Serial\nInputList3Cue2212=Set Item 199 Icon Serial\nInputList3SigType2212=Serial\nInputList3Cue2213=Set Item 200 Icon Serial\nInputList3SigType2213=Serial\nInputList3Cue2214=Set Item 201 Icon Serial\nInputList3SigType2214=Serial\nInputList3Cue2215=Set Item 202 Icon Serial\nInputList3SigType2215=Serial\nInputList3Cue2216=Set Item 203 Icon Serial\nInputList3SigType2216=Serial\nInputList3Cue2217=Set Item 204 Icon Serial\nInputList3SigType2217=Serial\nInputList3Cue2218=Set Item 205 Icon Serial\nInputList3SigType2218=Serial\nInputList3Cue2219=Set Item 206 Icon Serial\nInputList3SigType2219=Serial\nInputList3Cue2220=Set Item 207 Icon Serial\nInputList3SigType2220=Serial\nInputList3Cue2221=Set Item 208 Icon Serial\nInputList3SigType2221=Serial\nInputList3Cue2222=Set Item 209 Icon Serial\nInputList3SigType2222=Serial\nInputList3Cue2223=Set Item 210 Icon Serial\nInputList3SigType2223=Serial\nInputList3Cue2224=Set Item 211 Icon Serial\nInputList3SigType2224=Serial\nInputList3Cue2225=Set Item 212 Icon Serial\nInputList3SigType2225=Serial\nInputList3Cue2226=Set Item 213 Icon Serial\nInputList3SigType2226=Serial\nInputList3Cue2227=Set Item 214 Icon Serial\nInputList3SigType2227=Serial\nInputList3Cue2228=Set Item 215 Icon Serial\nInputList3SigType2228=Serial\nInputList3Cue2229=Set Item 216 Icon Serial\nInputList3SigType2229=Serial\nInputList3Cue2230=Set Item 217 Icon Serial\nInputList3SigType2230=Serial\nInputList3Cue2231=Set Item 218 Icon Serial\nInputList3SigType2231=Serial\nInputList3Cue2232=Set Item 219 Icon Serial\nInputList3SigType2232=Serial\nInputList3Cue2233=Set Item 220 Icon Serial\nInputList3SigType2233=Serial\nInputList3Cue2234=Set Item 221 Icon Serial\nInputList3SigType2234=Serial\nInputList3Cue2235=Set Item 222 Icon Serial\nInputList3SigType2235=Serial\nInputList3Cue2236=Set Item 223 Icon Serial\nInputList3SigType2236=Serial\nInputList3Cue2237=Set Item 224 Icon Serial\nInputList3SigType2237=Serial\nInputList3Cue2238=Set Item 225 Icon Serial\nInputList3SigType2238=Serial\nInputList3Cue2239=Set Item 226 Icon Serial\nInputList3SigType2239=Serial\nInputList3Cue2240=Set Item 227 Icon Serial\nInputList3SigType2240=Serial\nInputList3Cue2241=Set Item 228 Icon Serial\nInputList3SigType2241=Serial\nInputList3Cue2242=Set Item 229 Icon Serial\nInputList3SigType2242=Serial\nInputList3Cue2243=Set Item 230 Icon Serial\nInputList3SigType2243=Serial\nInputList3Cue2244=Set Item 231 Icon Serial\nInputList3SigType2244=Serial\nInputList3Cue2245=Set Item 232 Icon Serial\nInputList3SigType2245=Serial\nInputList3Cue2246=Set Item 233 Icon Serial\nInputList3SigType2246=Serial\nInputList3Cue2247=Set Item 234 Icon Serial\nInputList3SigType2247=Serial\nInputList3Cue2248=Set Item 235 Icon Serial\nInputList3SigType2248=Serial\nInputList3Cue2249=Set Item 236 Icon Serial\nInputList3SigType2249=Serial\nInputList3Cue2250=Set Item 237 Icon Serial\nInputList3SigType2250=Serial\nInputList3Cue2251=Set Item 238 Icon Serial\nInputList3SigType2251=Serial\nInputList3Cue2252=Set Item 239 Icon Serial\nInputList3SigType2252=Serial\nInputList3Cue2253=Set Item 240 Icon Serial\nInputList3SigType2253=Serial\nInputList3Cue2254=Set Item 241 Icon Serial\nInputList3SigType2254=Serial\nInputList3Cue2255=Set Item 242 Icon Serial\nInputList3SigType2255=Serial\nInputList3Cue2256=Set Item 243 Icon Serial\nInputList3SigType2256=Serial\nInputList3Cue2257=Set Item 244 Icon Serial\nInputList3SigType2257=Serial\nInputList3Cue2258=Set Item 245 Icon Serial\nInputList3SigType2258=Serial\nInputList3Cue2259=Set Item 246 Icon Serial\nInputList3SigType2259=Serial\nInputList3Cue2260=Set Item 247 Icon Serial\nInputList3SigType2260=Serial\nInputList3Cue2261=Set Item 248 Icon Serial\nInputList3SigType2261=Serial\nInputList3Cue2262=Set Item 249 Icon Serial\nInputList3SigType2262=Serial\nInputList3Cue2263=Set Item 250 Icon Serial\nInputList3SigType2263=Serial\nInputList3Cue2264=Set Item 251 Icon Serial\nInputList3SigType2264=Serial\nInputList3Cue2265=Set Item 252 Icon Serial\nInputList3SigType2265=Serial\nInputList3Cue2266=Set Item 253 Icon Serial\nInputList3SigType2266=Serial\nInputList3Cue2267=Set Item 254 Icon Serial\nInputList3SigType2267=Serial\nInputList3Cue2268=Set Item 255 Icon Serial\nInputList3SigType2268=Serial\nInputList3Cue2269=[~UNUSED2~]\nInputList3SigType2269=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED3~]\nOutputList3SigType22=Serial\nOutputList3Cue23=[~UNUSED3~]\nOutputList3SigType23=Serial\nOutputList3Cue24=[~UNUSED3~]\nOutputList3SigType24=Serial\nOutputList3Cue25=[~UNUSED3~]\nOutputList3SigType25=Serial\nOutputList3Cue26=[~UNUSED3~]\nOutputList3SigType26=Serial\nOutputList3Cue27=[~UNUSED3~]\nOutputList3SigType27=Serial\nOutputList3Cue28=[~UNUSED3~]\nOutputList3SigType28=Serial\nOutputList3Cue29=[~UNUSED3~]\nOutputList3SigType29=Serial\nOutputList3Cue30=[~UNUSED3~]\nOutputList3SigType30=Serial\nOutputList3Cue31=[~UNUSED3~]\nOutputList3SigType31=Serial\nOutputList3Cue32=[~UNUSED3~]\nOutputList3SigType32=Serial\nOutputList3Cue33=[~UNUSED3~]\nOutputList3SigType33=Serial\nOutputList3Cue34=[~UNUSED3~]\nOutputList3SigType34=Serial\nOutputList3Cue35=[~UNUSED3~]\nOutputList3SigType35=Serial\nOutputList3Cue36=[~UNUSED3~]\nOutputList3SigType36=Serial\nOutputList3Cue37=[~UNUSED3~]\nOutputList3SigType37=Serial\nOutputList3Cue38=[~UNUSED3~]\nOutputList3SigType38=Serial\nOutputList3Cue39=[~UNUSED3~]\nOutputList3SigType39=Serial\nOutputList3Cue40=[~UNUSED3~]\nOutputList3SigType40=Serial\nOutputList3Cue41=[~UNUSED3~]\nOutputList3SigType41=Serial\nOutputList3Cue42=[~UNUSED3~]\nOutputList3SigType42=Serial\nOutputList3Cue43=[~UNUSED3~]\nOutputList3SigType43=Serial\nOutputList3Cue44=[~UNUSED3~]\nOutputList3SigType44=Serial\nOutputList3Cue45=[~UNUSED3~]\nOutputList3SigType45=Serial\nOutputList3Cue46=[~UNUSED3~]\nOutputList3SigType46=Serial\nOutputList3Cue47=[~UNUSED3~]\nOutputList3SigType47=Serial\nOutputList3Cue48=[~UNUSED3~]\nOutputList3SigType48=Serial\nOutputList3Cue49=[~UNUSED3~]\nOutputList3SigType49=Serial\nOutputList3Cue50=[~UNUSED3~]\nOutputList3SigType50=Serial\nOutputList3Cue51=[~UNUSED3~]\nOutputList3SigType51=Serial\nOutputList3Cue52=[~UNUSED3~]\nOutputList3SigType52=Serial\nOutputList3Cue53=[~UNUSED3~]\nOutputList3SigType53=Serial\nOutputList3Cue54=[~UNUSED3~]\nOutputList3SigType54=Serial\nOutputList3Cue55=[~UNUSED3~]\nOutputList3SigType55=Serial\nOutputList3Cue56=[~UNUSED3~]\nOutputList3SigType56=Serial\nOutputList3Cue57=[~UNUSED3~]\nOutputList3SigType57=Serial\nOutputList3Cue58=[~UNUSED3~]\nOutputList3SigType58=Serial\nOutputList3Cue59=[~UNUSED3~]\nOutputList3SigType59=Serial\nOutputList3Cue60=[~UNUSED3~]\nOutputList3SigType60=Serial\nOutputList3Cue61=[~UNUSED3~]\nOutputList3SigType61=Serial\nOutputList3Cue62=[~UNUSED3~]\nOutputList3SigType62=Serial\nOutputList3Cue63=[~UNUSED3~]\nOutputList3SigType63=Serial\nOutputList3Cue64=[~UNUSED3~]\nOutputList3SigType64=Serial\nOutputList3Cue65=[~UNUSED3~]\nOutputList3SigType65=Serial\nOutputList3Cue66=[~UNUSED3~]\nOutputList3SigType66=Serial\nOutputList3Cue67=[~UNUSED3~]\nOutputList3SigType67=Serial\nOutputList3Cue68=[~UNUSED3~]\nOutputList3SigType68=Serial\nOutputList3Cue69=[~UNUSED3~]\nOutputList3SigType69=Serial\nOutputList3Cue70=[~UNUSED3~]\nOutputList3SigType70=Serial\nOutputList3Cue71=[~UNUSED3~]\nOutputList3SigType71=Serial\nOutputList3Cue72=[~UNUSED3~]\nOutputList3SigType72=Serial\nOutputList3Cue73=[~UNUSED3~]\nOutputList3SigType73=Serial\nOutputList3Cue74=[~UNUSED3~]\nOutputList3SigType74=Serial\nOutputList3Cue75=[~UNUSED3~]\nOutputList3SigType75=Serial\nOutputList3Cue76=[~UNUSED3~]\nOutputList3SigType76=Serial\nOutputList3Cue77=[~UNUSED3~]\nOutputList3SigType77=Serial\nOutputList3Cue78=[~UNUSED3~]\nOutputList3SigType78=Serial\nOutputList3Cue79=[~UNUSED3~]\nOutputList3SigType79=Serial\nOutputList3Cue80=[~UNUSED3~]\nOutputList3SigType80=Serial\nOutputList3Cue81=[~UNUSED3~]\nOutputList3SigType81=Serial\nOutputList3Cue82=[~UNUSED3~]\nOutputList3SigType82=Serial\nOutputList3Cue83=[~UNUSED3~]\nOutputList3SigType83=Serial\nOutputList3Cue84=[~UNUSED3~]\nOutputList3SigType84=Serial\nOutputList3Cue85=[~UNUSED3~]\nOutputList3SigType85=Serial\nOutputList3Cue86=[~UNUSED3~]\nOutputList3SigType86=Serial\nOutputList3Cue87=[~UNUSED3~]\nOutputList3SigType87=Serial\nOutputList3Cue88=[~UNUSED3~]\nOutputList3SigType88=Serial\nOutputList3Cue89=[~UNUSED3~]\nOutputList3SigType89=Serial\nOutputList3Cue90=[~UNUSED3~]\nOutputList3SigType90=Serial\nOutputList3Cue91=[~UNUSED3~]\nOutputList3SigType91=Serial\nOutputList3Cue92=[~UNUSED3~]\nOutputList3SigType92=Serial\nOutputList3Cue93=[~UNUSED3~]\nOutputList3SigType93=Serial\nOutputList3Cue94=[~UNUSED3~]\nOutputList3SigType94=Serial\nOutputList3Cue95=[~UNUSED3~]\nOutputList3SigType95=Serial\nOutputList3Cue96=[~UNUSED3~]\nOutputList3SigType96=Serial\nOutputList3Cue97=[~UNUSED3~]\nOutputList3SigType97=Serial\nOutputList3Cue98=[~UNUSED3~]\nOutputList3SigType98=Serial\nOutputList3Cue99=[~UNUSED3~]\nOutputList3SigType99=Serial\nOutputList3Cue100=[~UNUSED3~]\nOutputList3SigType100=Serial\nOutputList3Cue101=[~UNUSED3~]\nOutputList3SigType101=Serial\nOutputList3Cue102=[~UNUSED3~]\nOutputList3SigType102=Serial\nOutputList3Cue103=[~UNUSED3~]\nOutputList3SigType103=Serial\nOutputList3Cue104=[~UNUSED3~]\nOutputList3SigType104=Serial\nOutputList3Cue105=[~UNUSED3~]\nOutputList3SigType105=Serial\nOutputList3Cue106=[~UNUSED3~]\nOutputList3SigType106=Serial\nOutputList3Cue107=[~UNUSED3~]\nOutputList3SigType107=Serial\nOutputList3Cue108=[~UNUSED3~]\nOutputList3SigType108=Serial\nOutputList3Cue109=[~UNUSED3~]\nOutputList3SigType109=Serial\nOutputList3Cue110=[~UNUSED3~]\nOutputList3SigType110=Serial\nOutputList3Cue111=[~UNUSED3~]\nOutputList3SigType111=Serial\nOutputList3Cue112=[~UNUSED3~]\nOutputList3SigType112=Serial\nOutputList3Cue113=[~UNUSED3~]\nOutputList3SigType113=Serial\nOutputList3Cue114=[~UNUSED3~]\nOutputList3SigType114=Serial\nOutputList3Cue115=[~UNUSED3~]\nOutputList3SigType115=Serial\nOutputList3Cue116=[~UNUSED3~]\nOutputList3SigType116=Serial\nOutputList3Cue117=[~UNUSED3~]\nOutputList3SigType117=Serial\nOutputList3Cue118=[~UNUSED3~]\nOutputList3SigType118=Serial\nOutputList3Cue119=[~UNUSED3~]\nOutputList3SigType119=Serial\nOutputList3Cue120=[~UNUSED3~]\nOutputList3SigType120=Serial\nOutputList3Cue121=[~UNUSED3~]\nOutputList3SigType121=Serial\nOutputList3Cue122=[~UNUSED3~]\nOutputList3SigType122=Serial\nOutputList3Cue123=[~UNUSED3~]\nOutputList3SigType123=Serial\nOutputList3Cue124=[~UNUSED3~]\nOutputList3SigType124=Serial\nOutputList3Cue125=[~UNUSED3~]\nOutputList3SigType125=Serial\nOutputList3Cue126=[~UNUSED3~]\nOutputList3SigType126=Serial\nOutputList3Cue127=[~UNUSED3~]\nOutputList3SigType127=Serial\nOutputList3Cue128=[~UNUSED3~]\nOutputList3SigType128=Serial\nOutputList3Cue129=[~UNUSED3~]\nOutputList3SigType129=Serial\nOutputList3Cue130=[~UNUSED3~]\nOutputList3SigType130=Serial\nOutputList3Cue131=[~UNUSED3~]\nOutputList3SigType131=Serial\nOutputList3Cue132=[~UNUSED3~]\nOutputList3SigType132=Serial\nOutputList3Cue133=[~UNUSED3~]\nOutputList3SigType133=Serial\nOutputList3Cue134=[~UNUSED3~]\nOutputList3SigType134=Serial\nOutputList3Cue135=[~UNUSED3~]\nOutputList3SigType135=Serial\nOutputList3Cue136=[~UNUSED3~]\nOutputList3SigType136=Serial\nOutputList3Cue137=[~UNUSED3~]\nOutputList3SigType137=Serial\nOutputList3Cue138=[~UNUSED3~]\nOutputList3SigType138=Serial\nOutputList3Cue139=[~UNUSED3~]\nOutputList3SigType139=Serial\nOutputList3Cue140=[~UNUSED3~]\nOutputList3SigType140=Serial\nOutputList3Cue141=[~UNUSED3~]\nOutputList3SigType141=Serial\nOutputList3Cue142=[~UNUSED3~]\nOutputList3SigType142=Serial\nOutputList3Cue143=[~UNUSED3~]\nOutputList3SigType143=Serial\nOutputList3Cue144=[~UNUSED3~]\nOutputList3SigType144=Serial\nOutputList3Cue145=[~UNUSED3~]\nOutputList3SigType145=Serial\nOutputList3Cue146=[~UNUSED3~]\nOutputList3SigType146=Serial\nOutputList3Cue147=[~UNUSED3~]\nOutputList3SigType147=Serial\nOutputList3Cue148=[~UNUSED3~]\nOutputList3SigType148=Serial\nOutputList3Cue149=[~UNUSED3~]\nOutputList3SigType149=Serial\nOutputList3Cue150=[~UNUSED3~]\nOutputList3SigType150=Serial\nOutputList3Cue151=[~UNUSED3~]\nOutputList3SigType151=Serial\nOutputList3Cue152=[~UNUSED3~]\nOutputList3SigType152=Serial\nOutputList3Cue153=[~UNUSED3~]\nOutputList3SigType153=Serial\nOutputList3Cue154=[~UNUSED3~]\nOutputList3SigType154=Serial\nOutputList3Cue155=[~UNUSED3~]\nOutputList3SigType155=Serial\nOutputList3Cue156=[~UNUSED3~]\nOutputList3SigType156=Serial\nOutputList3Cue157=[~UNUSED3~]\nOutputList3SigType157=Serial\nOutputList3Cue158=[~UNUSED3~]\nOutputList3SigType158=Serial\nOutputList3Cue159=[~UNUSED3~]\nOutputList3SigType159=Serial\nOutputList3Cue160=[~UNUSED3~]\nOutputList3SigType160=Serial\nOutputList3Cue161=[~UNUSED3~]\nOutputList3SigType161=Serial\nOutputList3Cue162=[~UNUSED3~]\nOutputList3SigType162=Serial\nOutputList3Cue163=[~UNUSED3~]\nOutputList3SigType163=Serial\nOutputList3Cue164=[~UNUSED3~]\nOutputList3SigType164=Serial\nOutputList3Cue165=[~UNUSED3~]\nOutputList3SigType165=Serial\nOutputList3Cue166=[~UNUSED3~]\nOutputList3SigType166=Serial\nOutputList3Cue167=[~UNUSED3~]\nOutputList3SigType167=Serial\nOutputList3Cue168=[~UNUSED3~]\nOutputList3SigType168=Serial\nOutputList3Cue169=[~UNUSED3~]\nOutputList3SigType169=Serial\nOutputList3Cue170=[~UNUSED3~]\nOutputList3SigType170=Serial\nOutputList3Cue171=[~UNUSED3~]\nOutputList3SigType171=Serial\nOutputList3Cue172=[~UNUSED3~]\nOutputList3SigType172=Serial\nOutputList3Cue173=[~UNUSED3~]\nOutputList3SigType173=Serial\nOutputList3Cue174=[~UNUSED3~]\nOutputList3SigType174=Serial\nOutputList3Cue175=[~UNUSED3~]\nOutputList3SigType175=Serial\nOutputList3Cue176=[~UNUSED3~]\nOutputList3SigType176=Serial\nOutputList3Cue177=[~UNUSED3~]\nOutputList3SigType177=Serial\nOutputList3Cue178=[~UNUSED3~]\nOutputList3SigType178=Serial\nOutputList3Cue179=[~UNUSED3~]\nOutputList3SigType179=Serial\nOutputList3Cue180=[~UNUSED3~]\nOutputList3SigType180=Serial\nOutputList3Cue181=[~UNUSED3~]\nOutputList3SigType181=Serial\nOutputList3Cue182=[~UNUSED3~]\nOutputList3SigType182=Serial\nOutputList3Cue183=[~UNUSED3~]\nOutputList3SigType183=Serial\nOutputList3Cue184=[~UNUSED3~]\nOutputList3SigType184=Serial\nOutputList3Cue185=[~UNUSED3~]\nOutputList3SigType185=Serial\nOutputList3Cue186=[~UNUSED3~]\nOutputList3SigType186=Serial\nOutputList3Cue187=[~UNUSED3~]\nOutputList3SigType187=Serial\nOutputList3Cue188=[~UNUSED3~]\nOutputList3SigType188=Serial\nOutputList3Cue189=[~UNUSED3~]\nOutputList3SigType189=Serial\nOutputList3Cue190=[~UNUSED3~]\nOutputList3SigType190=Serial\nOutputList3Cue191=[~UNUSED3~]\nOutputList3SigType191=Serial\nOutputList3Cue192=[~UNUSED3~]\nOutputList3SigType192=Serial\nOutputList3Cue193=[~UNUSED3~]\nOutputList3SigType193=Serial\nOutputList3Cue194=[~UNUSED3~]\nOutputList3SigType194=Serial\nOutputList3Cue195=[~UNUSED3~]\nOutputList3SigType195=Serial\nOutputList3Cue196=[~UNUSED3~]\nOutputList3SigType196=Serial\nOutputList3Cue197=[~UNUSED3~]\nOutputList3SigType197=Serial\nOutputList3Cue198=[~UNUSED3~]\nOutputList3SigType198=Serial\nOutputList3Cue199=[~UNUSED3~]\nOutputList3SigType199=Serial\nOutputList3Cue200=[~UNUSED3~]\nOutputList3SigType200=Serial\nOutputList3Cue201=[~UNUSED3~]\nOutputList3SigType201=Serial\nOutputList3Cue202=[~UNUSED3~]\nOutputList3SigType202=Serial\nOutputList3Cue203=[~UNUSED3~]\nOutputList3SigType203=Serial\nOutputList3Cue204=[~UNUSED3~]\nOutputList3SigType204=Serial\nOutputList3Cue205=[~UNUSED3~]\nOutputList3SigType205=Serial\nOutputList3Cue206=[~UNUSED3~]\nOutputList3SigType206=Serial\nOutputList3Cue207=[~UNUSED3~]\nOutputList3SigType207=Serial\nOutputList3Cue208=[~UNUSED3~]\nOutputList3SigType208=Serial\nOutputList3Cue209=[~UNUSED3~]\nOutputList3SigType209=Serial\nOutputList3Cue210=[~UNUSED3~]\nOutputList3SigType210=Serial\nOutputList3Cue211=[~UNUSED3~]\nOutputList3SigType211=Serial\nOutputList3Cue212=[~UNUSED3~]\nOutputList3SigType212=Serial\nOutputList3Cue213=[~UNUSED3~]\nOutputList3SigType213=Serial\nOutputList3Cue214=[~UNUSED3~]\nOutputList3SigType214=Serial\nOutputList3Cue215=[~UNUSED3~]\nOutputList3SigType215=Serial\nOutputList3Cue216=[~UNUSED3~]\nOutputList3SigType216=Serial\nOutputList3Cue217=[~UNUSED3~]\nOutputList3SigType217=Serial\nOutputList3Cue218=[~UNUSED3~]\nOutputList3SigType218=Serial\nOutputList3Cue219=[~UNUSED3~]\nOutputList3SigType219=Serial\nOutputList3Cue220=[~UNUSED3~]\nOutputList3SigType220=Serial\nOutputList3Cue221=[~UNUSED3~]\nOutputList3SigType221=Serial\nOutputList3Cue222=[~UNUSED3~]\nOutputList3SigType222=Serial\nOutputList3Cue223=[~UNUSED3~]\nOutputList3SigType223=Serial\nOutputList3Cue224=[~UNUSED3~]\nOutputList3SigType224=Serial\nOutputList3Cue225=[~UNUSED3~]\nOutputList3SigType225=Serial\nOutputList3Cue226=[~UNUSED3~]\nOutputList3SigType226=Serial\nOutputList3Cue227=[~UNUSED3~]\nOutputList3SigType227=Serial\nOutputList3Cue228=[~UNUSED3~]\nOutputList3SigType228=Serial\nOutputList3Cue229=[~UNUSED3~]\nOutputList3SigType229=Serial\nOutputList3Cue230=[~UNUSED3~]\nOutputList3SigType230=Serial\nOutputList3Cue231=[~UNUSED3~]\nOutputList3SigType231=Serial\nOutputList3Cue232=[~UNUSED3~]\nOutputList3SigType232=Serial\nOutputList3Cue233=[~UNUSED3~]\nOutputList3SigType233=Serial\nOutputList3Cue234=[~UNUSED3~]\nOutputList3SigType234=Serial\nOutputList3Cue235=[~UNUSED3~]\nOutputList3SigType235=Serial\nOutputList3Cue236=[~UNUSED3~]\nOutputList3SigType236=Serial\nOutputList3Cue237=[~UNUSED3~]\nOutputList3SigType237=Serial\nOutputList3Cue238=[~UNUSED3~]\nOutputList3SigType238=Serial\nOutputList3Cue239=[~UNUSED3~]\nOutputList3SigType239=Serial\nOutputList3Cue240=[~UNUSED3~]\nOutputList3SigType240=Serial\nOutputList3Cue241=[~UNUSED3~]\nOutputList3SigType241=Serial\nOutputList3Cue242=[~UNUSED3~]\nOutputList3SigType242=Serial\nOutputList3Cue243=[~UNUSED3~]\nOutputList3SigType243=Serial\nOutputList3Cue244=[~UNUSED3~]\nOutputList3SigType244=Serial\nOutputList3Cue245=[~UNUSED3~]\nOutputList3SigType245=Serial\nOutputList3Cue246=[~UNUSED3~]\nOutputList3SigType246=Serial\nOutputList3Cue247=[~UNUSED3~]\nOutputList3SigType247=Serial\nOutputList3Cue248=[~UNUSED3~]\nOutputList3SigType248=Serial\nOutputList3Cue249=[~UNUSED3~]\nOutputList3SigType249=Serial\nOutputList3Cue250=[~UNUSED3~]\nOutputList3SigType250=Serial\nOutputList3Cue251=[~UNUSED3~]\nOutputList3SigType251=Serial\nOutputList3Cue252=[~UNUSED3~]\nOutputList3SigType252=Serial\nOutputList3Cue253=[~UNUSED3~]\nOutputList3SigType253=Serial\nOutputList3Cue254=[~UNUSED3~]\nOutputList3SigType254=Serial\nOutputList3Cue255=[~UNUSED3~]\nOutputList3SigType255=Serial\nOutputList3Cue256=[~UNUSED3~]\nOutputList3SigType256=Serial\nOutputList3Cue257=[~UNUSED3~]\nOutputList3SigType257=Serial\nOutputList3Cue258=[~UNUSED3~]\nOutputList3SigType258=Serial\nOutputList3Cue259=[~UNUSED3~]\nOutputList3SigType259=Serial\nOutputList3Cue260=[~UNUSED3~]\nOutputList3SigType260=Serial\nOutputList3Cue261=[~UNUSED3~]\nOutputList3SigType261=Serial\nOutputList3Cue262=[~UNUSED3~]\nOutputList3SigType262=Serial\nOutputList3Cue263=[~UNUSED3~]\nOutputList3SigType263=Serial\nOutputList3Cue264=[~UNUSED3~]\nOutputList3SigType264=Serial\nOutputList3Cue265=[~UNUSED3~]\nOutputList3SigType265=Serial\nOutputList3Cue266=[~UNUSED3~]\nOutputList3SigType266=Serial\nOutputList3Cue267=[~UNUSED2~]\nOutputList3SigType267=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED3~]\nOutputList3SigType2024=Serial\nOutputList3Cue2025=[~UNUSED3~]\nOutputList3SigType2025=Serial\nOutputList3Cue2026=[~UNUSED3~]\nOutputList3SigType2026=Serial\nOutputList3Cue2027=[~UNUSED3~]\nOutputList3SigType2027=Serial\nOutputList3Cue2028=[~UNUSED3~]\nOutputList3SigType2028=Serial\nOutputList3Cue2029=[~UNUSED3~]\nOutputList3SigType2029=Serial\nOutputList3Cue2030=[~UNUSED3~]\nOutputList3SigType2030=Serial\nOutputList3Cue2031=[~UNUSED3~]\nOutputList3SigType2031=Serial\nOutputList3Cue2032=[~UNUSED3~]\nOutputList3SigType2032=Serial\nOutputList3Cue2033=[~UNUSED3~]\nOutputList3SigType2033=Serial\nOutputList3Cue2034=[~UNUSED3~]\nOutputList3SigType2034=Serial\nOutputList3Cue2035=[~UNUSED3~]\nOutputList3SigType2035=Serial\nOutputList3Cue2036=[~UNUSED3~]\nOutputList3SigType2036=Serial\nOutputList3Cue2037=[~UNUSED3~]\nOutputList3SigType2037=Serial\nOutputList3Cue2038=[~UNUSED3~]\nOutputList3SigType2038=Serial\nOutputList3Cue2039=[~UNUSED3~]\nOutputList3SigType2039=Serial\nOutputList3Cue2040=[~UNUSED3~]\nOutputList3SigType2040=Serial\nOutputList3Cue2041=[~UNUSED3~]\nOutputList3SigType2041=Serial\nOutputList3Cue2042=[~UNUSED3~]\nOutputList3SigType2042=Serial\nOutputList3Cue2043=[~UNUSED3~]\nOutputList3SigType2043=Serial\nOutputList3Cue2044=[~UNUSED3~]\nOutputList3SigType2044=Serial\nOutputList3Cue2045=[~UNUSED3~]\nOutputList3SigType2045=Serial\nOutputList3Cue2046=[~UNUSED3~]\nOutputList3SigType2046=Serial\nOutputList3Cue2047=[~UNUSED3~]\nOutputList3SigType2047=Serial\nOutputList3Cue2048=[~UNUSED3~]\nOutputList3SigType2048=Serial\nOutputList3Cue2049=[~UNUSED3~]\nOutputList3SigType2049=Serial\nOutputList3Cue2050=[~UNUSED3~]\nOutputList3SigType2050=Serial\nOutputList3Cue2051=[~UNUSED3~]\nOutputList3SigType2051=Serial\nOutputList3Cue2052=[~UNUSED3~]\nOutputList3SigType2052=Serial\nOutputList3Cue2053=[~UNUSED3~]\nOutputList3SigType2053=Serial\nOutputList3Cue2054=[~UNUSED3~]\nOutputList3SigType2054=Serial\nOutputList3Cue2055=[~UNUSED3~]\nOutputList3SigType2055=Serial\nOutputList3Cue2056=[~UNUSED3~]\nOutputList3SigType2056=Serial\nOutputList3Cue2057=[~UNUSED3~]\nOutputList3SigType2057=Serial\nOutputList3Cue2058=[~UNUSED3~]\nOutputList3SigType2058=Serial\nOutputList3Cue2059=[~UNUSED3~]\nOutputList3SigType2059=Serial\nOutputList3Cue2060=[~UNUSED3~]\nOutputList3SigType2060=Serial\nOutputList3Cue2061=[~UNUSED3~]\nOutputList3SigType2061=Serial\nOutputList3Cue2062=[~UNUSED3~]\nOutputList3SigType2062=Serial\nOutputList3Cue2063=[~UNUSED3~]\nOutputList3SigType2063=Serial\nOutputList3Cue2064=[~UNUSED3~]\nOutputList3SigType2064=Serial\nOutputList3Cue2065=[~UNUSED3~]\nOutputList3SigType2065=Serial\nOutputList3Cue2066=[~UNUSED3~]\nOutputList3SigType2066=Serial\nOutputList3Cue2067=[~UNUSED3~]\nOutputList3SigType2067=Serial\nOutputList3Cue2068=[~UNUSED3~]\nOutputList3SigType2068=Serial\nOutputList3Cue2069=[~UNUSED3~]\nOutputList3SigType2069=Serial\nOutputList3Cue2070=[~UNUSED3~]\nOutputList3SigType2070=Serial\nOutputList3Cue2071=[~UNUSED3~]\nOutputList3SigType2071=Serial\nOutputList3Cue2072=[~UNUSED3~]\nOutputList3SigType2072=Serial\nOutputList3Cue2073=[~UNUSED3~]\nOutputList3SigType2073=Serial\nOutputList3Cue2074=[~UNUSED3~]\nOutputList3SigType2074=Serial\nOutputList3Cue2075=[~UNUSED3~]\nOutputList3SigType2075=Serial\nOutputList3Cue2076=[~UNUSED3~]\nOutputList3SigType2076=Serial\nOutputList3Cue2077=[~UNUSED3~]\nOutputList3SigType2077=Serial\nOutputList3Cue2078=[~UNUSED3~]\nOutputList3SigType2078=Serial\nOutputList3Cue2079=[~UNUSED3~]\nOutputList3SigType2079=Serial\nOutputList3Cue2080=[~UNUSED3~]\nOutputList3SigType2080=Serial\nOutputList3Cue2081=[~UNUSED3~]\nOutputList3SigType2081=Serial\nOutputList3Cue2082=[~UNUSED3~]\nOutputList3SigType2082=Serial\nOutputList3Cue2083=[~UNUSED3~]\nOutputList3SigType2083=Serial\nOutputList3Cue2084=[~UNUSED3~]\nOutputList3SigType2084=Serial\nOutputList3Cue2085=[~UNUSED3~]\nOutputList3SigType2085=Serial\nOutputList3Cue2086=[~UNUSED3~]\nOutputList3SigType2086=Serial\nOutputList3Cue2087=[~UNUSED3~]\nOutputList3SigType2087=Serial\nOutputList3Cue2088=[~UNUSED3~]\nOutputList3SigType2088=Serial\nOutputList3Cue2089=[~UNUSED3~]\nOutputList3SigType2089=Serial\nOutputList3Cue2090=[~UNUSED3~]\nOutputList3SigType2090=Serial\nOutputList3Cue2091=[~UNUSED3~]\nOutputList3SigType2091=Serial\nOutputList3Cue2092=[~UNUSED3~]\nOutputList3SigType2092=Serial\nOutputList3Cue2093=[~UNUSED3~]\nOutputList3SigType2093=Serial\nOutputList3Cue2094=[~UNUSED3~]\nOutputList3SigType2094=Serial\nOutputList3Cue2095=[~UNUSED3~]\nOutputList3SigType2095=Serial\nOutputList3Cue2096=[~UNUSED3~]\nOutputList3SigType2096=Serial\nOutputList3Cue2097=[~UNUSED3~]\nOutputList3SigType2097=Serial\nOutputList3Cue2098=[~UNUSED3~]\nOutputList3SigType2098=Serial\nOutputList3Cue2099=[~UNUSED3~]\nOutputList3SigType2099=Serial\nOutputList3Cue2100=[~UNUSED3~]\nOutputList3SigType2100=Serial\nOutputList3Cue2101=[~UNUSED3~]\nOutputList3SigType2101=Serial\nOutputList3Cue2102=[~UNUSED3~]\nOutputList3SigType2102=Serial\nOutputList3Cue2103=[~UNUSED3~]\nOutputList3SigType2103=Serial\nOutputList3Cue2104=[~UNUSED3~]\nOutputList3SigType2104=Serial\nOutputList3Cue2105=[~UNUSED3~]\nOutputList3SigType2105=Serial\nOutputList3Cue2106=[~UNUSED3~]\nOutputList3SigType2106=Serial\nOutputList3Cue2107=[~UNUSED3~]\nOutputList3SigType2107=Serial\nOutputList3Cue2108=[~UNUSED3~]\nOutputList3SigType2108=Serial\nOutputList3Cue2109=[~UNUSED3~]\nOutputList3SigType2109=Serial\nOutputList3Cue2110=[~UNUSED3~]\nOutputList3SigType2110=Serial\nOutputList3Cue2111=[~UNUSED3~]\nOutputList3SigType2111=Serial\nOutputList3Cue2112=[~UNUSED3~]\nOutputList3SigType2112=Serial\nOutputList3Cue2113=[~UNUSED3~]\nOutputList3SigType2113=Serial\nOutputList3Cue2114=[~UNUSED3~]\nOutputList3SigType2114=Serial\nOutputList3Cue2115=[~UNUSED3~]\nOutputList3SigType2115=Serial\nOutputList3Cue2116=[~UNUSED3~]\nOutputList3SigType2116=Serial\nOutputList3Cue2117=[~UNUSED3~]\nOutputList3SigType2117=Serial\nOutputList3Cue2118=[~UNUSED3~]\nOutputList3SigType2118=Serial\nOutputList3Cue2119=[~UNUSED3~]\nOutputList3SigType2119=Serial\nOutputList3Cue2120=[~UNUSED3~]\nOutputList3SigType2120=Serial\nOutputList3Cue2121=[~UNUSED3~]\nOutputList3SigType2121=Serial\nOutputList3Cue2122=[~UNUSED3~]\nOutputList3SigType2122=Serial\nOutputList3Cue2123=[~UNUSED3~]\nOutputList3SigType2123=Serial\nOutputList3Cue2124=[~UNUSED3~]\nOutputList3SigType2124=Serial\nOutputList3Cue2125=[~UNUSED3~]\nOutputList3SigType2125=Serial\nOutputList3Cue2126=[~UNUSED3~]\nOutputList3SigType2126=Serial\nOutputList3Cue2127=[~UNUSED3~]\nOutputList3SigType2127=Serial\nOutputList3Cue2128=[~UNUSED3~]\nOutputList3SigType2128=Serial\nOutputList3Cue2129=[~UNUSED3~]\nOutputList3SigType2129=Serial\nOutputList3Cue2130=[~UNUSED3~]\nOutputList3SigType2130=Serial\nOutputList3Cue2131=[~UNUSED3~]\nOutputList3SigType2131=Serial\nOutputList3Cue2132=[~UNUSED3~]\nOutputList3SigType2132=Serial\nOutputList3Cue2133=[~UNUSED3~]\nOutputList3SigType2133=Serial\nOutputList3Cue2134=[~UNUSED3~]\nOutputList3SigType2134=Serial\nOutputList3Cue2135=[~UNUSED3~]\nOutputList3SigType2135=Serial\nOutputList3Cue2136=[~UNUSED3~]\nOutputList3SigType2136=Serial\nOutputList3Cue2137=[~UNUSED3~]\nOutputList3SigType2137=Serial\nOutputList3Cue2138=[~UNUSED3~]\nOutputList3SigType2138=Serial\nOutputList3Cue2139=[~UNUSED3~]\nOutputList3SigType2139=Serial\nOutputList3Cue2140=[~UNUSED3~]\nOutputList3SigType2140=Serial\nOutputList3Cue2141=[~UNUSED3~]\nOutputList3SigType2141=Serial\nOutputList3Cue2142=[~UNUSED3~]\nOutputList3SigType2142=Serial\nOutputList3Cue2143=[~UNUSED3~]\nOutputList3SigType2143=Serial\nOutputList3Cue2144=[~UNUSED3~]\nOutputList3SigType2144=Serial\nOutputList3Cue2145=[~UNUSED3~]\nOutputList3SigType2145=Serial\nOutputList3Cue2146=[~UNUSED3~]\nOutputList3SigType2146=Serial\nOutputList3Cue2147=[~UNUSED3~]\nOutputList3SigType2147=Serial\nOutputList3Cue2148=[~UNUSED3~]\nOutputList3SigType2148=Serial\nOutputList3Cue2149=[~UNUSED3~]\nOutputList3SigType2149=Serial\nOutputList3Cue2150=[~UNUSED3~]\nOutputList3SigType2150=Serial\nOutputList3Cue2151=[~UNUSED3~]\nOutputList3SigType2151=Serial\nOutputList3Cue2152=[~UNUSED3~]\nOutputList3SigType2152=Serial\nOutputList3Cue2153=[~UNUSED3~]\nOutputList3SigType2153=Serial\nOutputList3Cue2154=[~UNUSED3~]\nOutputList3SigType2154=Serial\nOutputList3Cue2155=[~UNUSED3~]\nOutputList3SigType2155=Serial\nOutputList3Cue2156=[~UNUSED3~]\nOutputList3SigType2156=Serial\nOutputList3Cue2157=[~UNUSED3~]\nOutputList3SigType2157=Serial\nOutputList3Cue2158=[~UNUSED3~]\nOutputList3SigType2158=Serial\nOutputList3Cue2159=[~UNUSED3~]\nOutputList3SigType2159=Serial\nOutputList3Cue2160=[~UNUSED3~]\nOutputList3SigType2160=Serial\nOutputList3Cue2161=[~UNUSED3~]\nOutputList3SigType2161=Serial\nOutputList3Cue2162=[~UNUSED3~]\nOutputList3SigType2162=Serial\nOutputList3Cue2163=[~UNUSED3~]\nOutputList3SigType2163=Serial\nOutputList3Cue2164=[~UNUSED3~]\nOutputList3SigType2164=Serial\nOutputList3Cue2165=[~UNUSED3~]\nOutputList3SigType2165=Serial\nOutputList3Cue2166=[~UNUSED3~]\nOutputList3SigType2166=Serial\nOutputList3Cue2167=[~UNUSED3~]\nOutputList3SigType2167=Serial\nOutputList3Cue2168=[~UNUSED3~]\nOutputList3SigType2168=Serial\nOutputList3Cue2169=[~UNUSED3~]\nOutputList3SigType2169=Serial\nOutputList3Cue2170=[~UNUSED3~]\nOutputList3SigType2170=Serial\nOutputList3Cue2171=[~UNUSED3~]\nOutputList3SigType2171=Serial\nOutputList3Cue2172=[~UNUSED3~]\nOutputList3SigType2172=Serial\nOutputList3Cue2173=[~UNUSED3~]\nOutputList3SigType2173=Serial\nOutputList3Cue2174=[~UNUSED3~]\nOutputList3SigType2174=Serial\nOutputList3Cue2175=[~UNUSED3~]\nOutputList3SigType2175=Serial\nOutputList3Cue2176=[~UNUSED3~]\nOutputList3SigType2176=Serial\nOutputList3Cue2177=[~UNUSED3~]\nOutputList3SigType2177=Serial\nOutputList3Cue2178=[~UNUSED3~]\nOutputList3SigType2178=Serial\nOutputList3Cue2179=[~UNUSED3~]\nOutputList3SigType2179=Serial\nOutputList3Cue2180=[~UNUSED3~]\nOutputList3SigType2180=Serial\nOutputList3Cue2181=[~UNUSED3~]\nOutputList3SigType2181=Serial\nOutputList3Cue2182=[~UNUSED3~]\nOutputList3SigType2182=Serial\nOutputList3Cue2183=[~UNUSED3~]\nOutputList3SigType2183=Serial\nOutputList3Cue2184=[~UNUSED3~]\nOutputList3SigType2184=Serial\nOutputList3Cue2185=[~UNUSED3~]\nOutputList3SigType2185=Serial\nOutputList3Cue2186=[~UNUSED3~]\nOutputList3SigType2186=Serial\nOutputList3Cue2187=[~UNUSED3~]\nOutputList3SigType2187=Serial\nOutputList3Cue2188=[~UNUSED3~]\nOutputList3SigType2188=Serial\nOutputList3Cue2189=[~UNUSED3~]\nOutputList3SigType2189=Serial\nOutputList3Cue2190=[~UNUSED3~]\nOutputList3SigType2190=Serial\nOutputList3Cue2191=[~UNUSED3~]\nOutputList3SigType2191=Serial\nOutputList3Cue2192=[~UNUSED3~]\nOutputList3SigType2192=Serial\nOutputList3Cue2193=[~UNUSED3~]\nOutputList3SigType2193=Serial\nOutputList3Cue2194=[~UNUSED3~]\nOutputList3SigType2194=Serial\nOutputList3Cue2195=[~UNUSED3~]\nOutputList3SigType2195=Serial\nOutputList3Cue2196=[~UNUSED3~]\nOutputList3SigType2196=Serial\nOutputList3Cue2197=[~UNUSED3~]\nOutputList3SigType2197=Serial\nOutputList3Cue2198=[~UNUSED3~]\nOutputList3SigType2198=Serial\nOutputList3Cue2199=[~UNUSED3~]\nOutputList3SigType2199=Serial\nOutputList3Cue2200=[~UNUSED3~]\nOutputList3SigType2200=Serial\nOutputList3Cue2201=[~UNUSED3~]\nOutputList3SigType2201=Serial\nOutputList3Cue2202=[~UNUSED3~]\nOutputList3SigType2202=Serial\nOutputList3Cue2203=[~UNUSED3~]\nOutputList3SigType2203=Serial\nOutputList3Cue2204=[~UNUSED3~]\nOutputList3SigType2204=Serial\nOutputList3Cue2205=[~UNUSED3~]\nOutputList3SigType2205=Serial\nOutputList3Cue2206=[~UNUSED3~]\nOutputList3SigType2206=Serial\nOutputList3Cue2207=[~UNUSED3~]\nOutputList3SigType2207=Serial\nOutputList3Cue2208=[~UNUSED3~]\nOutputList3SigType2208=Serial\nOutputList3Cue2209=[~UNUSED3~]\nOutputList3SigType2209=Serial\nOutputList3Cue2210=[~UNUSED3~]\nOutputList3SigType2210=Serial\nOutputList3Cue2211=[~UNUSED3~]\nOutputList3SigType2211=Serial\nOutputList3Cue2212=[~UNUSED3~]\nOutputList3SigType2212=Serial\nOutputList3Cue2213=[~UNUSED3~]\nOutputList3SigType2213=Serial\nOutputList3Cue2214=[~UNUSED3~]\nOutputList3SigType2214=Serial\nOutputList3Cue2215=[~UNUSED3~]\nOutputList3SigType2215=Serial\nOutputList3Cue2216=[~UNUSED3~]\nOutputList3SigType2216=Serial\nOutputList3Cue2217=[~UNUSED3~]\nOutputList3SigType2217=Serial\nOutputList3Cue2218=[~UNUSED3~]\nOutputList3SigType2218=Serial\nOutputList3Cue2219=[~UNUSED3~]\nOutputList3SigType2219=Serial\nOutputList3Cue2220=[~UNUSED3~]\nOutputList3SigType2220=Serial\nOutputList3Cue2221=[~UNUSED3~]\nOutputList3SigType2221=Serial\nOutputList3Cue2222=[~UNUSED3~]\nOutputList3SigType2222=Serial\nOutputList3Cue2223=[~UNUSED3~]\nOutputList3SigType2223=Serial\nOutputList3Cue2224=[~UNUSED3~]\nOutputList3SigType2224=Serial\nOutputList3Cue2225=[~UNUSED3~]\nOutputList3SigType2225=Serial\nOutputList3Cue2226=[~UNUSED3~]\nOutputList3SigType2226=Serial\nOutputList3Cue2227=[~UNUSED3~]\nOutputList3SigType2227=Serial\nOutputList3Cue2228=[~UNUSED3~]\nOutputList3SigType2228=Serial\nOutputList3Cue2229=[~UNUSED3~]\nOutputList3SigType2229=Serial\nOutputList3Cue2230=[~UNUSED3~]\nOutputList3SigType2230=Serial\nOutputList3Cue2231=[~UNUSED3~]\nOutputList3SigType2231=Serial\nOutputList3Cue2232=[~UNUSED3~]\nOutputList3SigType2232=Serial\nOutputList3Cue2233=[~UNUSED3~]\nOutputList3SigType2233=Serial\nOutputList3Cue2234=[~UNUSED3~]\nOutputList3SigType2234=Serial\nOutputList3Cue2235=[~UNUSED3~]\nOutputList3SigType2235=Serial\nOutputList3Cue2236=[~UNUSED3~]\nOutputList3SigType2236=Serial\nOutputList3Cue2237=[~UNUSED3~]\nOutputList3SigType2237=Serial\nOutputList3Cue2238=[~UNUSED3~]\nOutputList3SigType2238=Serial\nOutputList3Cue2239=[~UNUSED3~]\nOutputList3SigType2239=Serial\nOutputList3Cue2240=[~UNUSED3~]\nOutputList3SigType2240=Serial\nOutputList3Cue2241=[~UNUSED3~]\nOutputList3SigType2241=Serial\nOutputList3Cue2242=[~UNUSED3~]\nOutputList3SigType2242=Serial\nOutputList3Cue2243=[~UNUSED3~]\nOutputList3SigType2243=Serial\nOutputList3Cue2244=[~UNUSED3~]\nOutputList3SigType2244=Serial\nOutputList3Cue2245=[~UNUSED3~]\nOutputList3SigType2245=Serial\nOutputList3Cue2246=[~UNUSED3~]\nOutputList3SigType2246=Serial\nOutputList3Cue2247=[~UNUSED3~]\nOutputList3SigType2247=Serial\nOutputList3Cue2248=[~UNUSED3~]\nOutputList3SigType2248=Serial\nOutputList3Cue2249=[~UNUSED3~]\nOutputList3SigType2249=Serial\nOutputList3Cue2250=[~UNUSED3~]\nOutputList3SigType2250=Serial\nOutputList3Cue2251=[~UNUSED3~]\nOutputList3SigType2251=Serial\nOutputList3Cue2252=[~UNUSED3~]\nOutputList3SigType2252=Serial\nOutputList3Cue2253=[~UNUSED3~]\nOutputList3SigType2253=Serial\nOutputList3Cue2254=[~UNUSED3~]\nOutputList3SigType2254=Serial\nOutputList3Cue2255=[~UNUSED3~]\nOutputList3SigType2255=Serial\nOutputList3Cue2256=[~UNUSED3~]\nOutputList3SigType2256=Serial\nOutputList3Cue2257=[~UNUSED3~]\nOutputList3SigType2257=Serial\nOutputList3Cue2258=[~UNUSED3~]\nOutputList3SigType2258=Serial\nOutputList3Cue2259=[~UNUSED3~]\nOutputList3SigType2259=Serial\nOutputList3Cue2260=[~UNUSED3~]\nOutputList3SigType2260=Serial\nOutputList3Cue2261=[~UNUSED3~]\nOutputList3SigType2261=Serial\nOutputList3Cue2262=[~UNUSED3~]\nOutputList3SigType2262=Serial\nOutputList3Cue2263=[~UNUSED3~]\nOutputList3SigType2263=Serial\nOutputList3Cue2264=[~UNUSED3~]\nOutputList3SigType2264=Serial\nOutputList3Cue2265=[~UNUSED3~]\nOutputList3SigType2265=Serial\nOutputList3Cue2266=[~UNUSED3~]\nOutputList3SigType2266=Serial\nOutputList3Cue2267=[~UNUSED3~]\nOutputList3SigType2267=Serial\nOutputList3Cue2268=[~UNUSED3~]\nOutputList3SigType2268=Serial\nOutputList3Cue2269=[~UNUSED2~]\nOutputList3SigType2269=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=2\nCedH=2\nSmartObjId=1202d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=2\nTp=1\nHD=TRUE\nDV=1202d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=2\nName=PepperDash Essentials TSW-560_[D.VC] Directory_Dynamic Button List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\nHint=VC DTMF Keypad (Smart Object ID=1201)\nCode=3\nSGControlType=Simple Keypad\nSGControlName=VC DTMF Keypad\nGUID=B4D53525-26EF-4326-8234-1D53F6E9BF04\nSmplCName=PepperDash Essentials TSW-560_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=*\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=/#\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=3\nCedH=3\nSmartObjId=1201d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=3\nTp=1\nHD=TRUE\nDV=1201d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=3\nName=PepperDash Essentials TSW-560_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[Z.Dialogs] PIN_PIN Keypad.ced\nHint=PIN Keypad (Smart Object ID=3903)\nCode=4\nSGControlType=Simple Keypad\nSGControlName=PIN Keypad\nGUID=D85E5DFB-DE45-45FE-93C5-2FA177B68BFB\nSmplCName=PepperDash Essentials TSW-560_[Z.Dialogs] PIN_PIN Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=Misc_1\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=Misc_2\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=4\nCedH=4\nSmartObjId=3903d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=4\nTp=1\nHD=TRUE\nDV=3903d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=4\nName=PepperDash Essentials TSW-560_[Z.Dialogs] PIN_PIN Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\nHint=Subpage Reference List Vertical_3 (Smart Object ID=3902)\nCode=5\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_3\nGUID=3490D547-0B98-444B-A284-D6C1BAB2FDCE\nSmplCName=PepperDash Essentials TSW-560_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4076\nMaxVariableInputs=4076\nMinVariableOutputs=4076\nMaxVariableOutputs=4076\nNumFixedParams=1\nMinVariableInputsList2=72\nMaxVariableInputsList2=72\nMinVariableOutputsList2=72\nMaxVariableOutputsList2=72\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=[~UNUSED2~]\nInputSigType32=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=[~UNUSED2~]\nInputSigType2034=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=[~EndGroup~]fb\nInputSigType4076=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED2~]\nOutputSigType32=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED2~]\nOutputSigType2034=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=[~EndGroup~]Press\nOutputSigType4076=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=an_fb37\nInputList2SigType48=Analog\nInputList2Cue49=an_fb38\nInputList2SigType49=Analog\nInputList2Cue50=an_fb39\nInputList2SigType50=Analog\nInputList2Cue51=an_fb40\nInputList2SigType51=Analog\nInputList2Cue52=an_fb41\nInputList2SigType52=Analog\nInputList2Cue53=an_fb42\nInputList2SigType53=Analog\nInputList2Cue54=an_fb43\nInputList2SigType54=Analog\nInputList2Cue55=an_fb44\nInputList2SigType55=Analog\nInputList2Cue56=an_fb45\nInputList2SigType56=Analog\nInputList2Cue57=an_fb46\nInputList2SigType57=Analog\nInputList2Cue58=an_fb47\nInputList2SigType58=Analog\nInputList2Cue59=an_fb48\nInputList2SigType59=Analog\nInputList2Cue60=an_fb49\nInputList2SigType60=Analog\nInputList2Cue61=an_fb50\nInputList2SigType61=Analog\nInputList2Cue62=an_fb51\nInputList2SigType62=Analog\nInputList2Cue63=an_fb52\nInputList2SigType63=Analog\nInputList2Cue64=an_fb53\nInputList2SigType64=Analog\nInputList2Cue65=an_fb54\nInputList2SigType65=Analog\nInputList2Cue66=an_fb55\nInputList2SigType66=Analog\nInputList2Cue67=an_fb56\nInputList2SigType67=Analog\nInputList2Cue68=an_fb57\nInputList2SigType68=Analog\nInputList2Cue69=an_fb58\nInputList2SigType69=Analog\nInputList2Cue70=an_fb59\nInputList2SigType70=Analog\nInputList2Cue71=an_fb60\nInputList2SigType71=Analog\nInputList2Cue72=[~EndGroup~]an_fb\nInputList2SigType72=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=an_act37\nOutputList2SigType48=Analog\nOutputList2Cue49=an_act38\nOutputList2SigType49=Analog\nOutputList2Cue50=an_act39\nOutputList2SigType50=Analog\nOutputList2Cue51=an_act40\nOutputList2SigType51=Analog\nOutputList2Cue52=an_act41\nOutputList2SigType52=Analog\nOutputList2Cue53=an_act42\nOutputList2SigType53=Analog\nOutputList2Cue54=an_act43\nOutputList2SigType54=Analog\nOutputList2Cue55=an_act44\nOutputList2SigType55=Analog\nOutputList2Cue56=an_act45\nOutputList2SigType56=Analog\nOutputList2Cue57=an_act46\nOutputList2SigType57=Analog\nOutputList2Cue58=an_act47\nOutputList2SigType58=Analog\nOutputList2Cue59=an_act48\nOutputList2SigType59=Analog\nOutputList2Cue60=an_act49\nOutputList2SigType60=Analog\nOutputList2Cue61=an_act50\nOutputList2SigType61=Analog\nOutputList2Cue62=an_act51\nOutputList2SigType62=Analog\nOutputList2Cue63=an_act52\nOutputList2SigType63=Analog\nOutputList2Cue64=an_act53\nOutputList2SigType64=Analog\nOutputList2Cue65=an_act54\nOutputList2SigType65=Analog\nOutputList2Cue66=an_act55\nOutputList2SigType66=Analog\nOutputList2Cue67=an_act56\nOutputList2SigType67=Analog\nOutputList2Cue68=an_act57\nOutputList2SigType68=Analog\nOutputList2Cue69=an_act58\nOutputList2SigType69=Analog\nOutputList2Cue70=an_act59\nOutputList2SigType70=Analog\nOutputList2Cue71=an_act60\nOutputList2SigType71=Analog\nOutputList2Cue72=[~EndGroup~]an_act\nOutputList2SigType72=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=5\nCedH=5\nSmartObjId=3902d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=5\nTp=1\nHD=TRUE\nDV=3902d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=5\nName=PepperDash Essentials TSW-560_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\nHint=Subpage Reference List Vertical_4 (Smart Object ID=3904)\nCode=6\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_4\nGUID=AC10DF35-3FD4-4F62-9708-0A06AE7A5A14\nSmplCName=PepperDash Essentials TSW-560_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4116\nMaxVariableInputs=4116\nMinVariableOutputs=4116\nMaxVariableOutputs=4116\nNumFixedParams=1\nMinVariableInputsList2=42\nMaxVariableInputsList2=42\nMinVariableOutputsList2=42\nMaxVariableOutputsList2=42\nMinVariableInputsList3=42\nMaxVariableInputsList3=42\nMinVariableOutputsList3=42\nMaxVariableOutputsList3=42\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=[~UNUSED2~]\nInputSigType22=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=[~UNUSED2~]\nInputSigType2024=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=fb91\nInputSigType4106=Digital\nInputCue4107=fb92\nInputSigType4107=Digital\nInputCue4108=fb93\nInputSigType4108=Digital\nInputCue4109=fb94\nInputSigType4109=Digital\nInputCue4110=fb95\nInputSigType4110=Digital\nInputCue4111=fb96\nInputSigType4111=Digital\nInputCue4112=fb97\nInputSigType4112=Digital\nInputCue4113=fb98\nInputSigType4113=Digital\nInputCue4114=fb99\nInputSigType4114=Digital\nInputCue4115=fb100\nInputSigType4115=Digital\nInputCue4116=[~EndGroup~]fb\nInputSigType4116=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED2~]\nOutputSigType22=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED2~]\nOutputSigType2024=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=press91\nOutputSigType4106=Digital\nOutputCue4107=press92\nOutputSigType4107=Digital\nOutputCue4108=press93\nOutputSigType4108=Digital\nOutputCue4109=press94\nOutputSigType4109=Digital\nOutputCue4110=press95\nOutputSigType4110=Digital\nOutputCue4111=press96\nOutputSigType4111=Digital\nOutputCue4112=press97\nOutputSigType4112=Digital\nOutputCue4113=press98\nOutputSigType4113=Digital\nOutputCue4114=press99\nOutputSigType4114=Digital\nOutputCue4115=press100\nOutputSigType4115=Digital\nOutputCue4116=[~EndGroup~]Press\nOutputSigType4116=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=[~EndGroup~]an_fb\nInputList2SigType42=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=[~EndGroup~]an_act\nOutputList2SigType42=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=[~EndGroup~]text-o\nInputList3SigType42=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=[~EndGroup~]text-i\nOutputList3SigType42=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=6\nCedH=6\nSmartObjId=3904d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=6\nTp=1\nHD=TRUE\nDV=3904d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=6\nName=PepperDash Essentials TSW-560_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\nHint=Subpage Reference List Horizontal_3 (Smart Object ID=3200)\nCode=7\nSGControlType=Subpage Reference List Horizontal\nSGControlName=Subpage Reference List Horizontal_3\nGUID=D638D437-6722-43E1-B435-EF13DCE5ECF0\nSmplCName=PepperDash Essentials TSW-560_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10124\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4076\nMaxVariableInputs=4076\nMinVariableOutputs=4076\nMaxVariableOutputs=4076\nNumFixedParams=1\nMinVariableInputsList2=72\nMaxVariableInputsList2=72\nMinVariableOutputsList2=72\nMaxVariableOutputsList2=72\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=[~UNUSED2~]\nInputSigType32=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=[~UNUSED2~]\nInputSigType2034=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=[~EndGroup~]fb\nInputSigType4076=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED2~]\nOutputSigType32=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED2~]\nOutputSigType2034=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=[~EndGroup~]Press\nOutputSigType4076=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=an_fb37\nInputList2SigType48=Analog\nInputList2Cue49=an_fb38\nInputList2SigType49=Analog\nInputList2Cue50=an_fb39\nInputList2SigType50=Analog\nInputList2Cue51=an_fb40\nInputList2SigType51=Analog\nInputList2Cue52=an_fb41\nInputList2SigType52=Analog\nInputList2Cue53=an_fb42\nInputList2SigType53=Analog\nInputList2Cue54=an_fb43\nInputList2SigType54=Analog\nInputList2Cue55=an_fb44\nInputList2SigType55=Analog\nInputList2Cue56=an_fb45\nInputList2SigType56=Analog\nInputList2Cue57=an_fb46\nInputList2SigType57=Analog\nInputList2Cue58=an_fb47\nInputList2SigType58=Analog\nInputList2Cue59=an_fb48\nInputList2SigType59=Analog\nInputList2Cue60=an_fb49\nInputList2SigType60=Analog\nInputList2Cue61=an_fb50\nInputList2SigType61=Analog\nInputList2Cue62=an_fb51\nInputList2SigType62=Analog\nInputList2Cue63=an_fb52\nInputList2SigType63=Analog\nInputList2Cue64=an_fb53\nInputList2SigType64=Analog\nInputList2Cue65=an_fb54\nInputList2SigType65=Analog\nInputList2Cue66=an_fb55\nInputList2SigType66=Analog\nInputList2Cue67=an_fb56\nInputList2SigType67=Analog\nInputList2Cue68=an_fb57\nInputList2SigType68=Analog\nInputList2Cue69=an_fb58\nInputList2SigType69=Analog\nInputList2Cue70=an_fb59\nInputList2SigType70=Analog\nInputList2Cue71=an_fb60\nInputList2SigType71=Analog\nInputList2Cue72=[~EndGroup~]an_fb\nInputList2SigType72=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=an_act37\nOutputList2SigType48=Analog\nOutputList2Cue49=an_act38\nOutputList2SigType49=Analog\nOutputList2Cue50=an_act39\nOutputList2SigType50=Analog\nOutputList2Cue51=an_act40\nOutputList2SigType51=Analog\nOutputList2Cue52=an_act41\nOutputList2SigType52=Analog\nOutputList2Cue53=an_act42\nOutputList2SigType53=Analog\nOutputList2Cue54=an_act43\nOutputList2SigType54=Analog\nOutputList2Cue55=an_act44\nOutputList2SigType55=Analog\nOutputList2Cue56=an_act45\nOutputList2SigType56=Analog\nOutputList2Cue57=an_act46\nOutputList2SigType57=Analog\nOutputList2Cue58=an_act47\nOutputList2SigType58=Analog\nOutputList2Cue59=an_act48\nOutputList2SigType59=Analog\nOutputList2Cue60=an_act49\nOutputList2SigType60=Analog\nOutputList2Cue61=an_act50\nOutputList2SigType61=Analog\nOutputList2Cue62=an_act51\nOutputList2SigType62=Analog\nOutputList2Cue63=an_act52\nOutputList2SigType63=Analog\nOutputList2Cue64=an_act53\nOutputList2SigType64=Analog\nOutputList2Cue65=an_act54\nOutputList2SigType65=Analog\nOutputList2Cue66=an_act55\nOutputList2SigType66=Analog\nOutputList2Cue67=an_act56\nOutputList2SigType67=Analog\nOutputList2Cue68=an_act57\nOutputList2SigType68=Analog\nOutputList2Cue69=an_act58\nOutputList2SigType69=Analog\nOutputList2Cue70=an_act59\nOutputList2SigType70=Analog\nOutputList2Cue71=an_act60\nOutputList2SigType71=Analog\nOutputList2Cue72=[~EndGroup~]an_act\nOutputList2SigType72=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=7\nCedH=7\nSmartObjId=3200d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=7\nTp=1\nHD=TRUE\nDV=3200d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=7\nName=PepperDash Essentials TSW-560_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\nHint=Channel Preset List Basic (Smart Object ID=1806)\nCode=8\nSGControlType=Subpage Reference List Vertical\nSGControlName=Channel Preset List Basic\nGUID=46C43CE4-6BB9-4B76-A13E-B4279202F998\nSmplCName=PepperDash Essentials TSW-560_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4106\nMaxVariableInputs=4106\nMinVariableOutputs=4106\nMaxVariableOutputs=4106\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=132\nMaxVariableInputsList3=132\nMinVariableOutputsList3=132\nMaxVariableOutputsList3=132\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=[~EndGroup~]fb\nInputSigType4106=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=[~EndGroup~]Press\nOutputSigType4106=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=[~EndGroup~]text-o\nInputList3SigType132=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=[~EndGroup~]text-i\nOutputList3SigType132=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=8\nCedH=8\nSmartObjId=1806d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=8\nTp=1\nHD=TRUE\nDV=1806d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=8\nName=PepperDash Essentials TSW-560_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\nHint=Modes Bottom Bar SRL (Smart Object ID=15022)\nCode=9\nSGControlType=Subpage Reference List Horizontal\nSGControlName=Modes Bottom Bar SRL\nGUID=812FF0C4-486D-4ABC-90FA-405F19104323\nSmplCName=PepperDash Essentials TSW-560_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10124\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4028\nMaxVariableInputs=4028\nMinVariableOutputs=4028\nMaxVariableOutputs=4028\nNumFixedParams=1\nMinVariableInputsList2=24\nMaxVariableInputsList2=24\nMinVariableOutputsList2=24\nMaxVariableOutputsList2=24\nMinVariableInputsList3=24\nMaxVariableInputsList3=24\nMinVariableOutputsList3=24\nMaxVariableOutputsList3=24\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=[~UNUSED2~]\nInputSigType16=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=[~UNUSED2~]\nInputSigType2018=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=[~EndGroup~]fb\nInputSigType4028=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED2~]\nOutputSigType16=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED2~]\nOutputSigType2018=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=[~EndGroup~]Press\nOutputSigType4028=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=[~EndGroup~]an_fb\nInputList2SigType24=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=[~EndGroup~]an_act\nOutputList2SigType24=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=[~EndGroup~]text-o\nInputList3SigType24=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=[~EndGroup~]text-i\nOutputList3SigType24=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=9\nCedH=9\nSmartObjId=15022d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=9\nTp=1\nHD=TRUE\nDV=15022d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=9\nName=PepperDash Essentials TSW-560_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[B.AV] Object - Presets_CATV Icon List Vertical.ced\nHint=CATV Icon List Vertical (Smart Object ID=10012)\nCode=10\nSGControlType=Subpage Reference List Vertical\nSGControlName=CATV Icon List Vertical\nGUID=B34C4DC0-DD10-4C49-AEBA-F968934EFDC6\nSmplCName=PepperDash Essentials TSW-560_[B.AV] Object - Presets_CATV Icon List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4316\nMaxVariableInputs=4316\nMinVariableOutputs=4316\nMaxVariableOutputs=4316\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=412\nMaxVariableInputsList3=412\nMinVariableOutputsList3=412\nMaxVariableOutputsList3=412\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=Item 31 Enable\nInputSigType42=Digital\nInputCue43=Item 32 Enable\nInputSigType43=Digital\nInputCue44=Item 33 Enable\nInputSigType44=Digital\nInputCue45=Item 34 Enable\nInputSigType45=Digital\nInputCue46=Item 35 Enable\nInputSigType46=Digital\nInputCue47=Item 36 Enable\nInputSigType47=Digital\nInputCue48=Item 37 Enable\nInputSigType48=Digital\nInputCue49=Item 38 Enable\nInputSigType49=Digital\nInputCue50=Item 39 Enable\nInputSigType50=Digital\nInputCue51=Item 40 Enable\nInputSigType51=Digital\nInputCue52=Item 41 Enable\nInputSigType52=Digital\nInputCue53=Item 42 Enable\nInputSigType53=Digital\nInputCue54=Item 43 Enable\nInputSigType54=Digital\nInputCue55=Item 44 Enable\nInputSigType55=Digital\nInputCue56=Item 45 Enable\nInputSigType56=Digital\nInputCue57=Item 46 Enable\nInputSigType57=Digital\nInputCue58=Item 47 Enable\nInputSigType58=Digital\nInputCue59=Item 48 Enable\nInputSigType59=Digital\nInputCue60=Item 49 Enable\nInputSigType60=Digital\nInputCue61=Item 50 Enable\nInputSigType61=Digital\nInputCue62=Item 51 Enable\nInputSigType62=Digital\nInputCue63=Item 52 Enable\nInputSigType63=Digital\nInputCue64=Item 53 Enable\nInputSigType64=Digital\nInputCue65=Item 54 Enable\nInputSigType65=Digital\nInputCue66=Item 55 Enable\nInputSigType66=Digital\nInputCue67=Item 56 Enable\nInputSigType67=Digital\nInputCue68=Item 57 Enable\nInputSigType68=Digital\nInputCue69=Item 58 Enable\nInputSigType69=Digital\nInputCue70=Item 59 Enable\nInputSigType70=Digital\nInputCue71=Item 60 Enable\nInputSigType71=Digital\nInputCue72=Item 61 Enable\nInputSigType72=Digital\nInputCue73=Item 62 Enable\nInputSigType73=Digital\nInputCue74=Item 63 Enable\nInputSigType74=Digital\nInputCue75=Item 64 Enable\nInputSigType75=Digital\nInputCue76=Item 65 Enable\nInputSigType76=Digital\nInputCue77=Item 66 Enable\nInputSigType77=Digital\nInputCue78=Item 67 Enable\nInputSigType78=Digital\nInputCue79=Item 68 Enable\nInputSigType79=Digital\nInputCue80=Item 69 Enable\nInputSigType80=Digital\nInputCue81=Item 70 Enable\nInputSigType81=Digital\nInputCue82=Item 71 Enable\nInputSigType82=Digital\nInputCue83=Item 72 Enable\nInputSigType83=Digital\nInputCue84=Item 73 Enable\nInputSigType84=Digital\nInputCue85=Item 74 Enable\nInputSigType85=Digital\nInputCue86=Item 75 Enable\nInputSigType86=Digital\nInputCue87=Item 76 Enable\nInputSigType87=Digital\nInputCue88=Item 77 Enable\nInputSigType88=Digital\nInputCue89=Item 78 Enable\nInputSigType89=Digital\nInputCue90=Item 79 Enable\nInputSigType90=Digital\nInputCue91=Item 80 Enable\nInputSigType91=Digital\nInputCue92=Item 81 Enable\nInputSigType92=Digital\nInputCue93=Item 82 Enable\nInputSigType93=Digital\nInputCue94=Item 83 Enable\nInputSigType94=Digital\nInputCue95=Item 84 Enable\nInputSigType95=Digital\nInputCue96=Item 85 Enable\nInputSigType96=Digital\nInputCue97=Item 86 Enable\nInputSigType97=Digital\nInputCue98=Item 87 Enable\nInputSigType98=Digital\nInputCue99=Item 88 Enable\nInputSigType99=Digital\nInputCue100=Item 89 Enable\nInputSigType100=Digital\nInputCue101=Item 90 Enable\nInputSigType101=Digital\nInputCue102=Item 91 Enable\nInputSigType102=Digital\nInputCue103=Item 92 Enable\nInputSigType103=Digital\nInputCue104=Item 93 Enable\nInputSigType104=Digital\nInputCue105=Item 94 Enable\nInputSigType105=Digital\nInputCue106=Item 95 Enable\nInputSigType106=Digital\nInputCue107=Item 96 Enable\nInputSigType107=Digital\nInputCue108=Item 97 Enable\nInputSigType108=Digital\nInputCue109=Item 98 Enable\nInputSigType109=Digital\nInputCue110=Item 99 Enable\nInputSigType110=Digital\nInputCue111=Item 100 Enable\nInputSigType111=Digital\nInputCue112=[~UNUSED2~]\nInputSigType112=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=Item 31 Visible\nInputSigType2044=Digital\nInputCue2045=Item 32 Visible\nInputSigType2045=Digital\nInputCue2046=Item 33 Visible\nInputSigType2046=Digital\nInputCue2047=Item 34 Visible\nInputSigType2047=Digital\nInputCue2048=Item 35 Visible\nInputSigType2048=Digital\nInputCue2049=Item 36 Visible\nInputSigType2049=Digital\nInputCue2050=Item 37 Visible\nInputSigType2050=Digital\nInputCue2051=Item 38 Visible\nInputSigType2051=Digital\nInputCue2052=Item 39 Visible\nInputSigType2052=Digital\nInputCue2053=Item 40 Visible\nInputSigType2053=Digital\nInputCue2054=Item 41 Visible\nInputSigType2054=Digital\nInputCue2055=Item 42 Visible\nInputSigType2055=Digital\nInputCue2056=Item 43 Visible\nInputSigType2056=Digital\nInputCue2057=Item 44 Visible\nInputSigType2057=Digital\nInputCue2058=Item 45 Visible\nInputSigType2058=Digital\nInputCue2059=Item 46 Visible\nInputSigType2059=Digital\nInputCue2060=Item 47 Visible\nInputSigType2060=Digital\nInputCue2061=Item 48 Visible\nInputSigType2061=Digital\nInputCue2062=Item 49 Visible\nInputSigType2062=Digital\nInputCue2063=Item 50 Visible\nInputSigType2063=Digital\nInputCue2064=Item 51 Visible\nInputSigType2064=Digital\nInputCue2065=Item 52 Visible\nInputSigType2065=Digital\nInputCue2066=Item 53 Visible\nInputSigType2066=Digital\nInputCue2067=Item 54 Visible\nInputSigType2067=Digital\nInputCue2068=Item 55 Visible\nInputSigType2068=Digital\nInputCue2069=Item 56 Visible\nInputSigType2069=Digital\nInputCue2070=Item 57 Visible\nInputSigType2070=Digital\nInputCue2071=Item 58 Visible\nInputSigType2071=Digital\nInputCue2072=Item 59 Visible\nInputSigType2072=Digital\nInputCue2073=Item 60 Visible\nInputSigType2073=Digital\nInputCue2074=Item 61 Visible\nInputSigType2074=Digital\nInputCue2075=Item 62 Visible\nInputSigType2075=Digital\nInputCue2076=Item 63 Visible\nInputSigType2076=Digital\nInputCue2077=Item 64 Visible\nInputSigType2077=Digital\nInputCue2078=Item 65 Visible\nInputSigType2078=Digital\nInputCue2079=Item 66 Visible\nInputSigType2079=Digital\nInputCue2080=Item 67 Visible\nInputSigType2080=Digital\nInputCue2081=Item 68 Visible\nInputSigType2081=Digital\nInputCue2082=Item 69 Visible\nInputSigType2082=Digital\nInputCue2083=Item 70 Visible\nInputSigType2083=Digital\nInputCue2084=Item 71 Visible\nInputSigType2084=Digital\nInputCue2085=Item 72 Visible\nInputSigType2085=Digital\nInputCue2086=Item 73 Visible\nInputSigType2086=Digital\nInputCue2087=Item 74 Visible\nInputSigType2087=Digital\nInputCue2088=Item 75 Visible\nInputSigType2088=Digital\nInputCue2089=Item 76 Visible\nInputSigType2089=Digital\nInputCue2090=Item 77 Visible\nInputSigType2090=Digital\nInputCue2091=Item 78 Visible\nInputSigType2091=Digital\nInputCue2092=Item 79 Visible\nInputSigType2092=Digital\nInputCue2093=Item 80 Visible\nInputSigType2093=Digital\nInputCue2094=Item 81 Visible\nInputSigType2094=Digital\nInputCue2095=Item 82 Visible\nInputSigType2095=Digital\nInputCue2096=Item 83 Visible\nInputSigType2096=Digital\nInputCue2097=Item 84 Visible\nInputSigType2097=Digital\nInputCue2098=Item 85 Visible\nInputSigType2098=Digital\nInputCue2099=Item 86 Visible\nInputSigType2099=Digital\nInputCue2100=Item 87 Visible\nInputSigType2100=Digital\nInputCue2101=Item 88 Visible\nInputSigType2101=Digital\nInputCue2102=Item 89 Visible\nInputSigType2102=Digital\nInputCue2103=Item 90 Visible\nInputSigType2103=Digital\nInputCue2104=Item 91 Visible\nInputSigType2104=Digital\nInputCue2105=Item 92 Visible\nInputSigType2105=Digital\nInputCue2106=Item 93 Visible\nInputSigType2106=Digital\nInputCue2107=Item 94 Visible\nInputSigType2107=Digital\nInputCue2108=Item 95 Visible\nInputSigType2108=Digital\nInputCue2109=Item 96 Visible\nInputSigType2109=Digital\nInputCue2110=Item 97 Visible\nInputSigType2110=Digital\nInputCue2111=Item 98 Visible\nInputSigType2111=Digital\nInputCue2112=Item 99 Visible\nInputSigType2112=Digital\nInputCue2113=Item 100 Visible\nInputSigType2113=Digital\nInputCue2114=[~UNUSED2~]\nInputSigType2114=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=fb91\nInputSigType4106=Digital\nInputCue4107=fb92\nInputSigType4107=Digital\nInputCue4108=fb93\nInputSigType4108=Digital\nInputCue4109=fb94\nInputSigType4109=Digital\nInputCue4110=fb95\nInputSigType4110=Digital\nInputCue4111=fb96\nInputSigType4111=Digital\nInputCue4112=fb97\nInputSigType4112=Digital\nInputCue4113=fb98\nInputSigType4113=Digital\nInputCue4114=fb99\nInputSigType4114=Digital\nInputCue4115=fb100\nInputSigType4115=Digital\nInputCue4116=fb101\nInputSigType4116=Digital\nInputCue4117=fb102\nInputSigType4117=Digital\nInputCue4118=fb103\nInputSigType4118=Digital\nInputCue4119=fb104\nInputSigType4119=Digital\nInputCue4120=fb105\nInputSigType4120=Digital\nInputCue4121=fb106\nInputSigType4121=Digital\nInputCue4122=fb107\nInputSigType4122=Digital\nInputCue4123=fb108\nInputSigType4123=Digital\nInputCue4124=fb109\nInputSigType4124=Digital\nInputCue4125=fb110\nInputSigType4125=Digital\nInputCue4126=fb111\nInputSigType4126=Digital\nInputCue4127=fb112\nInputSigType4127=Digital\nInputCue4128=fb113\nInputSigType4128=Digital\nInputCue4129=fb114\nInputSigType4129=Digital\nInputCue4130=fb115\nInputSigType4130=Digital\nInputCue4131=fb116\nInputSigType4131=Digital\nInputCue4132=fb117\nInputSigType4132=Digital\nInputCue4133=fb118\nInputSigType4133=Digital\nInputCue4134=fb119\nInputSigType4134=Digital\nInputCue4135=fb120\nInputSigType4135=Digital\nInputCue4136=fb121\nInputSigType4136=Digital\nInputCue4137=fb122\nInputSigType4137=Digital\nInputCue4138=fb123\nInputSigType4138=Digital\nInputCue4139=fb124\nInputSigType4139=Digital\nInputCue4140=fb125\nInputSigType4140=Digital\nInputCue4141=fb126\nInputSigType4141=Digital\nInputCue4142=fb127\nInputSigType4142=Digital\nInputCue4143=fb128\nInputSigType4143=Digital\nInputCue4144=fb129\nInputSigType4144=Digital\nInputCue4145=fb130\nInputSigType4145=Digital\nInputCue4146=fb131\nInputSigType4146=Digital\nInputCue4147=fb132\nInputSigType4147=Digital\nInputCue4148=fb133\nInputSigType4148=Digital\nInputCue4149=fb134\nInputSigType4149=Digital\nInputCue4150=fb135\nInputSigType4150=Digital\nInputCue4151=fb136\nInputSigType4151=Digital\nInputCue4152=fb137\nInputSigType4152=Digital\nInputCue4153=fb138\nInputSigType4153=Digital\nInputCue4154=fb139\nInputSigType4154=Digital\nInputCue4155=fb140\nInputSigType4155=Digital\nInputCue4156=fb141\nInputSigType4156=Digital\nInputCue4157=fb142\nInputSigType4157=Digital\nInputCue4158=fb143\nInputSigType4158=Digital\nInputCue4159=fb144\nInputSigType4159=Digital\nInputCue4160=fb145\nInputSigType4160=Digital\nInputCue4161=fb146\nInputSigType4161=Digital\nInputCue4162=fb147\nInputSigType4162=Digital\nInputCue4163=fb148\nInputSigType4163=Digital\nInputCue4164=fb149\nInputSigType4164=Digital\nInputCue4165=fb150\nInputSigType4165=Digital\nInputCue4166=fb151\nInputSigType4166=Digital\nInputCue4167=fb152\nInputSigType4167=Digital\nInputCue4168=fb153\nInputSigType4168=Digital\nInputCue4169=fb154\nInputSigType4169=Digital\nInputCue4170=fb155\nInputSigType4170=Digital\nInputCue4171=fb156\nInputSigType4171=Digital\nInputCue4172=fb157\nInputSigType4172=Digital\nInputCue4173=fb158\nInputSigType4173=Digital\nInputCue4174=fb159\nInputSigType4174=Digital\nInputCue4175=fb160\nInputSigType4175=Digital\nInputCue4176=fb161\nInputSigType4176=Digital\nInputCue4177=fb162\nInputSigType4177=Digital\nInputCue4178=fb163\nInputSigType4178=Digital\nInputCue4179=fb164\nInputSigType4179=Digital\nInputCue4180=fb165\nInputSigType4180=Digital\nInputCue4181=fb166\nInputSigType4181=Digital\nInputCue4182=fb167\nInputSigType4182=Digital\nInputCue4183=fb168\nInputSigType4183=Digital\nInputCue4184=fb169\nInputSigType4184=Digital\nInputCue4185=fb170\nInputSigType4185=Digital\nInputCue4186=fb171\nInputSigType4186=Digital\nInputCue4187=fb172\nInputSigType4187=Digital\nInputCue4188=fb173\nInputSigType4188=Digital\nInputCue4189=fb174\nInputSigType4189=Digital\nInputCue4190=fb175\nInputSigType4190=Digital\nInputCue4191=fb176\nInputSigType4191=Digital\nInputCue4192=fb177\nInputSigType4192=Digital\nInputCue4193=fb178\nInputSigType4193=Digital\nInputCue4194=fb179\nInputSigType4194=Digital\nInputCue4195=fb180\nInputSigType4195=Digital\nInputCue4196=fb181\nInputSigType4196=Digital\nInputCue4197=fb182\nInputSigType4197=Digital\nInputCue4198=fb183\nInputSigType4198=Digital\nInputCue4199=fb184\nInputSigType4199=Digital\nInputCue4200=fb185\nInputSigType4200=Digital\nInputCue4201=fb186\nInputSigType4201=Digital\nInputCue4202=fb187\nInputSigType4202=Digital\nInputCue4203=fb188\nInputSigType4203=Digital\nInputCue4204=fb189\nInputSigType4204=Digital\nInputCue4205=fb190\nInputSigType4205=Digital\nInputCue4206=fb191\nInputSigType4206=Digital\nInputCue4207=fb192\nInputSigType4207=Digital\nInputCue4208=fb193\nInputSigType4208=Digital\nInputCue4209=fb194\nInputSigType4209=Digital\nInputCue4210=fb195\nInputSigType4210=Digital\nInputCue4211=fb196\nInputSigType4211=Digital\nInputCue4212=fb197\nInputSigType4212=Digital\nInputCue4213=fb198\nInputSigType4213=Digital\nInputCue4214=fb199\nInputSigType4214=Digital\nInputCue4215=fb200\nInputSigType4215=Digital\nInputCue4216=fb201\nInputSigType4216=Digital\nInputCue4217=fb202\nInputSigType4217=Digital\nInputCue4218=fb203\nInputSigType4218=Digital\nInputCue4219=fb204\nInputSigType4219=Digital\nInputCue4220=fb205\nInputSigType4220=Digital\nInputCue4221=fb206\nInputSigType4221=Digital\nInputCue4222=fb207\nInputSigType4222=Digital\nInputCue4223=fb208\nInputSigType4223=Digital\nInputCue4224=fb209\nInputSigType4224=Digital\nInputCue4225=fb210\nInputSigType4225=Digital\nInputCue4226=fb211\nInputSigType4226=Digital\nInputCue4227=fb212\nInputSigType4227=Digital\nInputCue4228=fb213\nInputSigType4228=Digital\nInputCue4229=fb214\nInputSigType4229=Digital\nInputCue4230=fb215\nInputSigType4230=Digital\nInputCue4231=fb216\nInputSigType4231=Digital\nInputCue4232=fb217\nInputSigType4232=Digital\nInputCue4233=fb218\nInputSigType4233=Digital\nInputCue4234=fb219\nInputSigType4234=Digital\nInputCue4235=fb220\nInputSigType4235=Digital\nInputCue4236=fb221\nInputSigType4236=Digital\nInputCue4237=fb222\nInputSigType4237=Digital\nInputCue4238=fb223\nInputSigType4238=Digital\nInputCue4239=fb224\nInputSigType4239=Digital\nInputCue4240=fb225\nInputSigType4240=Digital\nInputCue4241=fb226\nInputSigType4241=Digital\nInputCue4242=fb227\nInputSigType4242=Digital\nInputCue4243=fb228\nInputSigType4243=Digital\nInputCue4244=fb229\nInputSigType4244=Digital\nInputCue4245=fb230\nInputSigType4245=Digital\nInputCue4246=fb231\nInputSigType4246=Digital\nInputCue4247=fb232\nInputSigType4247=Digital\nInputCue4248=fb233\nInputSigType4248=Digital\nInputCue4249=fb234\nInputSigType4249=Digital\nInputCue4250=fb235\nInputSigType4250=Digital\nInputCue4251=fb236\nInputSigType4251=Digital\nInputCue4252=fb237\nInputSigType4252=Digital\nInputCue4253=fb238\nInputSigType4253=Digital\nInputCue4254=fb239\nInputSigType4254=Digital\nInputCue4255=fb240\nInputSigType4255=Digital\nInputCue4256=fb241\nInputSigType4256=Digital\nInputCue4257=fb242\nInputSigType4257=Digital\nInputCue4258=fb243\nInputSigType4258=Digital\nInputCue4259=fb244\nInputSigType4259=Digital\nInputCue4260=fb245\nInputSigType4260=Digital\nInputCue4261=fb246\nInputSigType4261=Digital\nInputCue4262=fb247\nInputSigType4262=Digital\nInputCue4263=fb248\nInputSigType4263=Digital\nInputCue4264=fb249\nInputSigType4264=Digital\nInputCue4265=fb250\nInputSigType4265=Digital\nInputCue4266=fb251\nInputSigType4266=Digital\nInputCue4267=fb252\nInputSigType4267=Digital\nInputCue4268=fb253\nInputSigType4268=Digital\nInputCue4269=fb254\nInputSigType4269=Digital\nInputCue4270=fb255\nInputSigType4270=Digital\nInputCue4271=fb256\nInputSigType4271=Digital\nInputCue4272=fb257\nInputSigType4272=Digital\nInputCue4273=fb258\nInputSigType4273=Digital\nInputCue4274=fb259\nInputSigType4274=Digital\nInputCue4275=fb260\nInputSigType4275=Digital\nInputCue4276=fb261\nInputSigType4276=Digital\nInputCue4277=fb262\nInputSigType4277=Digital\nInputCue4278=fb263\nInputSigType4278=Digital\nInputCue4279=fb264\nInputSigType4279=Digital\nInputCue4280=fb265\nInputSigType4280=Digital\nInputCue4281=fb266\nInputSigType4281=Digital\nInputCue4282=fb267\nInputSigType4282=Digital\nInputCue4283=fb268\nInputSigType4283=Digital\nInputCue4284=fb269\nInputSigType4284=Digital\nInputCue4285=fb270\nInputSigType4285=Digital\nInputCue4286=fb271\nInputSigType4286=Digital\nInputCue4287=fb272\nInputSigType4287=Digital\nInputCue4288=fb273\nInputSigType4288=Digital\nInputCue4289=fb274\nInputSigType4289=Digital\nInputCue4290=fb275\nInputSigType4290=Digital\nInputCue4291=fb276\nInputSigType4291=Digital\nInputCue4292=fb277\nInputSigType4292=Digital\nInputCue4293=fb278\nInputSigType4293=Digital\nInputCue4294=fb279\nInputSigType4294=Digital\nInputCue4295=fb280\nInputSigType4295=Digital\nInputCue4296=fb281\nInputSigType4296=Digital\nInputCue4297=fb282\nInputSigType4297=Digital\nInputCue4298=fb283\nInputSigType4298=Digital\nInputCue4299=fb284\nInputSigType4299=Digital\nInputCue4300=fb285\nInputSigType4300=Digital\nInputCue4301=fb286\nInputSigType4301=Digital\nInputCue4302=fb287\nInputSigType4302=Digital\nInputCue4303=fb288\nInputSigType4303=Digital\nInputCue4304=fb289\nInputSigType4304=Digital\nInputCue4305=fb290\nInputSigType4305=Digital\nInputCue4306=fb291\nInputSigType4306=Digital\nInputCue4307=fb292\nInputSigType4307=Digital\nInputCue4308=fb293\nInputSigType4308=Digital\nInputCue4309=fb294\nInputSigType4309=Digital\nInputCue4310=fb295\nInputSigType4310=Digital\nInputCue4311=fb296\nInputSigType4311=Digital\nInputCue4312=fb297\nInputSigType4312=Digital\nInputCue4313=fb298\nInputSigType4313=Digital\nInputCue4314=fb299\nInputSigType4314=Digital\nInputCue4315=fb300\nInputSigType4315=Digital\nInputCue4316=[~EndGroup~]fb\nInputSigType4316=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED3~]\nOutputSigType42=Digital\nOutputCue43=[~UNUSED3~]\nOutputSigType43=Digital\nOutputCue44=[~UNUSED3~]\nOutputSigType44=Digital\nOutputCue45=[~UNUSED3~]\nOutputSigType45=Digital\nOutputCue46=[~UNUSED3~]\nOutputSigType46=Digital\nOutputCue47=[~UNUSED3~]\nOutputSigType47=Digital\nOutputCue48=[~UNUSED3~]\nOutputSigType48=Digital\nOutputCue49=[~UNUSED3~]\nOutputSigType49=Digital\nOutputCue50=[~UNUSED3~]\nOutputSigType50=Digital\nOutputCue51=[~UNUSED3~]\nOutputSigType51=Digital\nOutputCue52=[~UNUSED3~]\nOutputSigType52=Digital\nOutputCue53=[~UNUSED3~]\nOutputSigType53=Digital\nOutputCue54=[~UNUSED3~]\nOutputSigType54=Digital\nOutputCue55=[~UNUSED3~]\nOutputSigType55=Digital\nOutputCue56=[~UNUSED3~]\nOutputSigType56=Digital\nOutputCue57=[~UNUSED3~]\nOutputSigType57=Digital\nOutputCue58=[~UNUSED3~]\nOutputSigType58=Digital\nOutputCue59=[~UNUSED3~]\nOutputSigType59=Digital\nOutputCue60=[~UNUSED3~]\nOutputSigType60=Digital\nOutputCue61=[~UNUSED3~]\nOutputSigType61=Digital\nOutputCue62=[~UNUSED3~]\nOutputSigType62=Digital\nOutputCue63=[~UNUSED3~]\nOutputSigType63=Digital\nOutputCue64=[~UNUSED3~]\nOutputSigType64=Digital\nOutputCue65=[~UNUSED3~]\nOutputSigType65=Digital\nOutputCue66=[~UNUSED3~]\nOutputSigType66=Digital\nOutputCue67=[~UNUSED3~]\nOutputSigType67=Digital\nOutputCue68=[~UNUSED3~]\nOutputSigType68=Digital\nOutputCue69=[~UNUSED3~]\nOutputSigType69=Digital\nOutputCue70=[~UNUSED3~]\nOutputSigType70=Digital\nOutputCue71=[~UNUSED3~]\nOutputSigType71=Digital\nOutputCue72=[~UNUSED3~]\nOutputSigType72=Digital\nOutputCue73=[~UNUSED3~]\nOutputSigType73=Digital\nOutputCue74=[~UNUSED3~]\nOutputSigType74=Digital\nOutputCue75=[~UNUSED3~]\nOutputSigType75=Digital\nOutputCue76=[~UNUSED3~]\nOutputSigType76=Digital\nOutputCue77=[~UNUSED3~]\nOutputSigType77=Digital\nOutputCue78=[~UNUSED3~]\nOutputSigType78=Digital\nOutputCue79=[~UNUSED3~]\nOutputSigType79=Digital\nOutputCue80=[~UNUSED3~]\nOutputSigType80=Digital\nOutputCue81=[~UNUSED3~]\nOutputSigType81=Digital\nOutputCue82=[~UNUSED3~]\nOutputSigType82=Digital\nOutputCue83=[~UNUSED3~]\nOutputSigType83=Digital\nOutputCue84=[~UNUSED3~]\nOutputSigType84=Digital\nOutputCue85=[~UNUSED3~]\nOutputSigType85=Digital\nOutputCue86=[~UNUSED3~]\nOutputSigType86=Digital\nOutputCue87=[~UNUSED3~]\nOutputSigType87=Digital\nOutputCue88=[~UNUSED3~]\nOutputSigType88=Digital\nOutputCue89=[~UNUSED3~]\nOutputSigType89=Digital\nOutputCue90=[~UNUSED3~]\nOutputSigType90=Digital\nOutputCue91=[~UNUSED3~]\nOutputSigType91=Digital\nOutputCue92=[~UNUSED3~]\nOutputSigType92=Digital\nOutputCue93=[~UNUSED3~]\nOutputSigType93=Digital\nOutputCue94=[~UNUSED3~]\nOutputSigType94=Digital\nOutputCue95=[~UNUSED3~]\nOutputSigType95=Digital\nOutputCue96=[~UNUSED3~]\nOutputSigType96=Digital\nOutputCue97=[~UNUSED3~]\nOutputSigType97=Digital\nOutputCue98=[~UNUSED3~]\nOutputSigType98=Digital\nOutputCue99=[~UNUSED3~]\nOutputSigType99=Digital\nOutputCue100=[~UNUSED3~]\nOutputSigType100=Digital\nOutputCue101=[~UNUSED3~]\nOutputSigType101=Digital\nOutputCue102=[~UNUSED3~]\nOutputSigType102=Digital\nOutputCue103=[~UNUSED3~]\nOutputSigType103=Digital\nOutputCue104=[~UNUSED3~]\nOutputSigType104=Digital\nOutputCue105=[~UNUSED3~]\nOutputSigType105=Digital\nOutputCue106=[~UNUSED3~]\nOutputSigType106=Digital\nOutputCue107=[~UNUSED3~]\nOutputSigType107=Digital\nOutputCue108=[~UNUSED3~]\nOutputSigType108=Digital\nOutputCue109=[~UNUSED3~]\nOutputSigType109=Digital\nOutputCue110=[~UNUSED3~]\nOutputSigType110=Digital\nOutputCue111=[~UNUSED3~]\nOutputSigType111=Digital\nOutputCue112=[~UNUSED2~]\nOutputSigType112=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED3~]\nOutputSigType2044=Digital\nOutputCue2045=[~UNUSED3~]\nOutputSigType2045=Digital\nOutputCue2046=[~UNUSED3~]\nOutputSigType2046=Digital\nOutputCue2047=[~UNUSED3~]\nOutputSigType2047=Digital\nOutputCue2048=[~UNUSED3~]\nOutputSigType2048=Digital\nOutputCue2049=[~UNUSED3~]\nOutputSigType2049=Digital\nOutputCue2050=[~UNUSED3~]\nOutputSigType2050=Digital\nOutputCue2051=[~UNUSED3~]\nOutputSigType2051=Digital\nOutputCue2052=[~UNUSED3~]\nOutputSigType2052=Digital\nOutputCue2053=[~UNUSED3~]\nOutputSigType2053=Digital\nOutputCue2054=[~UNUSED3~]\nOutputSigType2054=Digital\nOutputCue2055=[~UNUSED3~]\nOutputSigType2055=Digital\nOutputCue2056=[~UNUSED3~]\nOutputSigType2056=Digital\nOutputCue2057=[~UNUSED3~]\nOutputSigType2057=Digital\nOutputCue2058=[~UNUSED3~]\nOutputSigType2058=Digital\nOutputCue2059=[~UNUSED3~]\nOutputSigType2059=Digital\nOutputCue2060=[~UNUSED3~]\nOutputSigType2060=Digital\nOutputCue2061=[~UNUSED3~]\nOutputSigType2061=Digital\nOutputCue2062=[~UNUSED3~]\nOutputSigType2062=Digital\nOutputCue2063=[~UNUSED3~]\nOutputSigType2063=Digital\nOutputCue2064=[~UNUSED3~]\nOutputSigType2064=Digital\nOutputCue2065=[~UNUSED3~]\nOutputSigType2065=Digital\nOutputCue2066=[~UNUSED3~]\nOutputSigType2066=Digital\nOutputCue2067=[~UNUSED3~]\nOutputSigType2067=Digital\nOutputCue2068=[~UNUSED3~]\nOutputSigType2068=Digital\nOutputCue2069=[~UNUSED3~]\nOutputSigType2069=Digital\nOutputCue2070=[~UNUSED3~]\nOutputSigType2070=Digital\nOutputCue2071=[~UNUSED3~]\nOutputSigType2071=Digital\nOutputCue2072=[~UNUSED3~]\nOutputSigType2072=Digital\nOutputCue2073=[~UNUSED3~]\nOutputSigType2073=Digital\nOutputCue2074=[~UNUSED3~]\nOutputSigType2074=Digital\nOutputCue2075=[~UNUSED3~]\nOutputSigType2075=Digital\nOutputCue2076=[~UNUSED3~]\nOutputSigType2076=Digital\nOutputCue2077=[~UNUSED3~]\nOutputSigType2077=Digital\nOutputCue2078=[~UNUSED3~]\nOutputSigType2078=Digital\nOutputCue2079=[~UNUSED3~]\nOutputSigType2079=Digital\nOutputCue2080=[~UNUSED3~]\nOutputSigType2080=Digital\nOutputCue2081=[~UNUSED3~]\nOutputSigType2081=Digital\nOutputCue2082=[~UNUSED3~]\nOutputSigType2082=Digital\nOutputCue2083=[~UNUSED3~]\nOutputSigType2083=Digital\nOutputCue2084=[~UNUSED3~]\nOutputSigType2084=Digital\nOutputCue2085=[~UNUSED3~]\nOutputSigType2085=Digital\nOutputCue2086=[~UNUSED3~]\nOutputSigType2086=Digital\nOutputCue2087=[~UNUSED3~]\nOutputSigType2087=Digital\nOutputCue2088=[~UNUSED3~]\nOutputSigType2088=Digital\nOutputCue2089=[~UNUSED3~]\nOutputSigType2089=Digital\nOutputCue2090=[~UNUSED3~]\nOutputSigType2090=Digital\nOutputCue2091=[~UNUSED3~]\nOutputSigType2091=Digital\nOutputCue2092=[~UNUSED3~]\nOutputSigType2092=Digital\nOutputCue2093=[~UNUSED3~]\nOutputSigType2093=Digital\nOutputCue2094=[~UNUSED3~]\nOutputSigType2094=Digital\nOutputCue2095=[~UNUSED3~]\nOutputSigType2095=Digital\nOutputCue2096=[~UNUSED3~]\nOutputSigType2096=Digital\nOutputCue2097=[~UNUSED3~]\nOutputSigType2097=Digital\nOutputCue2098=[~UNUSED3~]\nOutputSigType2098=Digital\nOutputCue2099=[~UNUSED3~]\nOutputSigType2099=Digital\nOutputCue2100=[~UNUSED3~]\nOutputSigType2100=Digital\nOutputCue2101=[~UNUSED3~]\nOutputSigType2101=Digital\nOutputCue2102=[~UNUSED3~]\nOutputSigType2102=Digital\nOutputCue2103=[~UNUSED3~]\nOutputSigType2103=Digital\nOutputCue2104=[~UNUSED3~]\nOutputSigType2104=Digital\nOutputCue2105=[~UNUSED3~]\nOutputSigType2105=Digital\nOutputCue2106=[~UNUSED3~]\nOutputSigType2106=Digital\nOutputCue2107=[~UNUSED3~]\nOutputSigType2107=Digital\nOutputCue2108=[~UNUSED3~]\nOutputSigType2108=Digital\nOutputCue2109=[~UNUSED3~]\nOutputSigType2109=Digital\nOutputCue2110=[~UNUSED3~]\nOutputSigType2110=Digital\nOutputCue2111=[~UNUSED3~]\nOutputSigType2111=Digital\nOutputCue2112=[~UNUSED3~]\nOutputSigType2112=Digital\nOutputCue2113=[~UNUSED3~]\nOutputSigType2113=Digital\nOutputCue2114=[~UNUSED2~]\nOutputSigType2114=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=press91\nOutputSigType4106=Digital\nOutputCue4107=press92\nOutputSigType4107=Digital\nOutputCue4108=press93\nOutputSigType4108=Digital\nOutputCue4109=press94\nOutputSigType4109=Digital\nOutputCue4110=press95\nOutputSigType4110=Digital\nOutputCue4111=press96\nOutputSigType4111=Digital\nOutputCue4112=press97\nOutputSigType4112=Digital\nOutputCue4113=press98\nOutputSigType4113=Digital\nOutputCue4114=press99\nOutputSigType4114=Digital\nOutputCue4115=press100\nOutputSigType4115=Digital\nOutputCue4116=press101\nOutputSigType4116=Digital\nOutputCue4117=press102\nOutputSigType4117=Digital\nOutputCue4118=press103\nOutputSigType4118=Digital\nOutputCue4119=press104\nOutputSigType4119=Digital\nOutputCue4120=press105\nOutputSigType4120=Digital\nOutputCue4121=press106\nOutputSigType4121=Digital\nOutputCue4122=press107\nOutputSigType4122=Digital\nOutputCue4123=press108\nOutputSigType4123=Digital\nOutputCue4124=press109\nOutputSigType4124=Digital\nOutputCue4125=press110\nOutputSigType4125=Digital\nOutputCue4126=press111\nOutputSigType4126=Digital\nOutputCue4127=press112\nOutputSigType4127=Digital\nOutputCue4128=press113\nOutputSigType4128=Digital\nOutputCue4129=press114\nOutputSigType4129=Digital\nOutputCue4130=press115\nOutputSigType4130=Digital\nOutputCue4131=press116\nOutputSigType4131=Digital\nOutputCue4132=press117\nOutputSigType4132=Digital\nOutputCue4133=press118\nOutputSigType4133=Digital\nOutputCue4134=press119\nOutputSigType4134=Digital\nOutputCue4135=press120\nOutputSigType4135=Digital\nOutputCue4136=press121\nOutputSigType4136=Digital\nOutputCue4137=press122\nOutputSigType4137=Digital\nOutputCue4138=press123\nOutputSigType4138=Digital\nOutputCue4139=press124\nOutputSigType4139=Digital\nOutputCue4140=press125\nOutputSigType4140=Digital\nOutputCue4141=press126\nOutputSigType4141=Digital\nOutputCue4142=press127\nOutputSigType4142=Digital\nOutputCue4143=press128\nOutputSigType4143=Digital\nOutputCue4144=press129\nOutputSigType4144=Digital\nOutputCue4145=press130\nOutputSigType4145=Digital\nOutputCue4146=press131\nOutputSigType4146=Digital\nOutputCue4147=press132\nOutputSigType4147=Digital\nOutputCue4148=press133\nOutputSigType4148=Digital\nOutputCue4149=press134\nOutputSigType4149=Digital\nOutputCue4150=press135\nOutputSigType4150=Digital\nOutputCue4151=press136\nOutputSigType4151=Digital\nOutputCue4152=press137\nOutputSigType4152=Digital\nOutputCue4153=press138\nOutputSigType4153=Digital\nOutputCue4154=press139\nOutputSigType4154=Digital\nOutputCue4155=press140\nOutputSigType4155=Digital\nOutputCue4156=press141\nOutputSigType4156=Digital\nOutputCue4157=press142\nOutputSigType4157=Digital\nOutputCue4158=press143\nOutputSigType4158=Digital\nOutputCue4159=press144\nOutputSigType4159=Digital\nOutputCue4160=press145\nOutputSigType4160=Digital\nOutputCue4161=press146\nOutputSigType4161=Digital\nOutputCue4162=press147\nOutputSigType4162=Digital\nOutputCue4163=press148\nOutputSigType4163=Digital\nOutputCue4164=press149\nOutputSigType4164=Digital\nOutputCue4165=press150\nOutputSigType4165=Digital\nOutputCue4166=press151\nOutputSigType4166=Digital\nOutputCue4167=press152\nOutputSigType4167=Digital\nOutputCue4168=press153\nOutputSigType4168=Digital\nOutputCue4169=press154\nOutputSigType4169=Digital\nOutputCue4170=press155\nOutputSigType4170=Digital\nOutputCue4171=press156\nOutputSigType4171=Digital\nOutputCue4172=press157\nOutputSigType4172=Digital\nOutputCue4173=press158\nOutputSigType4173=Digital\nOutputCue4174=press159\nOutputSigType4174=Digital\nOutputCue4175=press160\nOutputSigType4175=Digital\nOutputCue4176=press161\nOutputSigType4176=Digital\nOutputCue4177=press162\nOutputSigType4177=Digital\nOutputCue4178=press163\nOutputSigType4178=Digital\nOutputCue4179=press164\nOutputSigType4179=Digital\nOutputCue4180=press165\nOutputSigType4180=Digital\nOutputCue4181=press166\nOutputSigType4181=Digital\nOutputCue4182=press167\nOutputSigType4182=Digital\nOutputCue4183=press168\nOutputSigType4183=Digital\nOutputCue4184=press169\nOutputSigType4184=Digital\nOutputCue4185=press170\nOutputSigType4185=Digital\nOutputCue4186=press171\nOutputSigType4186=Digital\nOutputCue4187=press172\nOutputSigType4187=Digital\nOutputCue4188=press173\nOutputSigType4188=Digital\nOutputCue4189=press174\nOutputSigType4189=Digital\nOutputCue4190=press175\nOutputSigType4190=Digital\nOutputCue4191=press176\nOutputSigType4191=Digital\nOutputCue4192=press177\nOutputSigType4192=Digital\nOutputCue4193=press178\nOutputSigType4193=Digital\nOutputCue4194=press179\nOutputSigType4194=Digital\nOutputCue4195=press180\nOutputSigType4195=Digital\nOutputCue4196=press181\nOutputSigType4196=Digital\nOutputCue4197=press182\nOutputSigType4197=Digital\nOutputCue4198=press183\nOutputSigType4198=Digital\nOutputCue4199=press184\nOutputSigType4199=Digital\nOutputCue4200=press185\nOutputSigType4200=Digital\nOutputCue4201=press186\nOutputSigType4201=Digital\nOutputCue4202=press187\nOutputSigType4202=Digital\nOutputCue4203=press188\nOutputSigType4203=Digital\nOutputCue4204=press189\nOutputSigType4204=Digital\nOutputCue4205=press190\nOutputSigType4205=Digital\nOutputCue4206=press191\nOutputSigType4206=Digital\nOutputCue4207=press192\nOutputSigType4207=Digital\nOutputCue4208=press193\nOutputSigType4208=Digital\nOutputCue4209=press194\nOutputSigType4209=Digital\nOutputCue4210=press195\nOutputSigType4210=Digital\nOutputCue4211=press196\nOutputSigType4211=Digital\nOutputCue4212=press197\nOutputSigType4212=Digital\nOutputCue4213=press198\nOutputSigType4213=Digital\nOutputCue4214=press199\nOutputSigType4214=Digital\nOutputCue4215=press200\nOutputSigType4215=Digital\nOutputCue4216=press201\nOutputSigType4216=Digital\nOutputCue4217=press202\nOutputSigType4217=Digital\nOutputCue4218=press203\nOutputSigType4218=Digital\nOutputCue4219=press204\nOutputSigType4219=Digital\nOutputCue4220=press205\nOutputSigType4220=Digital\nOutputCue4221=press206\nOutputSigType4221=Digital\nOutputCue4222=press207\nOutputSigType4222=Digital\nOutputCue4223=press208\nOutputSigType4223=Digital\nOutputCue4224=press209\nOutputSigType4224=Digital\nOutputCue4225=press210\nOutputSigType4225=Digital\nOutputCue4226=press211\nOutputSigType4226=Digital\nOutputCue4227=press212\nOutputSigType4227=Digital\nOutputCue4228=press213\nOutputSigType4228=Digital\nOutputCue4229=press214\nOutputSigType4229=Digital\nOutputCue4230=press215\nOutputSigType4230=Digital\nOutputCue4231=press216\nOutputSigType4231=Digital\nOutputCue4232=press217\nOutputSigType4232=Digital\nOutputCue4233=press218\nOutputSigType4233=Digital\nOutputCue4234=press219\nOutputSigType4234=Digital\nOutputCue4235=press220\nOutputSigType4235=Digital\nOutputCue4236=press221\nOutputSigType4236=Digital\nOutputCue4237=press222\nOutputSigType4237=Digital\nOutputCue4238=press223\nOutputSigType4238=Digital\nOutputCue4239=press224\nOutputSigType4239=Digital\nOutputCue4240=press225\nOutputSigType4240=Digital\nOutputCue4241=press226\nOutputSigType4241=Digital\nOutputCue4242=press227\nOutputSigType4242=Digital\nOutputCue4243=press228\nOutputSigType4243=Digital\nOutputCue4244=press229\nOutputSigType4244=Digital\nOutputCue4245=press230\nOutputSigType4245=Digital\nOutputCue4246=press231\nOutputSigType4246=Digital\nOutputCue4247=press232\nOutputSigType4247=Digital\nOutputCue4248=press233\nOutputSigType4248=Digital\nOutputCue4249=press234\nOutputSigType4249=Digital\nOutputCue4250=press235\nOutputSigType4250=Digital\nOutputCue4251=press236\nOutputSigType4251=Digital\nOutputCue4252=press237\nOutputSigType4252=Digital\nOutputCue4253=press238\nOutputSigType4253=Digital\nOutputCue4254=press239\nOutputSigType4254=Digital\nOutputCue4255=press240\nOutputSigType4255=Digital\nOutputCue4256=press241\nOutputSigType4256=Digital\nOutputCue4257=press242\nOutputSigType4257=Digital\nOutputCue4258=press243\nOutputSigType4258=Digital\nOutputCue4259=press244\nOutputSigType4259=Digital\nOutputCue4260=press245\nOutputSigType4260=Digital\nOutputCue4261=press246\nOutputSigType4261=Digital\nOutputCue4262=press247\nOutputSigType4262=Digital\nOutputCue4263=press248\nOutputSigType4263=Digital\nOutputCue4264=press249\nOutputSigType4264=Digital\nOutputCue4265=press250\nOutputSigType4265=Digital\nOutputCue4266=press251\nOutputSigType4266=Digital\nOutputCue4267=press252\nOutputSigType4267=Digital\nOutputCue4268=press253\nOutputSigType4268=Digital\nOutputCue4269=press254\nOutputSigType4269=Digital\nOutputCue4270=press255\nOutputSigType4270=Digital\nOutputCue4271=press256\nOutputSigType4271=Digital\nOutputCue4272=press257\nOutputSigType4272=Digital\nOutputCue4273=press258\nOutputSigType4273=Digital\nOutputCue4274=press259\nOutputSigType4274=Digital\nOutputCue4275=press260\nOutputSigType4275=Digital\nOutputCue4276=press261\nOutputSigType4276=Digital\nOutputCue4277=press262\nOutputSigType4277=Digital\nOutputCue4278=press263\nOutputSigType4278=Digital\nOutputCue4279=press264\nOutputSigType4279=Digital\nOutputCue4280=press265\nOutputSigType4280=Digital\nOutputCue4281=press266\nOutputSigType4281=Digital\nOutputCue4282=press267\nOutputSigType4282=Digital\nOutputCue4283=press268\nOutputSigType4283=Digital\nOutputCue4284=press269\nOutputSigType4284=Digital\nOutputCue4285=press270\nOutputSigType4285=Digital\nOutputCue4286=press271\nOutputSigType4286=Digital\nOutputCue4287=press272\nOutputSigType4287=Digital\nOutputCue4288=press273\nOutputSigType4288=Digital\nOutputCue4289=press274\nOutputSigType4289=Digital\nOutputCue4290=press275\nOutputSigType4290=Digital\nOutputCue4291=press276\nOutputSigType4291=Digital\nOutputCue4292=press277\nOutputSigType4292=Digital\nOutputCue4293=press278\nOutputSigType4293=Digital\nOutputCue4294=press279\nOutputSigType4294=Digital\nOutputCue4295=press280\nOutputSigType4295=Digital\nOutputCue4296=press281\nOutputSigType4296=Digital\nOutputCue4297=press282\nOutputSigType4297=Digital\nOutputCue4298=press283\nOutputSigType4298=Digital\nOutputCue4299=press284\nOutputSigType4299=Digital\nOutputCue4300=press285\nOutputSigType4300=Digital\nOutputCue4301=press286\nOutputSigType4301=Digital\nOutputCue4302=press287\nOutputSigType4302=Digital\nOutputCue4303=press288\nOutputSigType4303=Digital\nOutputCue4304=press289\nOutputSigType4304=Digital\nOutputCue4305=press290\nOutputSigType4305=Digital\nOutputCue4306=press291\nOutputSigType4306=Digital\nOutputCue4307=press292\nOutputSigType4307=Digital\nOutputCue4308=press293\nOutputSigType4308=Digital\nOutputCue4309=press294\nOutputSigType4309=Digital\nOutputCue4310=press295\nOutputSigType4310=Digital\nOutputCue4311=press296\nOutputSigType4311=Digital\nOutputCue4312=press297\nOutputSigType4312=Digital\nOutputCue4313=press298\nOutputSigType4313=Digital\nOutputCue4314=press299\nOutputSigType4314=Digital\nOutputCue4315=press300\nOutputSigType4315=Digital\nOutputCue4316=[~EndGroup~]Press\nOutputSigType4316=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=text-o121\nInputList3SigType132=Serial\nInputList3Cue133=text-o122\nInputList3SigType133=Serial\nInputList3Cue134=text-o123\nInputList3SigType134=Serial\nInputList3Cue135=text-o124\nInputList3SigType135=Serial\nInputList3Cue136=text-o125\nInputList3SigType136=Serial\nInputList3Cue137=text-o126\nInputList3SigType137=Serial\nInputList3Cue138=text-o127\nInputList3SigType138=Serial\nInputList3Cue139=text-o128\nInputList3SigType139=Serial\nInputList3Cue140=text-o129\nInputList3SigType140=Serial\nInputList3Cue141=text-o130\nInputList3SigType141=Serial\nInputList3Cue142=text-o131\nInputList3SigType142=Serial\nInputList3Cue143=text-o132\nInputList3SigType143=Serial\nInputList3Cue144=text-o133\nInputList3SigType144=Serial\nInputList3Cue145=text-o134\nInputList3SigType145=Serial\nInputList3Cue146=text-o135\nInputList3SigType146=Serial\nInputList3Cue147=text-o136\nInputList3SigType147=Serial\nInputList3Cue148=text-o137\nInputList3SigType148=Serial\nInputList3Cue149=text-o138\nInputList3SigType149=Serial\nInputList3Cue150=text-o139\nInputList3SigType150=Serial\nInputList3Cue151=text-o140\nInputList3SigType151=Serial\nInputList3Cue152=text-o141\nInputList3SigType152=Serial\nInputList3Cue153=text-o142\nInputList3SigType153=Serial\nInputList3Cue154=text-o143\nInputList3SigType154=Serial\nInputList3Cue155=text-o144\nInputList3SigType155=Serial\nInputList3Cue156=text-o145\nInputList3SigType156=Serial\nInputList3Cue157=text-o146\nInputList3SigType157=Serial\nInputList3Cue158=text-o147\nInputList3SigType158=Serial\nInputList3Cue159=text-o148\nInputList3SigType159=Serial\nInputList3Cue160=text-o149\nInputList3SigType160=Serial\nInputList3Cue161=text-o150\nInputList3SigType161=Serial\nInputList3Cue162=text-o151\nInputList3SigType162=Serial\nInputList3Cue163=text-o152\nInputList3SigType163=Serial\nInputList3Cue164=text-o153\nInputList3SigType164=Serial\nInputList3Cue165=text-o154\nInputList3SigType165=Serial\nInputList3Cue166=text-o155\nInputList3SigType166=Serial\nInputList3Cue167=text-o156\nInputList3SigType167=Serial\nInputList3Cue168=text-o157\nInputList3SigType168=Serial\nInputList3Cue169=text-o158\nInputList3SigType169=Serial\nInputList3Cue170=text-o159\nInputList3SigType170=Serial\nInputList3Cue171=text-o160\nInputList3SigType171=Serial\nInputList3Cue172=text-o161\nInputList3SigType172=Serial\nInputList3Cue173=text-o162\nInputList3SigType173=Serial\nInputList3Cue174=text-o163\nInputList3SigType174=Serial\nInputList3Cue175=text-o164\nInputList3SigType175=Serial\nInputList3Cue176=text-o165\nInputList3SigType176=Serial\nInputList3Cue177=text-o166\nInputList3SigType177=Serial\nInputList3Cue178=text-o167\nInputList3SigType178=Serial\nInputList3Cue179=text-o168\nInputList3SigType179=Serial\nInputList3Cue180=text-o169\nInputList3SigType180=Serial\nInputList3Cue181=text-o170\nInputList3SigType181=Serial\nInputList3Cue182=text-o171\nInputList3SigType182=Serial\nInputList3Cue183=text-o172\nInputList3SigType183=Serial\nInputList3Cue184=text-o173\nInputList3SigType184=Serial\nInputList3Cue185=text-o174\nInputList3SigType185=Serial\nInputList3Cue186=text-o175\nInputList3SigType186=Serial\nInputList3Cue187=text-o176\nInputList3SigType187=Serial\nInputList3Cue188=text-o177\nInputList3SigType188=Serial\nInputList3Cue189=text-o178\nInputList3SigType189=Serial\nInputList3Cue190=text-o179\nInputList3SigType190=Serial\nInputList3Cue191=text-o180\nInputList3SigType191=Serial\nInputList3Cue192=text-o181\nInputList3SigType192=Serial\nInputList3Cue193=text-o182\nInputList3SigType193=Serial\nInputList3Cue194=text-o183\nInputList3SigType194=Serial\nInputList3Cue195=text-o184\nInputList3SigType195=Serial\nInputList3Cue196=text-o185\nInputList3SigType196=Serial\nInputList3Cue197=text-o186\nInputList3SigType197=Serial\nInputList3Cue198=text-o187\nInputList3SigType198=Serial\nInputList3Cue199=text-o188\nInputList3SigType199=Serial\nInputList3Cue200=text-o189\nInputList3SigType200=Serial\nInputList3Cue201=text-o190\nInputList3SigType201=Serial\nInputList3Cue202=text-o191\nInputList3SigType202=Serial\nInputList3Cue203=text-o192\nInputList3SigType203=Serial\nInputList3Cue204=text-o193\nInputList3SigType204=Serial\nInputList3Cue205=text-o194\nInputList3SigType205=Serial\nInputList3Cue206=text-o195\nInputList3SigType206=Serial\nInputList3Cue207=text-o196\nInputList3SigType207=Serial\nInputList3Cue208=text-o197\nInputList3SigType208=Serial\nInputList3Cue209=text-o198\nInputList3SigType209=Serial\nInputList3Cue210=text-o199\nInputList3SigType210=Serial\nInputList3Cue211=text-o200\nInputList3SigType211=Serial\nInputList3Cue212=text-o201\nInputList3SigType212=Serial\nInputList3Cue213=text-o202\nInputList3SigType213=Serial\nInputList3Cue214=text-o203\nInputList3SigType214=Serial\nInputList3Cue215=text-o204\nInputList3SigType215=Serial\nInputList3Cue216=text-o205\nInputList3SigType216=Serial\nInputList3Cue217=text-o206\nInputList3SigType217=Serial\nInputList3Cue218=text-o207\nInputList3SigType218=Serial\nInputList3Cue219=text-o208\nInputList3SigType219=Serial\nInputList3Cue220=text-o209\nInputList3SigType220=Serial\nInputList3Cue221=text-o210\nInputList3SigType221=Serial\nInputList3Cue222=text-o211\nInputList3SigType222=Serial\nInputList3Cue223=text-o212\nInputList3SigType223=Serial\nInputList3Cue224=text-o213\nInputList3SigType224=Serial\nInputList3Cue225=text-o214\nInputList3SigType225=Serial\nInputList3Cue226=text-o215\nInputList3SigType226=Serial\nInputList3Cue227=text-o216\nInputList3SigType227=Serial\nInputList3Cue228=text-o217\nInputList3SigType228=Serial\nInputList3Cue229=text-o218\nInputList3SigType229=Serial\nInputList3Cue230=text-o219\nInputList3SigType230=Serial\nInputList3Cue231=text-o220\nInputList3SigType231=Serial\nInputList3Cue232=text-o221\nInputList3SigType232=Serial\nInputList3Cue233=text-o222\nInputList3SigType233=Serial\nInputList3Cue234=text-o223\nInputList3SigType234=Serial\nInputList3Cue235=text-o224\nInputList3SigType235=Serial\nInputList3Cue236=text-o225\nInputList3SigType236=Serial\nInputList3Cue237=text-o226\nInputList3SigType237=Serial\nInputList3Cue238=text-o227\nInputList3SigType238=Serial\nInputList3Cue239=text-o228\nInputList3SigType239=Serial\nInputList3Cue240=text-o229\nInputList3SigType240=Serial\nInputList3Cue241=text-o230\nInputList3SigType241=Serial\nInputList3Cue242=text-o231\nInputList3SigType242=Serial\nInputList3Cue243=text-o232\nInputList3SigType243=Serial\nInputList3Cue244=text-o233\nInputList3SigType244=Serial\nInputList3Cue245=text-o234\nInputList3SigType245=Serial\nInputList3Cue246=text-o235\nInputList3SigType246=Serial\nInputList3Cue247=text-o236\nInputList3SigType247=Serial\nInputList3Cue248=text-o237\nInputList3SigType248=Serial\nInputList3Cue249=text-o238\nInputList3SigType249=Serial\nInputList3Cue250=text-o239\nInputList3SigType250=Serial\nInputList3Cue251=text-o240\nInputList3SigType251=Serial\nInputList3Cue252=text-o241\nInputList3SigType252=Serial\nInputList3Cue253=text-o242\nInputList3SigType253=Serial\nInputList3Cue254=text-o243\nInputList3SigType254=Serial\nInputList3Cue255=text-o244\nInputList3SigType255=Serial\nInputList3Cue256=text-o245\nInputList3SigType256=Serial\nInputList3Cue257=text-o246\nInputList3SigType257=Serial\nInputList3Cue258=text-o247\nInputList3SigType258=Serial\nInputList3Cue259=text-o248\nInputList3SigType259=Serial\nInputList3Cue260=text-o249\nInputList3SigType260=Serial\nInputList3Cue261=text-o250\nInputList3SigType261=Serial\nInputList3Cue262=text-o251\nInputList3SigType262=Serial\nInputList3Cue263=text-o252\nInputList3SigType263=Serial\nInputList3Cue264=text-o253\nInputList3SigType264=Serial\nInputList3Cue265=text-o254\nInputList3SigType265=Serial\nInputList3Cue266=text-o255\nInputList3SigType266=Serial\nInputList3Cue267=text-o256\nInputList3SigType267=Serial\nInputList3Cue268=text-o257\nInputList3SigType268=Serial\nInputList3Cue269=text-o258\nInputList3SigType269=Serial\nInputList3Cue270=text-o259\nInputList3SigType270=Serial\nInputList3Cue271=text-o260\nInputList3SigType271=Serial\nInputList3Cue272=text-o261\nInputList3SigType272=Serial\nInputList3Cue273=text-o262\nInputList3SigType273=Serial\nInputList3Cue274=text-o263\nInputList3SigType274=Serial\nInputList3Cue275=text-o264\nInputList3SigType275=Serial\nInputList3Cue276=text-o265\nInputList3SigType276=Serial\nInputList3Cue277=text-o266\nInputList3SigType277=Serial\nInputList3Cue278=text-o267\nInputList3SigType278=Serial\nInputList3Cue279=text-o268\nInputList3SigType279=Serial\nInputList3Cue280=text-o269\nInputList3SigType280=Serial\nInputList3Cue281=text-o270\nInputList3SigType281=Serial\nInputList3Cue282=text-o271\nInputList3SigType282=Serial\nInputList3Cue283=text-o272\nInputList3SigType283=Serial\nInputList3Cue284=text-o273\nInputList3SigType284=Serial\nInputList3Cue285=text-o274\nInputList3SigType285=Serial\nInputList3Cue286=text-o275\nInputList3SigType286=Serial\nInputList3Cue287=text-o276\nInputList3SigType287=Serial\nInputList3Cue288=text-o277\nInputList3SigType288=Serial\nInputList3Cue289=text-o278\nInputList3SigType289=Serial\nInputList3Cue290=text-o279\nInputList3SigType290=Serial\nInputList3Cue291=text-o280\nInputList3SigType291=Serial\nInputList3Cue292=text-o281\nInputList3SigType292=Serial\nInputList3Cue293=text-o282\nInputList3SigType293=Serial\nInputList3Cue294=text-o283\nInputList3SigType294=Serial\nInputList3Cue295=text-o284\nInputList3SigType295=Serial\nInputList3Cue296=text-o285\nInputList3SigType296=Serial\nInputList3Cue297=text-o286\nInputList3SigType297=Serial\nInputList3Cue298=text-o287\nInputList3SigType298=Serial\nInputList3Cue299=text-o288\nInputList3SigType299=Serial\nInputList3Cue300=text-o289\nInputList3SigType300=Serial\nInputList3Cue301=text-o290\nInputList3SigType301=Serial\nInputList3Cue302=text-o291\nInputList3SigType302=Serial\nInputList3Cue303=text-o292\nInputList3SigType303=Serial\nInputList3Cue304=text-o293\nInputList3SigType304=Serial\nInputList3Cue305=text-o294\nInputList3SigType305=Serial\nInputList3Cue306=text-o295\nInputList3SigType306=Serial\nInputList3Cue307=text-o296\nInputList3SigType307=Serial\nInputList3Cue308=text-o297\nInputList3SigType308=Serial\nInputList3Cue309=text-o298\nInputList3SigType309=Serial\nInputList3Cue310=text-o299\nInputList3SigType310=Serial\nInputList3Cue311=text-o300\nInputList3SigType311=Serial\nInputList3Cue312=text-o301\nInputList3SigType312=Serial\nInputList3Cue313=text-o302\nInputList3SigType313=Serial\nInputList3Cue314=text-o303\nInputList3SigType314=Serial\nInputList3Cue315=text-o304\nInputList3SigType315=Serial\nInputList3Cue316=text-o305\nInputList3SigType316=Serial\nInputList3Cue317=text-o306\nInputList3SigType317=Serial\nInputList3Cue318=text-o307\nInputList3SigType318=Serial\nInputList3Cue319=text-o308\nInputList3SigType319=Serial\nInputList3Cue320=text-o309\nInputList3SigType320=Serial\nInputList3Cue321=text-o310\nInputList3SigType321=Serial\nInputList3Cue322=text-o311\nInputList3SigType322=Serial\nInputList3Cue323=text-o312\nInputList3SigType323=Serial\nInputList3Cue324=text-o313\nInputList3SigType324=Serial\nInputList3Cue325=text-o314\nInputList3SigType325=Serial\nInputList3Cue326=text-o315\nInputList3SigType326=Serial\nInputList3Cue327=text-o316\nInputList3SigType327=Serial\nInputList3Cue328=text-o317\nInputList3SigType328=Serial\nInputList3Cue329=text-o318\nInputList3SigType329=Serial\nInputList3Cue330=text-o319\nInputList3SigType330=Serial\nInputList3Cue331=text-o320\nInputList3SigType331=Serial\nInputList3Cue332=text-o321\nInputList3SigType332=Serial\nInputList3Cue333=text-o322\nInputList3SigType333=Serial\nInputList3Cue334=text-o323\nInputList3SigType334=Serial\nInputList3Cue335=text-o324\nInputList3SigType335=Serial\nInputList3Cue336=text-o325\nInputList3SigType336=Serial\nInputList3Cue337=text-o326\nInputList3SigType337=Serial\nInputList3Cue338=text-o327\nInputList3SigType338=Serial\nInputList3Cue339=text-o328\nInputList3SigType339=Serial\nInputList3Cue340=text-o329\nInputList3SigType340=Serial\nInputList3Cue341=text-o330\nInputList3SigType341=Serial\nInputList3Cue342=text-o331\nInputList3SigType342=Serial\nInputList3Cue343=text-o332\nInputList3SigType343=Serial\nInputList3Cue344=text-o333\nInputList3SigType344=Serial\nInputList3Cue345=text-o334\nInputList3SigType345=Serial\nInputList3Cue346=text-o335\nInputList3SigType346=Serial\nInputList3Cue347=text-o336\nInputList3SigType347=Serial\nInputList3Cue348=text-o337\nInputList3SigType348=Serial\nInputList3Cue349=text-o338\nInputList3SigType349=Serial\nInputList3Cue350=text-o339\nInputList3SigType350=Serial\nInputList3Cue351=text-o340\nInputList3SigType351=Serial\nInputList3Cue352=text-o341\nInputList3SigType352=Serial\nInputList3Cue353=text-o342\nInputList3SigType353=Serial\nInputList3Cue354=text-o343\nInputList3SigType354=Serial\nInputList3Cue355=text-o344\nInputList3SigType355=Serial\nInputList3Cue356=text-o345\nInputList3SigType356=Serial\nInputList3Cue357=text-o346\nInputList3SigType357=Serial\nInputList3Cue358=text-o347\nInputList3SigType358=Serial\nInputList3Cue359=text-o348\nInputList3SigType359=Serial\nInputList3Cue360=text-o349\nInputList3SigType360=Serial\nInputList3Cue361=text-o350\nInputList3SigType361=Serial\nInputList3Cue362=text-o351\nInputList3SigType362=Serial\nInputList3Cue363=text-o352\nInputList3SigType363=Serial\nInputList3Cue364=text-o353\nInputList3SigType364=Serial\nInputList3Cue365=text-o354\nInputList3SigType365=Serial\nInputList3Cue366=text-o355\nInputList3SigType366=Serial\nInputList3Cue367=text-o356\nInputList3SigType367=Serial\nInputList3Cue368=text-o357\nInputList3SigType368=Serial\nInputList3Cue369=text-o358\nInputList3SigType369=Serial\nInputList3Cue370=text-o359\nInputList3SigType370=Serial\nInputList3Cue371=text-o360\nInputList3SigType371=Serial\nInputList3Cue372=text-o361\nInputList3SigType372=Serial\nInputList3Cue373=text-o362\nInputList3SigType373=Serial\nInputList3Cue374=text-o363\nInputList3SigType374=Serial\nInputList3Cue375=text-o364\nInputList3SigType375=Serial\nInputList3Cue376=text-o365\nInputList3SigType376=Serial\nInputList3Cue377=text-o366\nInputList3SigType377=Serial\nInputList3Cue378=text-o367\nInputList3SigType378=Serial\nInputList3Cue379=text-o368\nInputList3SigType379=Serial\nInputList3Cue380=text-o369\nInputList3SigType380=Serial\nInputList3Cue381=text-o370\nInputList3SigType381=Serial\nInputList3Cue382=text-o371\nInputList3SigType382=Serial\nInputList3Cue383=text-o372\nInputList3SigType383=Serial\nInputList3Cue384=text-o373\nInputList3SigType384=Serial\nInputList3Cue385=text-o374\nInputList3SigType385=Serial\nInputList3Cue386=text-o375\nInputList3SigType386=Serial\nInputList3Cue387=text-o376\nInputList3SigType387=Serial\nInputList3Cue388=text-o377\nInputList3SigType388=Serial\nInputList3Cue389=text-o378\nInputList3SigType389=Serial\nInputList3Cue390=text-o379\nInputList3SigType390=Serial\nInputList3Cue391=text-o380\nInputList3SigType391=Serial\nInputList3Cue392=text-o381\nInputList3SigType392=Serial\nInputList3Cue393=text-o382\nInputList3SigType393=Serial\nInputList3Cue394=text-o383\nInputList3SigType394=Serial\nInputList3Cue395=text-o384\nInputList3SigType395=Serial\nInputList3Cue396=text-o385\nInputList3SigType396=Serial\nInputList3Cue397=text-o386\nInputList3SigType397=Serial\nInputList3Cue398=text-o387\nInputList3SigType398=Serial\nInputList3Cue399=text-o388\nInputList3SigType399=Serial\nInputList3Cue400=text-o389\nInputList3SigType400=Serial\nInputList3Cue401=text-o390\nInputList3SigType401=Serial\nInputList3Cue402=text-o391\nInputList3SigType402=Serial\nInputList3Cue403=text-o392\nInputList3SigType403=Serial\nInputList3Cue404=text-o393\nInputList3SigType404=Serial\nInputList3Cue405=text-o394\nInputList3SigType405=Serial\nInputList3Cue406=text-o395\nInputList3SigType406=Serial\nInputList3Cue407=text-o396\nInputList3SigType407=Serial\nInputList3Cue408=text-o397\nInputList3SigType408=Serial\nInputList3Cue409=text-o398\nInputList3SigType409=Serial\nInputList3Cue410=text-o399\nInputList3SigType410=Serial\nInputList3Cue411=text-o400\nInputList3SigType411=Serial\nInputList3Cue412=[~EndGroup~]text-o\nInputList3SigType412=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=text-i121\nOutputList3SigType132=Serial\nOutputList3Cue133=text-i122\nOutputList3SigType133=Serial\nOutputList3Cue134=text-i123\nOutputList3SigType134=Serial\nOutputList3Cue135=text-i124\nOutputList3SigType135=Serial\nOutputList3Cue136=text-i125\nOutputList3SigType136=Serial\nOutputList3Cue137=text-i126\nOutputList3SigType137=Serial\nOutputList3Cue138=text-i127\nOutputList3SigType138=Serial\nOutputList3Cue139=text-i128\nOutputList3SigType139=Serial\nOutputList3Cue140=text-i129\nOutputList3SigType140=Serial\nOutputList3Cue141=text-i130\nOutputList3SigType141=Serial\nOutputList3Cue142=text-i131\nOutputList3SigType142=Serial\nOutputList3Cue143=text-i132\nOutputList3SigType143=Serial\nOutputList3Cue144=text-i133\nOutputList3SigType144=Serial\nOutputList3Cue145=text-i134\nOutputList3SigType145=Serial\nOutputList3Cue146=text-i135\nOutputList3SigType146=Serial\nOutputList3Cue147=text-i136\nOutputList3SigType147=Serial\nOutputList3Cue148=text-i137\nOutputList3SigType148=Serial\nOutputList3Cue149=text-i138\nOutputList3SigType149=Serial\nOutputList3Cue150=text-i139\nOutputList3SigType150=Serial\nOutputList3Cue151=text-i140\nOutputList3SigType151=Serial\nOutputList3Cue152=text-i141\nOutputList3SigType152=Serial\nOutputList3Cue153=text-i142\nOutputList3SigType153=Serial\nOutputList3Cue154=text-i143\nOutputList3SigType154=Serial\nOutputList3Cue155=text-i144\nOutputList3SigType155=Serial\nOutputList3Cue156=text-i145\nOutputList3SigType156=Serial\nOutputList3Cue157=text-i146\nOutputList3SigType157=Serial\nOutputList3Cue158=text-i147\nOutputList3SigType158=Serial\nOutputList3Cue159=text-i148\nOutputList3SigType159=Serial\nOutputList3Cue160=text-i149\nOutputList3SigType160=Serial\nOutputList3Cue161=text-i150\nOutputList3SigType161=Serial\nOutputList3Cue162=text-i151\nOutputList3SigType162=Serial\nOutputList3Cue163=text-i152\nOutputList3SigType163=Serial\nOutputList3Cue164=text-i153\nOutputList3SigType164=Serial\nOutputList3Cue165=text-i154\nOutputList3SigType165=Serial\nOutputList3Cue166=text-i155\nOutputList3SigType166=Serial\nOutputList3Cue167=text-i156\nOutputList3SigType167=Serial\nOutputList3Cue168=text-i157\nOutputList3SigType168=Serial\nOutputList3Cue169=text-i158\nOutputList3SigType169=Serial\nOutputList3Cue170=text-i159\nOutputList3SigType170=Serial\nOutputList3Cue171=text-i160\nOutputList3SigType171=Serial\nOutputList3Cue172=text-i161\nOutputList3SigType172=Serial\nOutputList3Cue173=text-i162\nOutputList3SigType173=Serial\nOutputList3Cue174=text-i163\nOutputList3SigType174=Serial\nOutputList3Cue175=text-i164\nOutputList3SigType175=Serial\nOutputList3Cue176=text-i165\nOutputList3SigType176=Serial\nOutputList3Cue177=text-i166\nOutputList3SigType177=Serial\nOutputList3Cue178=text-i167\nOutputList3SigType178=Serial\nOutputList3Cue179=text-i168\nOutputList3SigType179=Serial\nOutputList3Cue180=text-i169\nOutputList3SigType180=Serial\nOutputList3Cue181=text-i170\nOutputList3SigType181=Serial\nOutputList3Cue182=text-i171\nOutputList3SigType182=Serial\nOutputList3Cue183=text-i172\nOutputList3SigType183=Serial\nOutputList3Cue184=text-i173\nOutputList3SigType184=Serial\nOutputList3Cue185=text-i174\nOutputList3SigType185=Serial\nOutputList3Cue186=text-i175\nOutputList3SigType186=Serial\nOutputList3Cue187=text-i176\nOutputList3SigType187=Serial\nOutputList3Cue188=text-i177\nOutputList3SigType188=Serial\nOutputList3Cue189=text-i178\nOutputList3SigType189=Serial\nOutputList3Cue190=text-i179\nOutputList3SigType190=Serial\nOutputList3Cue191=text-i180\nOutputList3SigType191=Serial\nOutputList3Cue192=text-i181\nOutputList3SigType192=Serial\nOutputList3Cue193=text-i182\nOutputList3SigType193=Serial\nOutputList3Cue194=text-i183\nOutputList3SigType194=Serial\nOutputList3Cue195=text-i184\nOutputList3SigType195=Serial\nOutputList3Cue196=text-i185\nOutputList3SigType196=Serial\nOutputList3Cue197=text-i186\nOutputList3SigType197=Serial\nOutputList3Cue198=text-i187\nOutputList3SigType198=Serial\nOutputList3Cue199=text-i188\nOutputList3SigType199=Serial\nOutputList3Cue200=text-i189\nOutputList3SigType200=Serial\nOutputList3Cue201=text-i190\nOutputList3SigType201=Serial\nOutputList3Cue202=text-i191\nOutputList3SigType202=Serial\nOutputList3Cue203=text-i192\nOutputList3SigType203=Serial\nOutputList3Cue204=text-i193\nOutputList3SigType204=Serial\nOutputList3Cue205=text-i194\nOutputList3SigType205=Serial\nOutputList3Cue206=text-i195\nOutputList3SigType206=Serial\nOutputList3Cue207=text-i196\nOutputList3SigType207=Serial\nOutputList3Cue208=text-i197\nOutputList3SigType208=Serial\nOutputList3Cue209=text-i198\nOutputList3SigType209=Serial\nOutputList3Cue210=text-i199\nOutputList3SigType210=Serial\nOutputList3Cue211=text-i200\nOutputList3SigType211=Serial\nOutputList3Cue212=text-i201\nOutputList3SigType212=Serial\nOutputList3Cue213=text-i202\nOutputList3SigType213=Serial\nOutputList3Cue214=text-i203\nOutputList3SigType214=Serial\nOutputList3Cue215=text-i204\nOutputList3SigType215=Serial\nOutputList3Cue216=text-i205\nOutputList3SigType216=Serial\nOutputList3Cue217=text-i206\nOutputList3SigType217=Serial\nOutputList3Cue218=text-i207\nOutputList3SigType218=Serial\nOutputList3Cue219=text-i208\nOutputList3SigType219=Serial\nOutputList3Cue220=text-i209\nOutputList3SigType220=Serial\nOutputList3Cue221=text-i210\nOutputList3SigType221=Serial\nOutputList3Cue222=text-i211\nOutputList3SigType222=Serial\nOutputList3Cue223=text-i212\nOutputList3SigType223=Serial\nOutputList3Cue224=text-i213\nOutputList3SigType224=Serial\nOutputList3Cue225=text-i214\nOutputList3SigType225=Serial\nOutputList3Cue226=text-i215\nOutputList3SigType226=Serial\nOutputList3Cue227=text-i216\nOutputList3SigType227=Serial\nOutputList3Cue228=text-i217\nOutputList3SigType228=Serial\nOutputList3Cue229=text-i218\nOutputList3SigType229=Serial\nOutputList3Cue230=text-i219\nOutputList3SigType230=Serial\nOutputList3Cue231=text-i220\nOutputList3SigType231=Serial\nOutputList3Cue232=text-i221\nOutputList3SigType232=Serial\nOutputList3Cue233=text-i222\nOutputList3SigType233=Serial\nOutputList3Cue234=text-i223\nOutputList3SigType234=Serial\nOutputList3Cue235=text-i224\nOutputList3SigType235=Serial\nOutputList3Cue236=text-i225\nOutputList3SigType236=Serial\nOutputList3Cue237=text-i226\nOutputList3SigType237=Serial\nOutputList3Cue238=text-i227\nOutputList3SigType238=Serial\nOutputList3Cue239=text-i228\nOutputList3SigType239=Serial\nOutputList3Cue240=text-i229\nOutputList3SigType240=Serial\nOutputList3Cue241=text-i230\nOutputList3SigType241=Serial\nOutputList3Cue242=text-i231\nOutputList3SigType242=Serial\nOutputList3Cue243=text-i232\nOutputList3SigType243=Serial\nOutputList3Cue244=text-i233\nOutputList3SigType244=Serial\nOutputList3Cue245=text-i234\nOutputList3SigType245=Serial\nOutputList3Cue246=text-i235\nOutputList3SigType246=Serial\nOutputList3Cue247=text-i236\nOutputList3SigType247=Serial\nOutputList3Cue248=text-i237\nOutputList3SigType248=Serial\nOutputList3Cue249=text-i238\nOutputList3SigType249=Serial\nOutputList3Cue250=text-i239\nOutputList3SigType250=Serial\nOutputList3Cue251=text-i240\nOutputList3SigType251=Serial\nOutputList3Cue252=text-i241\nOutputList3SigType252=Serial\nOutputList3Cue253=text-i242\nOutputList3SigType253=Serial\nOutputList3Cue254=text-i243\nOutputList3SigType254=Serial\nOutputList3Cue255=text-i244\nOutputList3SigType255=Serial\nOutputList3Cue256=text-i245\nOutputList3SigType256=Serial\nOutputList3Cue257=text-i246\nOutputList3SigType257=Serial\nOutputList3Cue258=text-i247\nOutputList3SigType258=Serial\nOutputList3Cue259=text-i248\nOutputList3SigType259=Serial\nOutputList3Cue260=text-i249\nOutputList3SigType260=Serial\nOutputList3Cue261=text-i250\nOutputList3SigType261=Serial\nOutputList3Cue262=text-i251\nOutputList3SigType262=Serial\nOutputList3Cue263=text-i252\nOutputList3SigType263=Serial\nOutputList3Cue264=text-i253\nOutputList3SigType264=Serial\nOutputList3Cue265=text-i254\nOutputList3SigType265=Serial\nOutputList3Cue266=text-i255\nOutputList3SigType266=Serial\nOutputList3Cue267=text-i256\nOutputList3SigType267=Serial\nOutputList3Cue268=text-i257\nOutputList3SigType268=Serial\nOutputList3Cue269=text-i258\nOutputList3SigType269=Serial\nOutputList3Cue270=text-i259\nOutputList3SigType270=Serial\nOutputList3Cue271=text-i260\nOutputList3SigType271=Serial\nOutputList3Cue272=text-i261\nOutputList3SigType272=Serial\nOutputList3Cue273=text-i262\nOutputList3SigType273=Serial\nOutputList3Cue274=text-i263\nOutputList3SigType274=Serial\nOutputList3Cue275=text-i264\nOutputList3SigType275=Serial\nOutputList3Cue276=text-i265\nOutputList3SigType276=Serial\nOutputList3Cue277=text-i266\nOutputList3SigType277=Serial\nOutputList3Cue278=text-i267\nOutputList3SigType278=Serial\nOutputList3Cue279=text-i268\nOutputList3SigType279=Serial\nOutputList3Cue280=text-i269\nOutputList3SigType280=Serial\nOutputList3Cue281=text-i270\nOutputList3SigType281=Serial\nOutputList3Cue282=text-i271\nOutputList3SigType282=Serial\nOutputList3Cue283=text-i272\nOutputList3SigType283=Serial\nOutputList3Cue284=text-i273\nOutputList3SigType284=Serial\nOutputList3Cue285=text-i274\nOutputList3SigType285=Serial\nOutputList3Cue286=text-i275\nOutputList3SigType286=Serial\nOutputList3Cue287=text-i276\nOutputList3SigType287=Serial\nOutputList3Cue288=text-i277\nOutputList3SigType288=Serial\nOutputList3Cue289=text-i278\nOutputList3SigType289=Serial\nOutputList3Cue290=text-i279\nOutputList3SigType290=Serial\nOutputList3Cue291=text-i280\nOutputList3SigType291=Serial\nOutputList3Cue292=text-i281\nOutputList3SigType292=Serial\nOutputList3Cue293=text-i282\nOutputList3SigType293=Serial\nOutputList3Cue294=text-i283\nOutputList3SigType294=Serial\nOutputList3Cue295=text-i284\nOutputList3SigType295=Serial\nOutputList3Cue296=text-i285\nOutputList3SigType296=Serial\nOutputList3Cue297=text-i286\nOutputList3SigType297=Serial\nOutputList3Cue298=text-i287\nOutputList3SigType298=Serial\nOutputList3Cue299=text-i288\nOutputList3SigType299=Serial\nOutputList3Cue300=text-i289\nOutputList3SigType300=Serial\nOutputList3Cue301=text-i290\nOutputList3SigType301=Serial\nOutputList3Cue302=text-i291\nOutputList3SigType302=Serial\nOutputList3Cue303=text-i292\nOutputList3SigType303=Serial\nOutputList3Cue304=text-i293\nOutputList3SigType304=Serial\nOutputList3Cue305=text-i294\nOutputList3SigType305=Serial\nOutputList3Cue306=text-i295\nOutputList3SigType306=Serial\nOutputList3Cue307=text-i296\nOutputList3SigType307=Serial\nOutputList3Cue308=text-i297\nOutputList3SigType308=Serial\nOutputList3Cue309=text-i298\nOutputList3SigType309=Serial\nOutputList3Cue310=text-i299\nOutputList3SigType310=Serial\nOutputList3Cue311=text-i300\nOutputList3SigType311=Serial\nOutputList3Cue312=text-i301\nOutputList3SigType312=Serial\nOutputList3Cue313=text-i302\nOutputList3SigType313=Serial\nOutputList3Cue314=text-i303\nOutputList3SigType314=Serial\nOutputList3Cue315=text-i304\nOutputList3SigType315=Serial\nOutputList3Cue316=text-i305\nOutputList3SigType316=Serial\nOutputList3Cue317=text-i306\nOutputList3SigType317=Serial\nOutputList3Cue318=text-i307\nOutputList3SigType318=Serial\nOutputList3Cue319=text-i308\nOutputList3SigType319=Serial\nOutputList3Cue320=text-i309\nOutputList3SigType320=Serial\nOutputList3Cue321=text-i310\nOutputList3SigType321=Serial\nOutputList3Cue322=text-i311\nOutputList3SigType322=Serial\nOutputList3Cue323=text-i312\nOutputList3SigType323=Serial\nOutputList3Cue324=text-i313\nOutputList3SigType324=Serial\nOutputList3Cue325=text-i314\nOutputList3SigType325=Serial\nOutputList3Cue326=text-i315\nOutputList3SigType326=Serial\nOutputList3Cue327=text-i316\nOutputList3SigType327=Serial\nOutputList3Cue328=text-i317\nOutputList3SigType328=Serial\nOutputList3Cue329=text-i318\nOutputList3SigType329=Serial\nOutputList3Cue330=text-i319\nOutputList3SigType330=Serial\nOutputList3Cue331=text-i320\nOutputList3SigType331=Serial\nOutputList3Cue332=text-i321\nOutputList3SigType332=Serial\nOutputList3Cue333=text-i322\nOutputList3SigType333=Serial\nOutputList3Cue334=text-i323\nOutputList3SigType334=Serial\nOutputList3Cue335=text-i324\nOutputList3SigType335=Serial\nOutputList3Cue336=text-i325\nOutputList3SigType336=Serial\nOutputList3Cue337=text-i326\nOutputList3SigType337=Serial\nOutputList3Cue338=text-i327\nOutputList3SigType338=Serial\nOutputList3Cue339=text-i328\nOutputList3SigType339=Serial\nOutputList3Cue340=text-i329\nOutputList3SigType340=Serial\nOutputList3Cue341=text-i330\nOutputList3SigType341=Serial\nOutputList3Cue342=text-i331\nOutputList3SigType342=Serial\nOutputList3Cue343=text-i332\nOutputList3SigType343=Serial\nOutputList3Cue344=text-i333\nOutputList3SigType344=Serial\nOutputList3Cue345=text-i334\nOutputList3SigType345=Serial\nOutputList3Cue346=text-i335\nOutputList3SigType346=Serial\nOutputList3Cue347=text-i336\nOutputList3SigType347=Serial\nOutputList3Cue348=text-i337\nOutputList3SigType348=Serial\nOutputList3Cue349=text-i338\nOutputList3SigType349=Serial\nOutputList3Cue350=text-i339\nOutputList3SigType350=Serial\nOutputList3Cue351=text-i340\nOutputList3SigType351=Serial\nOutputList3Cue352=text-i341\nOutputList3SigType352=Serial\nOutputList3Cue353=text-i342\nOutputList3SigType353=Serial\nOutputList3Cue354=text-i343\nOutputList3SigType354=Serial\nOutputList3Cue355=text-i344\nOutputList3SigType355=Serial\nOutputList3Cue356=text-i345\nOutputList3SigType356=Serial\nOutputList3Cue357=text-i346\nOutputList3SigType357=Serial\nOutputList3Cue358=text-i347\nOutputList3SigType358=Serial\nOutputList3Cue359=text-i348\nOutputList3SigType359=Serial\nOutputList3Cue360=text-i349\nOutputList3SigType360=Serial\nOutputList3Cue361=text-i350\nOutputList3SigType361=Serial\nOutputList3Cue362=text-i351\nOutputList3SigType362=Serial\nOutputList3Cue363=text-i352\nOutputList3SigType363=Serial\nOutputList3Cue364=text-i353\nOutputList3SigType364=Serial\nOutputList3Cue365=text-i354\nOutputList3SigType365=Serial\nOutputList3Cue366=text-i355\nOutputList3SigType366=Serial\nOutputList3Cue367=text-i356\nOutputList3SigType367=Serial\nOutputList3Cue368=text-i357\nOutputList3SigType368=Serial\nOutputList3Cue369=text-i358\nOutputList3SigType369=Serial\nOutputList3Cue370=text-i359\nOutputList3SigType370=Serial\nOutputList3Cue371=text-i360\nOutputList3SigType371=Serial\nOutputList3Cue372=text-i361\nOutputList3SigType372=Serial\nOutputList3Cue373=text-i362\nOutputList3SigType373=Serial\nOutputList3Cue374=text-i363\nOutputList3SigType374=Serial\nOutputList3Cue375=text-i364\nOutputList3SigType375=Serial\nOutputList3Cue376=text-i365\nOutputList3SigType376=Serial\nOutputList3Cue377=text-i366\nOutputList3SigType377=Serial\nOutputList3Cue378=text-i367\nOutputList3SigType378=Serial\nOutputList3Cue379=text-i368\nOutputList3SigType379=Serial\nOutputList3Cue380=text-i369\nOutputList3SigType380=Serial\nOutputList3Cue381=text-i370\nOutputList3SigType381=Serial\nOutputList3Cue382=text-i371\nOutputList3SigType382=Serial\nOutputList3Cue383=text-i372\nOutputList3SigType383=Serial\nOutputList3Cue384=text-i373\nOutputList3SigType384=Serial\nOutputList3Cue385=text-i374\nOutputList3SigType385=Serial\nOutputList3Cue386=text-i375\nOutputList3SigType386=Serial\nOutputList3Cue387=text-i376\nOutputList3SigType387=Serial\nOutputList3Cue388=text-i377\nOutputList3SigType388=Serial\nOutputList3Cue389=text-i378\nOutputList3SigType389=Serial\nOutputList3Cue390=text-i379\nOutputList3SigType390=Serial\nOutputList3Cue391=text-i380\nOutputList3SigType391=Serial\nOutputList3Cue392=text-i381\nOutputList3SigType392=Serial\nOutputList3Cue393=text-i382\nOutputList3SigType393=Serial\nOutputList3Cue394=text-i383\nOutputList3SigType394=Serial\nOutputList3Cue395=text-i384\nOutputList3SigType395=Serial\nOutputList3Cue396=text-i385\nOutputList3SigType396=Serial\nOutputList3Cue397=text-i386\nOutputList3SigType397=Serial\nOutputList3Cue398=text-i387\nOutputList3SigType398=Serial\nOutputList3Cue399=text-i388\nOutputList3SigType399=Serial\nOutputList3Cue400=text-i389\nOutputList3SigType400=Serial\nOutputList3Cue401=text-i390\nOutputList3SigType401=Serial\nOutputList3Cue402=text-i391\nOutputList3SigType402=Serial\nOutputList3Cue403=text-i392\nOutputList3SigType403=Serial\nOutputList3Cue404=text-i393\nOutputList3SigType404=Serial\nOutputList3Cue405=text-i394\nOutputList3SigType405=Serial\nOutputList3Cue406=text-i395\nOutputList3SigType406=Serial\nOutputList3Cue407=text-i396\nOutputList3SigType407=Serial\nOutputList3Cue408=text-i397\nOutputList3SigType408=Serial\nOutputList3Cue409=text-i398\nOutputList3SigType409=Serial\nOutputList3Cue410=text-i399\nOutputList3SigType410=Serial\nOutputList3Cue411=text-i400\nOutputList3SigType411=Serial\nOutputList3Cue412=[~EndGroup~]text-i\nOutputList3SigType412=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=10\nCedH=10\nSmartObjId=10012d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=10\nTp=1\nHD=TRUE\nDV=10012d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=10\nName=PepperDash Essentials TSW-560_[B.AV] Object - Presets_CATV Icon List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[B.AV] Object - DPad STB_STB DPad.ced\nHint=STB DPad (Smart Object ID=10011)\nCode=11\nSGControlType=DPad\nSGControlName=STB DPad\nGUID=F2340FAD-85E9-4E49-B46E-15E9B096F58B\nSmplCName=PepperDash Essentials TSW-560_[B.AV] Object - DPad STB_STB DPad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10015\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=5\nMaxVariableInputs=5\nMinVariableOutputs=5\nMaxVariableOutputs=5\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Up\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=Down\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Left\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=Right\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=OK\nOutputSigType5=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=11\nCedH=11\nSmartObjId=10011d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=11\nTp=1\nHD=TRUE\nDV=10011d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=11\nName=PepperDash Essentials TSW-560_[B.AV] Object - DPad STB_STB DPad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\nHint=Tab Button Horizontal (Smart Object ID=10081)\nCode=12\nSGControlType=Horizontal Tab Button\nSGControlName=Tab Button Horizontal\nGUID=198C97B0-9D0C-46C2-9557-27EA33C0BE9F\nSmplCName=PepperDash Essentials TSW-560_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10031\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4\nMaxVariableInputs=4\nMinVariableOutputs=4\nMaxVariableOutputs=4\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Tab Button 1 Press\nOutputSigType1=Digital\nInputCue2=Tab Button 1 Select\nInputSigType2=Digital\nOutputCue2=[~UNUSED3~]\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Tab Button 2 Press\nOutputSigType3=Digital\nInputCue4=Tab Button 2 Select\nInputSigType4=Digital\nOutputCue4=[~UNUSED3~]\nOutputSigType4=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=12\nCedH=12\nSmartObjId=10081d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=12\nTp=1\nHD=TRUE\nDV=10081d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=12\nName=PepperDash Essentials TSW-560_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[B.AV] Object - Dpad DVD_DVD DPad.ced\nHint=DVD DPad (Smart Object ID=10411)\nCode=13\nSGControlType=DPad\nSGControlName=DVD DPad\nGUID=D49BCF22-C770-4146-93E6-00867D885E1E\nSmplCName=PepperDash Essentials TSW-560_[B.AV] Object - Dpad DVD_DVD DPad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10015\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=5\nMaxVariableInputs=5\nMinVariableOutputs=5\nMaxVariableOutputs=5\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Up\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=Down\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Left\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=Right\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=OK\nOutputSigType5=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=13\nCedH=13\nSmartObjId=10411d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=13\nTp=1\nHD=TRUE\nDV=10411d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=13\nName=PepperDash Essentials TSW-560_[B.AV] Object - Dpad DVD_DVD DPad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\nHint=CATV Keypad (Smart Object ID=10014)\nCode=14\nSGControlType=Simple Keypad\nSGControlName=CATV Keypad\nGUID=5E33D4F4-5C5F-430C-879A-AE82529B3943\nSmplCName=PepperDash Essentials TSW-560_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=Misc_1\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=Misc_2\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=14\nCedH=14\nSmartObjId=10014d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=14\nTp=1\nHD=TRUE\nDV=10014d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=14\nName=PepperDash Essentials TSW-560_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\nHint=CATV Icon List Vertical (Smart Object ID=3333)\nCode=15\nSGControlType=Subpage Reference List Vertical\nSGControlName=CATV Icon List Vertical\nGUID=D6A79678-BAD2-4F6D-8104-A941FBA9432F\nSmplCName=PepperDash Essentials TSW-560_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4106\nMaxVariableInputs=4106\nMinVariableOutputs=4106\nMaxVariableOutputs=4106\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=132\nMaxVariableInputsList3=132\nMinVariableOutputsList3=132\nMaxVariableOutputsList3=132\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=[~EndGroup~]fb\nInputSigType4106=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=[~EndGroup~]Press\nOutputSigType4106=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=[~EndGroup~]text-o\nInputList3SigType132=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=[~EndGroup~]text-i\nOutputList3SigType132=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=15\nCedH=15\nSmartObjId=3333d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=15\nTp=1\nHD=TRUE\nDV=3333d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=15\nName=PepperDash Essentials TSW-560_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\nHint=Subpage Reference List Vertical_2 (Smart Object ID=1001)\nCode=16\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_2\nGUID=5F63F5C2-A987-4957-9243-C28805CE297E\nSmplCName=PepperDash Essentials TSW-560_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4036\nMaxVariableInputs=4036\nMinVariableOutputs=4036\nMaxVariableOutputs=4036\nNumFixedParams=1\nMinVariableInputsList2=32\nMaxVariableInputsList2=32\nMinVariableOutputsList2=32\nMaxVariableOutputsList2=32\nMinVariableInputsList3=32\nMaxVariableInputsList3=32\nMinVariableOutputsList3=32\nMaxVariableOutputsList3=32\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=[~UNUSED2~]\nInputSigType16=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=[~UNUSED2~]\nInputSigType2018=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=[~EndGroup~]fb\nInputSigType4036=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED2~]\nOutputSigType16=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED2~]\nOutputSigType2018=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=[~EndGroup~]Press\nOutputSigType4036=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=[~EndGroup~]an_fb\nInputList2SigType32=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=[~EndGroup~]an_act\nOutputList2SigType32=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=[~EndGroup~]text-o\nInputList3SigType32=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=[~EndGroup~]text-i\nOutputList3SigType32=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=16\nCedH=16\nSmartObjId=1001d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=16\nTp=1\nHD=TRUE\nDV=1001d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=16\nName=PepperDash Essentials TSW-560_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\nHint=Dynamic Button List Vertical (Smart Object ID=3901)\nCode=17\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical\nGUID=15B95903-FB30-472E-9D10-82A9C3A9D7C7\nSmplCName=PepperDash Essentials TSW-560_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=[~UNUSED2~]\nInputSigType22=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=[~UNUSED2~]\nInputSigType2024=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=[~UNUSED2~]\nInputSigType4026=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=[~UNUSED2~]\nOutputSigType22=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED2~]\nOutputSigType2024=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED2~]\nOutputSigType4026=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=[~UNUSED2~]\nInputList2SigType22=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED2~]\nOutputList2SigType22=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=[~UNUSED2~]\nInputList3SigType22=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=[~UNUSED2~]\nInputList3SigType2024=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED2~]\nOutputList3SigType22=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED2~]\nOutputList3SigType2024=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=17\nCedH=17\nSmartObjId=3901d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=17\nTp=1\nHD=TRUE\nDV=3901d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=17\nName=PepperDash Essentials TSW-560_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\nHint=Subpage Reference List Vertical (Smart Object ID=15023)\nCode=18\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical\nGUID=EC6E6B35-F033-4F14-9465-FEEBE20455CC\nSmplCName=PepperDash Essentials TSW-560_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4052\nMaxVariableInputs=4052\nMinVariableOutputs=4052\nMaxVariableOutputs=4052\nNumFixedParams=1\nMinVariableInputsList2=48\nMaxVariableInputsList2=48\nMinVariableOutputsList2=48\nMaxVariableOutputsList2=48\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=[~UNUSED2~]\nInputSigType24=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=[~UNUSED2~]\nInputSigType2026=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=[~EndGroup~]fb\nInputSigType4052=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED2~]\nOutputSigType24=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED2~]\nOutputSigType2026=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=[~EndGroup~]Press\nOutputSigType4052=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=[~EndGroup~]an_fb\nInputList2SigType48=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=[~EndGroup~]an_act\nOutputList2SigType48=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=18\nCedH=18\nSmartObjId=15023d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=18\nTp=1\nHD=TRUE\nDV=15023d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=18\nName=PepperDash Essentials TSW-560_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-560_[D.VC] Recents_Dynamic Button List Vertical_2.ced\nHint=Dynamic Button List Vertical_2 (Smart Object ID=1203)\nCode=19\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical_2\nGUID=2C4B55E4-42AC-486D-BC6E-C7DD7D24C974\nSmplCName=PepperDash Essentials TSW-560_[D.VC] Recents_Dynamic Button List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=Item 11 Selected\nInputSigType22=Digital\nInputCue23=Item 12 Selected\nInputSigType23=Digital\nInputCue24=Item 13 Selected\nInputSigType24=Digital\nInputCue25=Item 14 Selected\nInputSigType25=Digital\nInputCue26=Item 15 Selected\nInputSigType26=Digital\nInputCue27=Item 16 Selected\nInputSigType27=Digital\nInputCue28=Item 17 Selected\nInputSigType28=Digital\nInputCue29=Item 18 Selected\nInputSigType29=Digital\nInputCue30=Item 19 Selected\nInputSigType30=Digital\nInputCue31=Item 20 Selected\nInputSigType31=Digital\nInputCue32=Item 21 Selected\nInputSigType32=Digital\nInputCue33=Item 22 Selected\nInputSigType33=Digital\nInputCue34=Item 23 Selected\nInputSigType34=Digital\nInputCue35=Item 24 Selected\nInputSigType35=Digital\nInputCue36=Item 25 Selected\nInputSigType36=Digital\nInputCue37=Item 26 Selected\nInputSigType37=Digital\nInputCue38=Item 27 Selected\nInputSigType38=Digital\nInputCue39=Item 28 Selected\nInputSigType39=Digital\nInputCue40=Item 29 Selected\nInputSigType40=Digital\nInputCue41=Item 30 Selected\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=Item 11 Enabled\nInputSigType2024=Digital\nInputCue2025=Item 12 Enabled\nInputSigType2025=Digital\nInputCue2026=Item 13 Enabled\nInputSigType2026=Digital\nInputCue2027=Item 14 Enabled\nInputSigType2027=Digital\nInputCue2028=Item 15 Enabled\nInputSigType2028=Digital\nInputCue2029=Item 16 Enabled\nInputSigType2029=Digital\nInputCue2030=Item 17 Enabled\nInputSigType2030=Digital\nInputCue2031=Item 18 Enabled\nInputSigType2031=Digital\nInputCue2032=Item 19 Enabled\nInputSigType2032=Digital\nInputCue2033=Item 20 Enabled\nInputSigType2033=Digital\nInputCue2034=Item 21 Enabled\nInputSigType2034=Digital\nInputCue2035=Item 22 Enabled\nInputSigType2035=Digital\nInputCue2036=Item 23 Enabled\nInputSigType2036=Digital\nInputCue2037=Item 24 Enabled\nInputSigType2037=Digital\nInputCue2038=Item 25 Enabled\nInputSigType2038=Digital\nInputCue2039=Item 26 Enabled\nInputSigType2039=Digital\nInputCue2040=Item 27 Enabled\nInputSigType2040=Digital\nInputCue2041=Item 28 Enabled\nInputSigType2041=Digital\nInputCue2042=Item 29 Enabled\nInputSigType2042=Digital\nInputCue2043=Item 30 Enabled\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=Item 11 Visible\nInputSigType4026=Digital\nInputCue4027=Item 12 Visible\nInputSigType4027=Digital\nInputCue4028=Item 13 Visible\nInputSigType4028=Digital\nInputCue4029=Item 14 Visible\nInputSigType4029=Digital\nInputCue4030=Item 15 Visible\nInputSigType4030=Digital\nInputCue4031=Item 16 Visible\nInputSigType4031=Digital\nInputCue4032=Item 17 Visible\nInputSigType4032=Digital\nInputCue4033=Item 18 Visible\nInputSigType4033=Digital\nInputCue4034=Item 19 Visible\nInputSigType4034=Digital\nInputCue4035=Item 20 Visible\nInputSigType4035=Digital\nInputCue4036=Item 21 Visible\nInputSigType4036=Digital\nInputCue4037=Item 22 Visible\nInputSigType4037=Digital\nInputCue4038=Item 23 Visible\nInputSigType4038=Digital\nInputCue4039=Item 24 Visible\nInputSigType4039=Digital\nInputCue4040=Item 25 Visible\nInputSigType4040=Digital\nInputCue4041=Item 26 Visible\nInputSigType4041=Digital\nInputCue4042=Item 27 Visible\nInputSigType4042=Digital\nInputCue4043=Item 28 Visible\nInputSigType4043=Digital\nInputCue4044=Item 29 Visible\nInputSigType4044=Digital\nInputCue4045=Item 30 Visible\nInputSigType4045=Digital\nInputCue4046=[~UNUSED2~]\nInputSigType4046=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=Item 11 Pressed\nOutputSigType22=Digital\nOutputCue23=Item 12 Pressed\nOutputSigType23=Digital\nOutputCue24=Item 13 Pressed\nOutputSigType24=Digital\nOutputCue25=Item 14 Pressed\nOutputSigType25=Digital\nOutputCue26=Item 15 Pressed\nOutputSigType26=Digital\nOutputCue27=Item 16 Pressed\nOutputSigType27=Digital\nOutputCue28=Item 17 Pressed\nOutputSigType28=Digital\nOutputCue29=Item 18 Pressed\nOutputSigType29=Digital\nOutputCue30=Item 19 Pressed\nOutputSigType30=Digital\nOutputCue31=Item 20 Pressed\nOutputSigType31=Digital\nOutputCue32=Item 21 Pressed\nOutputSigType32=Digital\nOutputCue33=Item 22 Pressed\nOutputSigType33=Digital\nOutputCue34=Item 23 Pressed\nOutputSigType34=Digital\nOutputCue35=Item 24 Pressed\nOutputSigType35=Digital\nOutputCue36=Item 25 Pressed\nOutputSigType36=Digital\nOutputCue37=Item 26 Pressed\nOutputSigType37=Digital\nOutputCue38=Item 27 Pressed\nOutputSigType38=Digital\nOutputCue39=Item 28 Pressed\nOutputSigType39=Digital\nOutputCue40=Item 29 Pressed\nOutputSigType40=Digital\nOutputCue41=Item 30 Pressed\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED3~]\nOutputSigType4026=Digital\nOutputCue4027=[~UNUSED3~]\nOutputSigType4027=Digital\nOutputCue4028=[~UNUSED3~]\nOutputSigType4028=Digital\nOutputCue4029=[~UNUSED3~]\nOutputSigType4029=Digital\nOutputCue4030=[~UNUSED3~]\nOutputSigType4030=Digital\nOutputCue4031=[~UNUSED3~]\nOutputSigType4031=Digital\nOutputCue4032=[~UNUSED3~]\nOutputSigType4032=Digital\nOutputCue4033=[~UNUSED3~]\nOutputSigType4033=Digital\nOutputCue4034=[~UNUSED3~]\nOutputSigType4034=Digital\nOutputCue4035=[~UNUSED3~]\nOutputSigType4035=Digital\nOutputCue4036=[~UNUSED3~]\nOutputSigType4036=Digital\nOutputCue4037=[~UNUSED3~]\nOutputSigType4037=Digital\nOutputCue4038=[~UNUSED3~]\nOutputSigType4038=Digital\nOutputCue4039=[~UNUSED3~]\nOutputSigType4039=Digital\nOutputCue4040=[~UNUSED3~]\nOutputSigType4040=Digital\nOutputCue4041=[~UNUSED3~]\nOutputSigType4041=Digital\nOutputCue4042=[~UNUSED3~]\nOutputSigType4042=Digital\nOutputCue4043=[~UNUSED3~]\nOutputSigType4043=Digital\nOutputCue4044=[~UNUSED3~]\nOutputSigType4044=Digital\nOutputCue4045=[~UNUSED3~]\nOutputSigType4045=Digital\nOutputCue4046=[~UNUSED2~]\nOutputSigType4046=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=Set Item 11 Icon Analog\nInputList2SigType22=Analog\nInputList2Cue23=Set Item 12 Icon Analog\nInputList2SigType23=Analog\nInputList2Cue24=Set Item 13 Icon Analog\nInputList2SigType24=Analog\nInputList2Cue25=Set Item 14 Icon Analog\nInputList2SigType25=Analog\nInputList2Cue26=Set Item 15 Icon Analog\nInputList2SigType26=Analog\nInputList2Cue27=Set Item 16 Icon Analog\nInputList2SigType27=Analog\nInputList2Cue28=Set Item 17 Icon Analog\nInputList2SigType28=Analog\nInputList2Cue29=Set Item 18 Icon Analog\nInputList2SigType29=Analog\nInputList2Cue30=Set Item 19 Icon Analog\nInputList2SigType30=Analog\nInputList2Cue31=Set Item 20 Icon Analog\nInputList2SigType31=Analog\nInputList2Cue32=Set Item 21 Icon Analog\nInputList2SigType32=Analog\nInputList2Cue33=Set Item 22 Icon Analog\nInputList2SigType33=Analog\nInputList2Cue34=Set Item 23 Icon Analog\nInputList2SigType34=Analog\nInputList2Cue35=Set Item 24 Icon Analog\nInputList2SigType35=Analog\nInputList2Cue36=Set Item 25 Icon Analog\nInputList2SigType36=Analog\nInputList2Cue37=Set Item 26 Icon Analog\nInputList2SigType37=Analog\nInputList2Cue38=Set Item 27 Icon Analog\nInputList2SigType38=Analog\nInputList2Cue39=Set Item 28 Icon Analog\nInputList2SigType39=Analog\nInputList2Cue40=Set Item 29 Icon Analog\nInputList2SigType40=Analog\nInputList2Cue41=Set Item 30 Icon Analog\nInputList2SigType41=Analog\nInputList2Cue42=[~UNUSED2~]\nInputList2SigType42=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED3~]\nOutputList2SigType22=Analog\nOutputList2Cue23=[~UNUSED3~]\nOutputList2SigType23=Analog\nOutputList2Cue24=[~UNUSED3~]\nOutputList2SigType24=Analog\nOutputList2Cue25=[~UNUSED3~]\nOutputList2SigType25=Analog\nOutputList2Cue26=[~UNUSED3~]\nOutputList2SigType26=Analog\nOutputList2Cue27=[~UNUSED3~]\nOutputList2SigType27=Analog\nOutputList2Cue28=[~UNUSED3~]\nOutputList2SigType28=Analog\nOutputList2Cue29=[~UNUSED3~]\nOutputList2SigType29=Analog\nOutputList2Cue30=[~UNUSED3~]\nOutputList2SigType30=Analog\nOutputList2Cue31=[~UNUSED3~]\nOutputList2SigType31=Analog\nOutputList2Cue32=[~UNUSED3~]\nOutputList2SigType32=Analog\nOutputList2Cue33=[~UNUSED3~]\nOutputList2SigType33=Analog\nOutputList2Cue34=[~UNUSED3~]\nOutputList2SigType34=Analog\nOutputList2Cue35=[~UNUSED3~]\nOutputList2SigType35=Analog\nOutputList2Cue36=[~UNUSED3~]\nOutputList2SigType36=Analog\nOutputList2Cue37=[~UNUSED3~]\nOutputList2SigType37=Analog\nOutputList2Cue38=[~UNUSED3~]\nOutputList2SigType38=Analog\nOutputList2Cue39=[~UNUSED3~]\nOutputList2SigType39=Analog\nOutputList2Cue40=[~UNUSED3~]\nOutputList2SigType40=Analog\nOutputList2Cue41=[~UNUSED3~]\nOutputList2SigType41=Analog\nOutputList2Cue42=[~UNUSED2~]\nOutputList2SigType42=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=Set Item 11 Text\nInputList3SigType22=Serial\nInputList3Cue23=Set Item 12 Text\nInputList3SigType23=Serial\nInputList3Cue24=Set Item 13 Text\nInputList3SigType24=Serial\nInputList3Cue25=Set Item 14 Text\nInputList3SigType25=Serial\nInputList3Cue26=Set Item 15 Text\nInputList3SigType26=Serial\nInputList3Cue27=Set Item 16 Text\nInputList3SigType27=Serial\nInputList3Cue28=Set Item 17 Text\nInputList3SigType28=Serial\nInputList3Cue29=Set Item 18 Text\nInputList3SigType29=Serial\nInputList3Cue30=Set Item 19 Text\nInputList3SigType30=Serial\nInputList3Cue31=Set Item 20 Text\nInputList3SigType31=Serial\nInputList3Cue32=Set Item 21 Text\nInputList3SigType32=Serial\nInputList3Cue33=Set Item 22 Text\nInputList3SigType33=Serial\nInputList3Cue34=Set Item 23 Text\nInputList3SigType34=Serial\nInputList3Cue35=Set Item 24 Text\nInputList3SigType35=Serial\nInputList3Cue36=Set Item 25 Text\nInputList3SigType36=Serial\nInputList3Cue37=Set Item 26 Text\nInputList3SigType37=Serial\nInputList3Cue38=Set Item 27 Text\nInputList3SigType38=Serial\nInputList3Cue39=Set Item 28 Text\nInputList3SigType39=Serial\nInputList3Cue40=Set Item 29 Text\nInputList3SigType40=Serial\nInputList3Cue41=Set Item 30 Text\nInputList3SigType41=Serial\nInputList3Cue42=[~UNUSED2~]\nInputList3SigType42=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=Set Item 11 Icon Serial\nInputList3SigType2024=Serial\nInputList3Cue2025=Set Item 12 Icon Serial\nInputList3SigType2025=Serial\nInputList3Cue2026=Set Item 13 Icon Serial\nInputList3SigType2026=Serial\nInputList3Cue2027=Set Item 14 Icon Serial\nInputList3SigType2027=Serial\nInputList3Cue2028=Set Item 15 Icon Serial\nInputList3SigType2028=Serial\nInputList3Cue2029=Set Item 16 Icon Serial\nInputList3SigType2029=Serial\nInputList3Cue2030=Set Item 17 Icon Serial\nInputList3SigType2030=Serial\nInputList3Cue2031=Set Item 18 Icon Serial\nInputList3SigType2031=Serial\nInputList3Cue2032=Set Item 19 Icon Serial\nInputList3SigType2032=Serial\nInputList3Cue2033=Set Item 20 Icon Serial\nInputList3SigType2033=Serial\nInputList3Cue2034=Set Item 21 Icon Serial\nInputList3SigType2034=Serial\nInputList3Cue2035=Set Item 22 Icon Serial\nInputList3SigType2035=Serial\nInputList3Cue2036=Set Item 23 Icon Serial\nInputList3SigType2036=Serial\nInputList3Cue2037=Set Item 24 Icon Serial\nInputList3SigType2037=Serial\nInputList3Cue2038=Set Item 25 Icon Serial\nInputList3SigType2038=Serial\nInputList3Cue2039=Set Item 26 Icon Serial\nInputList3SigType2039=Serial\nInputList3Cue2040=Set Item 27 Icon Serial\nInputList3SigType2040=Serial\nInputList3Cue2041=Set Item 28 Icon Serial\nInputList3SigType2041=Serial\nInputList3Cue2042=Set Item 29 Icon Serial\nInputList3SigType2042=Serial\nInputList3Cue2043=Set Item 30 Icon Serial\nInputList3SigType2043=Serial\nInputList3Cue2044=[~UNUSED2~]\nInputList3SigType2044=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED3~]\nOutputList3SigType22=Serial\nOutputList3Cue23=[~UNUSED3~]\nOutputList3SigType23=Serial\nOutputList3Cue24=[~UNUSED3~]\nOutputList3SigType24=Serial\nOutputList3Cue25=[~UNUSED3~]\nOutputList3SigType25=Serial\nOutputList3Cue26=[~UNUSED3~]\nOutputList3SigType26=Serial\nOutputList3Cue27=[~UNUSED3~]\nOutputList3SigType27=Serial\nOutputList3Cue28=[~UNUSED3~]\nOutputList3SigType28=Serial\nOutputList3Cue29=[~UNUSED3~]\nOutputList3SigType29=Serial\nOutputList3Cue30=[~UNUSED3~]\nOutputList3SigType30=Serial\nOutputList3Cue31=[~UNUSED3~]\nOutputList3SigType31=Serial\nOutputList3Cue32=[~UNUSED3~]\nOutputList3SigType32=Serial\nOutputList3Cue33=[~UNUSED3~]\nOutputList3SigType33=Serial\nOutputList3Cue34=[~UNUSED3~]\nOutputList3SigType34=Serial\nOutputList3Cue35=[~UNUSED3~]\nOutputList3SigType35=Serial\nOutputList3Cue36=[~UNUSED3~]\nOutputList3SigType36=Serial\nOutputList3Cue37=[~UNUSED3~]\nOutputList3SigType37=Serial\nOutputList3Cue38=[~UNUSED3~]\nOutputList3SigType38=Serial\nOutputList3Cue39=[~UNUSED3~]\nOutputList3SigType39=Serial\nOutputList3Cue40=[~UNUSED3~]\nOutputList3SigType40=Serial\nOutputList3Cue41=[~UNUSED3~]\nOutputList3SigType41=Serial\nOutputList3Cue42=[~UNUSED2~]\nOutputList3SigType42=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED3~]\nOutputList3SigType2024=Serial\nOutputList3Cue2025=[~UNUSED3~]\nOutputList3SigType2025=Serial\nOutputList3Cue2026=[~UNUSED3~]\nOutputList3SigType2026=Serial\nOutputList3Cue2027=[~UNUSED3~]\nOutputList3SigType2027=Serial\nOutputList3Cue2028=[~UNUSED3~]\nOutputList3SigType2028=Serial\nOutputList3Cue2029=[~UNUSED3~]\nOutputList3SigType2029=Serial\nOutputList3Cue2030=[~UNUSED3~]\nOutputList3SigType2030=Serial\nOutputList3Cue2031=[~UNUSED3~]\nOutputList3SigType2031=Serial\nOutputList3Cue2032=[~UNUSED3~]\nOutputList3SigType2032=Serial\nOutputList3Cue2033=[~UNUSED3~]\nOutputList3SigType2033=Serial\nOutputList3Cue2034=[~UNUSED3~]\nOutputList3SigType2034=Serial\nOutputList3Cue2035=[~UNUSED3~]\nOutputList3SigType2035=Serial\nOutputList3Cue2036=[~UNUSED3~]\nOutputList3SigType2036=Serial\nOutputList3Cue2037=[~UNUSED3~]\nOutputList3SigType2037=Serial\nOutputList3Cue2038=[~UNUSED3~]\nOutputList3SigType2038=Serial\nOutputList3Cue2039=[~UNUSED3~]\nOutputList3SigType2039=Serial\nOutputList3Cue2040=[~UNUSED3~]\nOutputList3SigType2040=Serial\nOutputList3Cue2041=[~UNUSED3~]\nOutputList3SigType2041=Serial\nOutputList3Cue2042=[~UNUSED3~]\nOutputList3SigType2042=Serial\nOutputList3Cue2043=[~UNUSED3~]\nOutputList3SigType2043=Serial\nOutputList3Cue2044=[~UNUSED2~]\nOutputList3SigType2044=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=19\nCedH=19\nSmartObjId=1203d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=19\nTp=1\nHD=TRUE\nDV=1203d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=19\nName=PepperDash Essentials TSW-560_[D.VC] Recents_Dynamic Button List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n"
  },
  {
    "path": "src/PepperDash.Essentials/SGD/PepperDash Essentials TSW-760.sgd",
    "content": "[\nObjTp=FSgntr\nSgntr=SGD\nRelVrs=3\nVTProeVer=6.1.05\nSchema=1\nCRCGUID=6C002F1C-3D96-4EBF-A0D9-8A124BE8F48A\n]\n;================================================================================\n[\nObjTp=Hd\nProjectFile=PepperDash Essentials TSW-760.vtp\nVtpGuid=D8D5F125-CB35-42E9-8AE3-4142597FD2C5\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[C.AC] Keypad_AC DTMF Keypad_2.ced\nHint=AC DTMF Keypad_2 (Smart Object ID=1101)\nCode=1\nSGControlType=Simple Keypad\nSGControlName=AC DTMF Keypad_2\nGUID=F2A259FE-82AD-4085-B5A6-22D7B034458C\nSmplCName=PepperDash Essentials TSW-760_[C.AC] Keypad_AC DTMF Keypad_2.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=*\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=/#\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=1\nCedH=1\nSmartObjId=1101d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=1\nTp=1\nHD=TRUE\nDV=1101d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=1\nName=PepperDash Essentials TSW-760_[C.AC] Keypad_AC DTMF Keypad_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[D.VC] Directory_Dynamic Button List Vertical_2.ced\nHint=Dynamic Button List Vertical_2 (Smart Object ID=1202)\nCode=2\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical_2\nGUID=79060F3C-D3A4-4A8D-ACF0-CB2376A31702\nSmplCName=PepperDash Essentials TSW-760_[D.VC] Directory_Dynamic Button List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=Item 11 Selected\nInputSigType22=Digital\nInputCue23=Item 12 Selected\nInputSigType23=Digital\nInputCue24=Item 13 Selected\nInputSigType24=Digital\nInputCue25=Item 14 Selected\nInputSigType25=Digital\nInputCue26=Item 15 Selected\nInputSigType26=Digital\nInputCue27=Item 16 Selected\nInputSigType27=Digital\nInputCue28=Item 17 Selected\nInputSigType28=Digital\nInputCue29=Item 18 Selected\nInputSigType29=Digital\nInputCue30=Item 19 Selected\nInputSigType30=Digital\nInputCue31=Item 20 Selected\nInputSigType31=Digital\nInputCue32=Item 21 Selected\nInputSigType32=Digital\nInputCue33=Item 22 Selected\nInputSigType33=Digital\nInputCue34=Item 23 Selected\nInputSigType34=Digital\nInputCue35=Item 24 Selected\nInputSigType35=Digital\nInputCue36=Item 25 Selected\nInputSigType36=Digital\nInputCue37=Item 26 Selected\nInputSigType37=Digital\nInputCue38=Item 27 Selected\nInputSigType38=Digital\nInputCue39=Item 28 Selected\nInputSigType39=Digital\nInputCue40=Item 29 Selected\nInputSigType40=Digital\nInputCue41=Item 30 Selected\nInputSigType41=Digital\nInputCue42=Item 31 Selected\nInputSigType42=Digital\nInputCue43=Item 32 Selected\nInputSigType43=Digital\nInputCue44=Item 33 Selected\nInputSigType44=Digital\nInputCue45=Item 34 Selected\nInputSigType45=Digital\nInputCue46=Item 35 Selected\nInputSigType46=Digital\nInputCue47=Item 36 Selected\nInputSigType47=Digital\nInputCue48=Item 37 Selected\nInputSigType48=Digital\nInputCue49=Item 38 Selected\nInputSigType49=Digital\nInputCue50=Item 39 Selected\nInputSigType50=Digital\nInputCue51=Item 40 Selected\nInputSigType51=Digital\nInputCue52=Item 41 Selected\nInputSigType52=Digital\nInputCue53=Item 42 Selected\nInputSigType53=Digital\nInputCue54=Item 43 Selected\nInputSigType54=Digital\nInputCue55=Item 44 Selected\nInputSigType55=Digital\nInputCue56=Item 45 Selected\nInputSigType56=Digital\nInputCue57=Item 46 Selected\nInputSigType57=Digital\nInputCue58=Item 47 Selected\nInputSigType58=Digital\nInputCue59=Item 48 Selected\nInputSigType59=Digital\nInputCue60=Item 49 Selected\nInputSigType60=Digital\nInputCue61=Item 50 Selected\nInputSigType61=Digital\nInputCue62=Item 51 Selected\nInputSigType62=Digital\nInputCue63=Item 52 Selected\nInputSigType63=Digital\nInputCue64=Item 53 Selected\nInputSigType64=Digital\nInputCue65=Item 54 Selected\nInputSigType65=Digital\nInputCue66=Item 55 Selected\nInputSigType66=Digital\nInputCue67=Item 56 Selected\nInputSigType67=Digital\nInputCue68=Item 57 Selected\nInputSigType68=Digital\nInputCue69=Item 58 Selected\nInputSigType69=Digital\nInputCue70=Item 59 Selected\nInputSigType70=Digital\nInputCue71=Item 60 Selected\nInputSigType71=Digital\nInputCue72=Item 61 Selected\nInputSigType72=Digital\nInputCue73=Item 62 Selected\nInputSigType73=Digital\nInputCue74=Item 63 Selected\nInputSigType74=Digital\nInputCue75=Item 64 Selected\nInputSigType75=Digital\nInputCue76=Item 65 Selected\nInputSigType76=Digital\nInputCue77=Item 66 Selected\nInputSigType77=Digital\nInputCue78=Item 67 Selected\nInputSigType78=Digital\nInputCue79=Item 68 Selected\nInputSigType79=Digital\nInputCue80=Item 69 Selected\nInputSigType80=Digital\nInputCue81=Item 70 Selected\nInputSigType81=Digital\nInputCue82=Item 71 Selected\nInputSigType82=Digital\nInputCue83=Item 72 Selected\nInputSigType83=Digital\nInputCue84=Item 73 Selected\nInputSigType84=Digital\nInputCue85=Item 74 Selected\nInputSigType85=Digital\nInputCue86=Item 75 Selected\nInputSigType86=Digital\nInputCue87=Item 76 Selected\nInputSigType87=Digital\nInputCue88=Item 77 Selected\nInputSigType88=Digital\nInputCue89=Item 78 Selected\nInputSigType89=Digital\nInputCue90=Item 79 Selected\nInputSigType90=Digital\nInputCue91=Item 80 Selected\nInputSigType91=Digital\nInputCue92=Item 81 Selected\nInputSigType92=Digital\nInputCue93=Item 82 Selected\nInputSigType93=Digital\nInputCue94=Item 83 Selected\nInputSigType94=Digital\nInputCue95=Item 84 Selected\nInputSigType95=Digital\nInputCue96=Item 85 Selected\nInputSigType96=Digital\nInputCue97=Item 86 Selected\nInputSigType97=Digital\nInputCue98=Item 87 Selected\nInputSigType98=Digital\nInputCue99=Item 88 Selected\nInputSigType99=Digital\nInputCue100=Item 89 Selected\nInputSigType100=Digital\nInputCue101=Item 90 Selected\nInputSigType101=Digital\nInputCue102=Item 91 Selected\nInputSigType102=Digital\nInputCue103=Item 92 Selected\nInputSigType103=Digital\nInputCue104=Item 93 Selected\nInputSigType104=Digital\nInputCue105=Item 94 Selected\nInputSigType105=Digital\nInputCue106=Item 95 Selected\nInputSigType106=Digital\nInputCue107=Item 96 Selected\nInputSigType107=Digital\nInputCue108=Item 97 Selected\nInputSigType108=Digital\nInputCue109=Item 98 Selected\nInputSigType109=Digital\nInputCue110=Item 99 Selected\nInputSigType110=Digital\nInputCue111=Item 100 Selected\nInputSigType111=Digital\nInputCue112=[~UNUSED2~]\nInputSigType112=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=Item 11 Enabled\nInputSigType2024=Digital\nInputCue2025=Item 12 Enabled\nInputSigType2025=Digital\nInputCue2026=Item 13 Enabled\nInputSigType2026=Digital\nInputCue2027=Item 14 Enabled\nInputSigType2027=Digital\nInputCue2028=Item 15 Enabled\nInputSigType2028=Digital\nInputCue2029=Item 16 Enabled\nInputSigType2029=Digital\nInputCue2030=Item 17 Enabled\nInputSigType2030=Digital\nInputCue2031=Item 18 Enabled\nInputSigType2031=Digital\nInputCue2032=Item 19 Enabled\nInputSigType2032=Digital\nInputCue2033=Item 20 Enabled\nInputSigType2033=Digital\nInputCue2034=Item 21 Enabled\nInputSigType2034=Digital\nInputCue2035=Item 22 Enabled\nInputSigType2035=Digital\nInputCue2036=Item 23 Enabled\nInputSigType2036=Digital\nInputCue2037=Item 24 Enabled\nInputSigType2037=Digital\nInputCue2038=Item 25 Enabled\nInputSigType2038=Digital\nInputCue2039=Item 26 Enabled\nInputSigType2039=Digital\nInputCue2040=Item 27 Enabled\nInputSigType2040=Digital\nInputCue2041=Item 28 Enabled\nInputSigType2041=Digital\nInputCue2042=Item 29 Enabled\nInputSigType2042=Digital\nInputCue2043=Item 30 Enabled\nInputSigType2043=Digital\nInputCue2044=Item 31 Enabled\nInputSigType2044=Digital\nInputCue2045=Item 32 Enabled\nInputSigType2045=Digital\nInputCue2046=Item 33 Enabled\nInputSigType2046=Digital\nInputCue2047=Item 34 Enabled\nInputSigType2047=Digital\nInputCue2048=Item 35 Enabled\nInputSigType2048=Digital\nInputCue2049=Item 36 Enabled\nInputSigType2049=Digital\nInputCue2050=Item 37 Enabled\nInputSigType2050=Digital\nInputCue2051=Item 38 Enabled\nInputSigType2051=Digital\nInputCue2052=Item 39 Enabled\nInputSigType2052=Digital\nInputCue2053=Item 40 Enabled\nInputSigType2053=Digital\nInputCue2054=Item 41 Enabled\nInputSigType2054=Digital\nInputCue2055=Item 42 Enabled\nInputSigType2055=Digital\nInputCue2056=Item 43 Enabled\nInputSigType2056=Digital\nInputCue2057=Item 44 Enabled\nInputSigType2057=Digital\nInputCue2058=Item 45 Enabled\nInputSigType2058=Digital\nInputCue2059=Item 46 Enabled\nInputSigType2059=Digital\nInputCue2060=Item 47 Enabled\nInputSigType2060=Digital\nInputCue2061=Item 48 Enabled\nInputSigType2061=Digital\nInputCue2062=Item 49 Enabled\nInputSigType2062=Digital\nInputCue2063=Item 50 Enabled\nInputSigType2063=Digital\nInputCue2064=Item 51 Enabled\nInputSigType2064=Digital\nInputCue2065=Item 52 Enabled\nInputSigType2065=Digital\nInputCue2066=Item 53 Enabled\nInputSigType2066=Digital\nInputCue2067=Item 54 Enabled\nInputSigType2067=Digital\nInputCue2068=Item 55 Enabled\nInputSigType2068=Digital\nInputCue2069=Item 56 Enabled\nInputSigType2069=Digital\nInputCue2070=Item 57 Enabled\nInputSigType2070=Digital\nInputCue2071=Item 58 Enabled\nInputSigType2071=Digital\nInputCue2072=Item 59 Enabled\nInputSigType2072=Digital\nInputCue2073=Item 60 Enabled\nInputSigType2073=Digital\nInputCue2074=Item 61 Enabled\nInputSigType2074=Digital\nInputCue2075=Item 62 Enabled\nInputSigType2075=Digital\nInputCue2076=Item 63 Enabled\nInputSigType2076=Digital\nInputCue2077=Item 64 Enabled\nInputSigType2077=Digital\nInputCue2078=Item 65 Enabled\nInputSigType2078=Digital\nInputCue2079=Item 66 Enabled\nInputSigType2079=Digital\nInputCue2080=Item 67 Enabled\nInputSigType2080=Digital\nInputCue2081=Item 68 Enabled\nInputSigType2081=Digital\nInputCue2082=Item 69 Enabled\nInputSigType2082=Digital\nInputCue2083=Item 70 Enabled\nInputSigType2083=Digital\nInputCue2084=Item 71 Enabled\nInputSigType2084=Digital\nInputCue2085=Item 72 Enabled\nInputSigType2085=Digital\nInputCue2086=Item 73 Enabled\nInputSigType2086=Digital\nInputCue2087=Item 74 Enabled\nInputSigType2087=Digital\nInputCue2088=Item 75 Enabled\nInputSigType2088=Digital\nInputCue2089=Item 76 Enabled\nInputSigType2089=Digital\nInputCue2090=Item 77 Enabled\nInputSigType2090=Digital\nInputCue2091=Item 78 Enabled\nInputSigType2091=Digital\nInputCue2092=Item 79 Enabled\nInputSigType2092=Digital\nInputCue2093=Item 80 Enabled\nInputSigType2093=Digital\nInputCue2094=Item 81 Enabled\nInputSigType2094=Digital\nInputCue2095=Item 82 Enabled\nInputSigType2095=Digital\nInputCue2096=Item 83 Enabled\nInputSigType2096=Digital\nInputCue2097=Item 84 Enabled\nInputSigType2097=Digital\nInputCue2098=Item 85 Enabled\nInputSigType2098=Digital\nInputCue2099=Item 86 Enabled\nInputSigType2099=Digital\nInputCue2100=Item 87 Enabled\nInputSigType2100=Digital\nInputCue2101=Item 88 Enabled\nInputSigType2101=Digital\nInputCue2102=Item 89 Enabled\nInputSigType2102=Digital\nInputCue2103=Item 90 Enabled\nInputSigType2103=Digital\nInputCue2104=Item 91 Enabled\nInputSigType2104=Digital\nInputCue2105=Item 92 Enabled\nInputSigType2105=Digital\nInputCue2106=Item 93 Enabled\nInputSigType2106=Digital\nInputCue2107=Item 94 Enabled\nInputSigType2107=Digital\nInputCue2108=Item 95 Enabled\nInputSigType2108=Digital\nInputCue2109=Item 96 Enabled\nInputSigType2109=Digital\nInputCue2110=Item 97 Enabled\nInputSigType2110=Digital\nInputCue2111=Item 98 Enabled\nInputSigType2111=Digital\nInputCue2112=Item 99 Enabled\nInputSigType2112=Digital\nInputCue2113=Item 100 Enabled\nInputSigType2113=Digital\nInputCue2114=[~UNUSED2~]\nInputSigType2114=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=Item 11 Visible\nInputSigType4026=Digital\nInputCue4027=Item 12 Visible\nInputSigType4027=Digital\nInputCue4028=Item 13 Visible\nInputSigType4028=Digital\nInputCue4029=Item 14 Visible\nInputSigType4029=Digital\nInputCue4030=Item 15 Visible\nInputSigType4030=Digital\nInputCue4031=Item 16 Visible\nInputSigType4031=Digital\nInputCue4032=Item 17 Visible\nInputSigType4032=Digital\nInputCue4033=Item 18 Visible\nInputSigType4033=Digital\nInputCue4034=Item 19 Visible\nInputSigType4034=Digital\nInputCue4035=Item 20 Visible\nInputSigType4035=Digital\nInputCue4036=Item 21 Visible\nInputSigType4036=Digital\nInputCue4037=Item 22 Visible\nInputSigType4037=Digital\nInputCue4038=Item 23 Visible\nInputSigType4038=Digital\nInputCue4039=Item 24 Visible\nInputSigType4039=Digital\nInputCue4040=Item 25 Visible\nInputSigType4040=Digital\nInputCue4041=Item 26 Visible\nInputSigType4041=Digital\nInputCue4042=Item 27 Visible\nInputSigType4042=Digital\nInputCue4043=Item 28 Visible\nInputSigType4043=Digital\nInputCue4044=Item 29 Visible\nInputSigType4044=Digital\nInputCue4045=Item 30 Visible\nInputSigType4045=Digital\nInputCue4046=Item 31 Visible\nInputSigType4046=Digital\nInputCue4047=Item 32 Visible\nInputSigType4047=Digital\nInputCue4048=Item 33 Visible\nInputSigType4048=Digital\nInputCue4049=Item 34 Visible\nInputSigType4049=Digital\nInputCue4050=Item 35 Visible\nInputSigType4050=Digital\nInputCue4051=Item 36 Visible\nInputSigType4051=Digital\nInputCue4052=Item 37 Visible\nInputSigType4052=Digital\nInputCue4053=Item 38 Visible\nInputSigType4053=Digital\nInputCue4054=Item 39 Visible\nInputSigType4054=Digital\nInputCue4055=Item 40 Visible\nInputSigType4055=Digital\nInputCue4056=Item 41 Visible\nInputSigType4056=Digital\nInputCue4057=Item 42 Visible\nInputSigType4057=Digital\nInputCue4058=Item 43 Visible\nInputSigType4058=Digital\nInputCue4059=Item 44 Visible\nInputSigType4059=Digital\nInputCue4060=Item 45 Visible\nInputSigType4060=Digital\nInputCue4061=Item 46 Visible\nInputSigType4061=Digital\nInputCue4062=Item 47 Visible\nInputSigType4062=Digital\nInputCue4063=Item 48 Visible\nInputSigType4063=Digital\nInputCue4064=Item 49 Visible\nInputSigType4064=Digital\nInputCue4065=Item 50 Visible\nInputSigType4065=Digital\nInputCue4066=Item 51 Visible\nInputSigType4066=Digital\nInputCue4067=Item 52 Visible\nInputSigType4067=Digital\nInputCue4068=Item 53 Visible\nInputSigType4068=Digital\nInputCue4069=Item 54 Visible\nInputSigType4069=Digital\nInputCue4070=Item 55 Visible\nInputSigType4070=Digital\nInputCue4071=Item 56 Visible\nInputSigType4071=Digital\nInputCue4072=Item 57 Visible\nInputSigType4072=Digital\nInputCue4073=Item 58 Visible\nInputSigType4073=Digital\nInputCue4074=Item 59 Visible\nInputSigType4074=Digital\nInputCue4075=Item 60 Visible\nInputSigType4075=Digital\nInputCue4076=Item 61 Visible\nInputSigType4076=Digital\nInputCue4077=Item 62 Visible\nInputSigType4077=Digital\nInputCue4078=Item 63 Visible\nInputSigType4078=Digital\nInputCue4079=Item 64 Visible\nInputSigType4079=Digital\nInputCue4080=Item 65 Visible\nInputSigType4080=Digital\nInputCue4081=Item 66 Visible\nInputSigType4081=Digital\nInputCue4082=Item 67 Visible\nInputSigType4082=Digital\nInputCue4083=Item 68 Visible\nInputSigType4083=Digital\nInputCue4084=Item 69 Visible\nInputSigType4084=Digital\nInputCue4085=Item 70 Visible\nInputSigType4085=Digital\nInputCue4086=Item 71 Visible\nInputSigType4086=Digital\nInputCue4087=Item 72 Visible\nInputSigType4087=Digital\nInputCue4088=Item 73 Visible\nInputSigType4088=Digital\nInputCue4089=Item 74 Visible\nInputSigType4089=Digital\nInputCue4090=Item 75 Visible\nInputSigType4090=Digital\nInputCue4091=Item 76 Visible\nInputSigType4091=Digital\nInputCue4092=Item 77 Visible\nInputSigType4092=Digital\nInputCue4093=Item 78 Visible\nInputSigType4093=Digital\nInputCue4094=Item 79 Visible\nInputSigType4094=Digital\nInputCue4095=Item 80 Visible\nInputSigType4095=Digital\nInputCue4096=Item 81 Visible\nInputSigType4096=Digital\nInputCue4097=Item 82 Visible\nInputSigType4097=Digital\nInputCue4098=Item 83 Visible\nInputSigType4098=Digital\nInputCue4099=Item 84 Visible\nInputSigType4099=Digital\nInputCue4100=Item 85 Visible\nInputSigType4100=Digital\nInputCue4101=Item 86 Visible\nInputSigType4101=Digital\nInputCue4102=Item 87 Visible\nInputSigType4102=Digital\nInputCue4103=Item 88 Visible\nInputSigType4103=Digital\nInputCue4104=Item 89 Visible\nInputSigType4104=Digital\nInputCue4105=Item 90 Visible\nInputSigType4105=Digital\nInputCue4106=Item 91 Visible\nInputSigType4106=Digital\nInputCue4107=Item 92 Visible\nInputSigType4107=Digital\nInputCue4108=Item 93 Visible\nInputSigType4108=Digital\nInputCue4109=Item 94 Visible\nInputSigType4109=Digital\nInputCue4110=Item 95 Visible\nInputSigType4110=Digital\nInputCue4111=Item 96 Visible\nInputSigType4111=Digital\nInputCue4112=Item 97 Visible\nInputSigType4112=Digital\nInputCue4113=Item 98 Visible\nInputSigType4113=Digital\nInputCue4114=Item 99 Visible\nInputSigType4114=Digital\nInputCue4115=Item 100 Visible\nInputSigType4115=Digital\nInputCue4116=[~UNUSED2~]\nInputSigType4116=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=Item 11 Pressed\nOutputSigType22=Digital\nOutputCue23=Item 12 Pressed\nOutputSigType23=Digital\nOutputCue24=Item 13 Pressed\nOutputSigType24=Digital\nOutputCue25=Item 14 Pressed\nOutputSigType25=Digital\nOutputCue26=Item 15 Pressed\nOutputSigType26=Digital\nOutputCue27=Item 16 Pressed\nOutputSigType27=Digital\nOutputCue28=Item 17 Pressed\nOutputSigType28=Digital\nOutputCue29=Item 18 Pressed\nOutputSigType29=Digital\nOutputCue30=Item 19 Pressed\nOutputSigType30=Digital\nOutputCue31=Item 20 Pressed\nOutputSigType31=Digital\nOutputCue32=Item 21 Pressed\nOutputSigType32=Digital\nOutputCue33=Item 22 Pressed\nOutputSigType33=Digital\nOutputCue34=Item 23 Pressed\nOutputSigType34=Digital\nOutputCue35=Item 24 Pressed\nOutputSigType35=Digital\nOutputCue36=Item 25 Pressed\nOutputSigType36=Digital\nOutputCue37=Item 26 Pressed\nOutputSigType37=Digital\nOutputCue38=Item 27 Pressed\nOutputSigType38=Digital\nOutputCue39=Item 28 Pressed\nOutputSigType39=Digital\nOutputCue40=Item 29 Pressed\nOutputSigType40=Digital\nOutputCue41=Item 30 Pressed\nOutputSigType41=Digital\nOutputCue42=Item 31 Pressed\nOutputSigType42=Digital\nOutputCue43=Item 32 Pressed\nOutputSigType43=Digital\nOutputCue44=Item 33 Pressed\nOutputSigType44=Digital\nOutputCue45=Item 34 Pressed\nOutputSigType45=Digital\nOutputCue46=Item 35 Pressed\nOutputSigType46=Digital\nOutputCue47=Item 36 Pressed\nOutputSigType47=Digital\nOutputCue48=Item 37 Pressed\nOutputSigType48=Digital\nOutputCue49=Item 38 Pressed\nOutputSigType49=Digital\nOutputCue50=Item 39 Pressed\nOutputSigType50=Digital\nOutputCue51=Item 40 Pressed\nOutputSigType51=Digital\nOutputCue52=Item 41 Pressed\nOutputSigType52=Digital\nOutputCue53=Item 42 Pressed\nOutputSigType53=Digital\nOutputCue54=Item 43 Pressed\nOutputSigType54=Digital\nOutputCue55=Item 44 Pressed\nOutputSigType55=Digital\nOutputCue56=Item 45 Pressed\nOutputSigType56=Digital\nOutputCue57=Item 46 Pressed\nOutputSigType57=Digital\nOutputCue58=Item 47 Pressed\nOutputSigType58=Digital\nOutputCue59=Item 48 Pressed\nOutputSigType59=Digital\nOutputCue60=Item 49 Pressed\nOutputSigType60=Digital\nOutputCue61=Item 50 Pressed\nOutputSigType61=Digital\nOutputCue62=Item 51 Pressed\nOutputSigType62=Digital\nOutputCue63=Item 52 Pressed\nOutputSigType63=Digital\nOutputCue64=Item 53 Pressed\nOutputSigType64=Digital\nOutputCue65=Item 54 Pressed\nOutputSigType65=Digital\nOutputCue66=Item 55 Pressed\nOutputSigType66=Digital\nOutputCue67=Item 56 Pressed\nOutputSigType67=Digital\nOutputCue68=Item 57 Pressed\nOutputSigType68=Digital\nOutputCue69=Item 58 Pressed\nOutputSigType69=Digital\nOutputCue70=Item 59 Pressed\nOutputSigType70=Digital\nOutputCue71=Item 60 Pressed\nOutputSigType71=Digital\nOutputCue72=Item 61 Pressed\nOutputSigType72=Digital\nOutputCue73=Item 62 Pressed\nOutputSigType73=Digital\nOutputCue74=Item 63 Pressed\nOutputSigType74=Digital\nOutputCue75=Item 64 Pressed\nOutputSigType75=Digital\nOutputCue76=Item 65 Pressed\nOutputSigType76=Digital\nOutputCue77=Item 66 Pressed\nOutputSigType77=Digital\nOutputCue78=Item 67 Pressed\nOutputSigType78=Digital\nOutputCue79=Item 68 Pressed\nOutputSigType79=Digital\nOutputCue80=Item 69 Pressed\nOutputSigType80=Digital\nOutputCue81=Item 70 Pressed\nOutputSigType81=Digital\nOutputCue82=Item 71 Pressed\nOutputSigType82=Digital\nOutputCue83=Item 72 Pressed\nOutputSigType83=Digital\nOutputCue84=Item 73 Pressed\nOutputSigType84=Digital\nOutputCue85=Item 74 Pressed\nOutputSigType85=Digital\nOutputCue86=Item 75 Pressed\nOutputSigType86=Digital\nOutputCue87=Item 76 Pressed\nOutputSigType87=Digital\nOutputCue88=Item 77 Pressed\nOutputSigType88=Digital\nOutputCue89=Item 78 Pressed\nOutputSigType89=Digital\nOutputCue90=Item 79 Pressed\nOutputSigType90=Digital\nOutputCue91=Item 80 Pressed\nOutputSigType91=Digital\nOutputCue92=Item 81 Pressed\nOutputSigType92=Digital\nOutputCue93=Item 82 Pressed\nOutputSigType93=Digital\nOutputCue94=Item 83 Pressed\nOutputSigType94=Digital\nOutputCue95=Item 84 Pressed\nOutputSigType95=Digital\nOutputCue96=Item 85 Pressed\nOutputSigType96=Digital\nOutputCue97=Item 86 Pressed\nOutputSigType97=Digital\nOutputCue98=Item 87 Pressed\nOutputSigType98=Digital\nOutputCue99=Item 88 Pressed\nOutputSigType99=Digital\nOutputCue100=Item 89 Pressed\nOutputSigType100=Digital\nOutputCue101=Item 90 Pressed\nOutputSigType101=Digital\nOutputCue102=Item 91 Pressed\nOutputSigType102=Digital\nOutputCue103=Item 92 Pressed\nOutputSigType103=Digital\nOutputCue104=Item 93 Pressed\nOutputSigType104=Digital\nOutputCue105=Item 94 Pressed\nOutputSigType105=Digital\nOutputCue106=Item 95 Pressed\nOutputSigType106=Digital\nOutputCue107=Item 96 Pressed\nOutputSigType107=Digital\nOutputCue108=Item 97 Pressed\nOutputSigType108=Digital\nOutputCue109=Item 98 Pressed\nOutputSigType109=Digital\nOutputCue110=Item 99 Pressed\nOutputSigType110=Digital\nOutputCue111=Item 100 Pressed\nOutputSigType111=Digital\nOutputCue112=[~UNUSED2~]\nOutputSigType112=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED3~]\nOutputSigType2044=Digital\nOutputCue2045=[~UNUSED3~]\nOutputSigType2045=Digital\nOutputCue2046=[~UNUSED3~]\nOutputSigType2046=Digital\nOutputCue2047=[~UNUSED3~]\nOutputSigType2047=Digital\nOutputCue2048=[~UNUSED3~]\nOutputSigType2048=Digital\nOutputCue2049=[~UNUSED3~]\nOutputSigType2049=Digital\nOutputCue2050=[~UNUSED3~]\nOutputSigType2050=Digital\nOutputCue2051=[~UNUSED3~]\nOutputSigType2051=Digital\nOutputCue2052=[~UNUSED3~]\nOutputSigType2052=Digital\nOutputCue2053=[~UNUSED3~]\nOutputSigType2053=Digital\nOutputCue2054=[~UNUSED3~]\nOutputSigType2054=Digital\nOutputCue2055=[~UNUSED3~]\nOutputSigType2055=Digital\nOutputCue2056=[~UNUSED3~]\nOutputSigType2056=Digital\nOutputCue2057=[~UNUSED3~]\nOutputSigType2057=Digital\nOutputCue2058=[~UNUSED3~]\nOutputSigType2058=Digital\nOutputCue2059=[~UNUSED3~]\nOutputSigType2059=Digital\nOutputCue2060=[~UNUSED3~]\nOutputSigType2060=Digital\nOutputCue2061=[~UNUSED3~]\nOutputSigType2061=Digital\nOutputCue2062=[~UNUSED3~]\nOutputSigType2062=Digital\nOutputCue2063=[~UNUSED3~]\nOutputSigType2063=Digital\nOutputCue2064=[~UNUSED3~]\nOutputSigType2064=Digital\nOutputCue2065=[~UNUSED3~]\nOutputSigType2065=Digital\nOutputCue2066=[~UNUSED3~]\nOutputSigType2066=Digital\nOutputCue2067=[~UNUSED3~]\nOutputSigType2067=Digital\nOutputCue2068=[~UNUSED3~]\nOutputSigType2068=Digital\nOutputCue2069=[~UNUSED3~]\nOutputSigType2069=Digital\nOutputCue2070=[~UNUSED3~]\nOutputSigType2070=Digital\nOutputCue2071=[~UNUSED3~]\nOutputSigType2071=Digital\nOutputCue2072=[~UNUSED3~]\nOutputSigType2072=Digital\nOutputCue2073=[~UNUSED3~]\nOutputSigType2073=Digital\nOutputCue2074=[~UNUSED3~]\nOutputSigType2074=Digital\nOutputCue2075=[~UNUSED3~]\nOutputSigType2075=Digital\nOutputCue2076=[~UNUSED3~]\nOutputSigType2076=Digital\nOutputCue2077=[~UNUSED3~]\nOutputSigType2077=Digital\nOutputCue2078=[~UNUSED3~]\nOutputSigType2078=Digital\nOutputCue2079=[~UNUSED3~]\nOutputSigType2079=Digital\nOutputCue2080=[~UNUSED3~]\nOutputSigType2080=Digital\nOutputCue2081=[~UNUSED3~]\nOutputSigType2081=Digital\nOutputCue2082=[~UNUSED3~]\nOutputSigType2082=Digital\nOutputCue2083=[~UNUSED3~]\nOutputSigType2083=Digital\nOutputCue2084=[~UNUSED3~]\nOutputSigType2084=Digital\nOutputCue2085=[~UNUSED3~]\nOutputSigType2085=Digital\nOutputCue2086=[~UNUSED3~]\nOutputSigType2086=Digital\nOutputCue2087=[~UNUSED3~]\nOutputSigType2087=Digital\nOutputCue2088=[~UNUSED3~]\nOutputSigType2088=Digital\nOutputCue2089=[~UNUSED3~]\nOutputSigType2089=Digital\nOutputCue2090=[~UNUSED3~]\nOutputSigType2090=Digital\nOutputCue2091=[~UNUSED3~]\nOutputSigType2091=Digital\nOutputCue2092=[~UNUSED3~]\nOutputSigType2092=Digital\nOutputCue2093=[~UNUSED3~]\nOutputSigType2093=Digital\nOutputCue2094=[~UNUSED3~]\nOutputSigType2094=Digital\nOutputCue2095=[~UNUSED3~]\nOutputSigType2095=Digital\nOutputCue2096=[~UNUSED3~]\nOutputSigType2096=Digital\nOutputCue2097=[~UNUSED3~]\nOutputSigType2097=Digital\nOutputCue2098=[~UNUSED3~]\nOutputSigType2098=Digital\nOutputCue2099=[~UNUSED3~]\nOutputSigType2099=Digital\nOutputCue2100=[~UNUSED3~]\nOutputSigType2100=Digital\nOutputCue2101=[~UNUSED3~]\nOutputSigType2101=Digital\nOutputCue2102=[~UNUSED3~]\nOutputSigType2102=Digital\nOutputCue2103=[~UNUSED3~]\nOutputSigType2103=Digital\nOutputCue2104=[~UNUSED3~]\nOutputSigType2104=Digital\nOutputCue2105=[~UNUSED3~]\nOutputSigType2105=Digital\nOutputCue2106=[~UNUSED3~]\nOutputSigType2106=Digital\nOutputCue2107=[~UNUSED3~]\nOutputSigType2107=Digital\nOutputCue2108=[~UNUSED3~]\nOutputSigType2108=Digital\nOutputCue2109=[~UNUSED3~]\nOutputSigType2109=Digital\nOutputCue2110=[~UNUSED3~]\nOutputSigType2110=Digital\nOutputCue2111=[~UNUSED3~]\nOutputSigType2111=Digital\nOutputCue2112=[~UNUSED3~]\nOutputSigType2112=Digital\nOutputCue2113=[~UNUSED3~]\nOutputSigType2113=Digital\nOutputCue2114=[~UNUSED2~]\nOutputSigType2114=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED3~]\nOutputSigType4026=Digital\nOutputCue4027=[~UNUSED3~]\nOutputSigType4027=Digital\nOutputCue4028=[~UNUSED3~]\nOutputSigType4028=Digital\nOutputCue4029=[~UNUSED3~]\nOutputSigType4029=Digital\nOutputCue4030=[~UNUSED3~]\nOutputSigType4030=Digital\nOutputCue4031=[~UNUSED3~]\nOutputSigType4031=Digital\nOutputCue4032=[~UNUSED3~]\nOutputSigType4032=Digital\nOutputCue4033=[~UNUSED3~]\nOutputSigType4033=Digital\nOutputCue4034=[~UNUSED3~]\nOutputSigType4034=Digital\nOutputCue4035=[~UNUSED3~]\nOutputSigType4035=Digital\nOutputCue4036=[~UNUSED3~]\nOutputSigType4036=Digital\nOutputCue4037=[~UNUSED3~]\nOutputSigType4037=Digital\nOutputCue4038=[~UNUSED3~]\nOutputSigType4038=Digital\nOutputCue4039=[~UNUSED3~]\nOutputSigType4039=Digital\nOutputCue4040=[~UNUSED3~]\nOutputSigType4040=Digital\nOutputCue4041=[~UNUSED3~]\nOutputSigType4041=Digital\nOutputCue4042=[~UNUSED3~]\nOutputSigType4042=Digital\nOutputCue4043=[~UNUSED3~]\nOutputSigType4043=Digital\nOutputCue4044=[~UNUSED3~]\nOutputSigType4044=Digital\nOutputCue4045=[~UNUSED3~]\nOutputSigType4045=Digital\nOutputCue4046=[~UNUSED3~]\nOutputSigType4046=Digital\nOutputCue4047=[~UNUSED3~]\nOutputSigType4047=Digital\nOutputCue4048=[~UNUSED3~]\nOutputSigType4048=Digital\nOutputCue4049=[~UNUSED3~]\nOutputSigType4049=Digital\nOutputCue4050=[~UNUSED3~]\nOutputSigType4050=Digital\nOutputCue4051=[~UNUSED3~]\nOutputSigType4051=Digital\nOutputCue4052=[~UNUSED3~]\nOutputSigType4052=Digital\nOutputCue4053=[~UNUSED3~]\nOutputSigType4053=Digital\nOutputCue4054=[~UNUSED3~]\nOutputSigType4054=Digital\nOutputCue4055=[~UNUSED3~]\nOutputSigType4055=Digital\nOutputCue4056=[~UNUSED3~]\nOutputSigType4056=Digital\nOutputCue4057=[~UNUSED3~]\nOutputSigType4057=Digital\nOutputCue4058=[~UNUSED3~]\nOutputSigType4058=Digital\nOutputCue4059=[~UNUSED3~]\nOutputSigType4059=Digital\nOutputCue4060=[~UNUSED3~]\nOutputSigType4060=Digital\nOutputCue4061=[~UNUSED3~]\nOutputSigType4061=Digital\nOutputCue4062=[~UNUSED3~]\nOutputSigType4062=Digital\nOutputCue4063=[~UNUSED3~]\nOutputSigType4063=Digital\nOutputCue4064=[~UNUSED3~]\nOutputSigType4064=Digital\nOutputCue4065=[~UNUSED3~]\nOutputSigType4065=Digital\nOutputCue4066=[~UNUSED3~]\nOutputSigType4066=Digital\nOutputCue4067=[~UNUSED3~]\nOutputSigType4067=Digital\nOutputCue4068=[~UNUSED3~]\nOutputSigType4068=Digital\nOutputCue4069=[~UNUSED3~]\nOutputSigType4069=Digital\nOutputCue4070=[~UNUSED3~]\nOutputSigType4070=Digital\nOutputCue4071=[~UNUSED3~]\nOutputSigType4071=Digital\nOutputCue4072=[~UNUSED3~]\nOutputSigType4072=Digital\nOutputCue4073=[~UNUSED3~]\nOutputSigType4073=Digital\nOutputCue4074=[~UNUSED3~]\nOutputSigType4074=Digital\nOutputCue4075=[~UNUSED3~]\nOutputSigType4075=Digital\nOutputCue4076=[~UNUSED3~]\nOutputSigType4076=Digital\nOutputCue4077=[~UNUSED3~]\nOutputSigType4077=Digital\nOutputCue4078=[~UNUSED3~]\nOutputSigType4078=Digital\nOutputCue4079=[~UNUSED3~]\nOutputSigType4079=Digital\nOutputCue4080=[~UNUSED3~]\nOutputSigType4080=Digital\nOutputCue4081=[~UNUSED3~]\nOutputSigType4081=Digital\nOutputCue4082=[~UNUSED3~]\nOutputSigType4082=Digital\nOutputCue4083=[~UNUSED3~]\nOutputSigType4083=Digital\nOutputCue4084=[~UNUSED3~]\nOutputSigType4084=Digital\nOutputCue4085=[~UNUSED3~]\nOutputSigType4085=Digital\nOutputCue4086=[~UNUSED3~]\nOutputSigType4086=Digital\nOutputCue4087=[~UNUSED3~]\nOutputSigType4087=Digital\nOutputCue4088=[~UNUSED3~]\nOutputSigType4088=Digital\nOutputCue4089=[~UNUSED3~]\nOutputSigType4089=Digital\nOutputCue4090=[~UNUSED3~]\nOutputSigType4090=Digital\nOutputCue4091=[~UNUSED3~]\nOutputSigType4091=Digital\nOutputCue4092=[~UNUSED3~]\nOutputSigType4092=Digital\nOutputCue4093=[~UNUSED3~]\nOutputSigType4093=Digital\nOutputCue4094=[~UNUSED3~]\nOutputSigType4094=Digital\nOutputCue4095=[~UNUSED3~]\nOutputSigType4095=Digital\nOutputCue4096=[~UNUSED3~]\nOutputSigType4096=Digital\nOutputCue4097=[~UNUSED3~]\nOutputSigType4097=Digital\nOutputCue4098=[~UNUSED3~]\nOutputSigType4098=Digital\nOutputCue4099=[~UNUSED3~]\nOutputSigType4099=Digital\nOutputCue4100=[~UNUSED3~]\nOutputSigType4100=Digital\nOutputCue4101=[~UNUSED3~]\nOutputSigType4101=Digital\nOutputCue4102=[~UNUSED3~]\nOutputSigType4102=Digital\nOutputCue4103=[~UNUSED3~]\nOutputSigType4103=Digital\nOutputCue4104=[~UNUSED3~]\nOutputSigType4104=Digital\nOutputCue4105=[~UNUSED3~]\nOutputSigType4105=Digital\nOutputCue4106=[~UNUSED3~]\nOutputSigType4106=Digital\nOutputCue4107=[~UNUSED3~]\nOutputSigType4107=Digital\nOutputCue4108=[~UNUSED3~]\nOutputSigType4108=Digital\nOutputCue4109=[~UNUSED3~]\nOutputSigType4109=Digital\nOutputCue4110=[~UNUSED3~]\nOutputSigType4110=Digital\nOutputCue4111=[~UNUSED3~]\nOutputSigType4111=Digital\nOutputCue4112=[~UNUSED3~]\nOutputSigType4112=Digital\nOutputCue4113=[~UNUSED3~]\nOutputSigType4113=Digital\nOutputCue4114=[~UNUSED3~]\nOutputSigType4114=Digital\nOutputCue4115=[~UNUSED3~]\nOutputSigType4115=Digital\nOutputCue4116=[~UNUSED2~]\nOutputSigType4116=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=Set Item 11 Icon Analog\nInputList2SigType22=Analog\nInputList2Cue23=Set Item 12 Icon Analog\nInputList2SigType23=Analog\nInputList2Cue24=Set Item 13 Icon Analog\nInputList2SigType24=Analog\nInputList2Cue25=Set Item 14 Icon Analog\nInputList2SigType25=Analog\nInputList2Cue26=Set Item 15 Icon Analog\nInputList2SigType26=Analog\nInputList2Cue27=Set Item 16 Icon Analog\nInputList2SigType27=Analog\nInputList2Cue28=Set Item 17 Icon Analog\nInputList2SigType28=Analog\nInputList2Cue29=Set Item 18 Icon Analog\nInputList2SigType29=Analog\nInputList2Cue30=Set Item 19 Icon Analog\nInputList2SigType30=Analog\nInputList2Cue31=Set Item 20 Icon Analog\nInputList2SigType31=Analog\nInputList2Cue32=Set Item 21 Icon Analog\nInputList2SigType32=Analog\nInputList2Cue33=Set Item 22 Icon Analog\nInputList2SigType33=Analog\nInputList2Cue34=Set Item 23 Icon Analog\nInputList2SigType34=Analog\nInputList2Cue35=Set Item 24 Icon Analog\nInputList2SigType35=Analog\nInputList2Cue36=Set Item 25 Icon Analog\nInputList2SigType36=Analog\nInputList2Cue37=Set Item 26 Icon Analog\nInputList2SigType37=Analog\nInputList2Cue38=Set Item 27 Icon Analog\nInputList2SigType38=Analog\nInputList2Cue39=Set Item 28 Icon Analog\nInputList2SigType39=Analog\nInputList2Cue40=Set Item 29 Icon Analog\nInputList2SigType40=Analog\nInputList2Cue41=Set Item 30 Icon Analog\nInputList2SigType41=Analog\nInputList2Cue42=Set Item 31 Icon Analog\nInputList2SigType42=Analog\nInputList2Cue43=Set Item 32 Icon Analog\nInputList2SigType43=Analog\nInputList2Cue44=Set Item 33 Icon Analog\nInputList2SigType44=Analog\nInputList2Cue45=Set Item 34 Icon Analog\nInputList2SigType45=Analog\nInputList2Cue46=Set Item 35 Icon Analog\nInputList2SigType46=Analog\nInputList2Cue47=Set Item 36 Icon Analog\nInputList2SigType47=Analog\nInputList2Cue48=Set Item 37 Icon Analog\nInputList2SigType48=Analog\nInputList2Cue49=Set Item 38 Icon Analog\nInputList2SigType49=Analog\nInputList2Cue50=Set Item 39 Icon Analog\nInputList2SigType50=Analog\nInputList2Cue51=Set Item 40 Icon Analog\nInputList2SigType51=Analog\nInputList2Cue52=Set Item 41 Icon Analog\nInputList2SigType52=Analog\nInputList2Cue53=Set Item 42 Icon Analog\nInputList2SigType53=Analog\nInputList2Cue54=Set Item 43 Icon Analog\nInputList2SigType54=Analog\nInputList2Cue55=Set Item 44 Icon Analog\nInputList2SigType55=Analog\nInputList2Cue56=Set Item 45 Icon Analog\nInputList2SigType56=Analog\nInputList2Cue57=Set Item 46 Icon Analog\nInputList2SigType57=Analog\nInputList2Cue58=Set Item 47 Icon Analog\nInputList2SigType58=Analog\nInputList2Cue59=Set Item 48 Icon Analog\nInputList2SigType59=Analog\nInputList2Cue60=Set Item 49 Icon Analog\nInputList2SigType60=Analog\nInputList2Cue61=Set Item 50 Icon Analog\nInputList2SigType61=Analog\nInputList2Cue62=Set Item 51 Icon Analog\nInputList2SigType62=Analog\nInputList2Cue63=Set Item 52 Icon Analog\nInputList2SigType63=Analog\nInputList2Cue64=Set Item 53 Icon Analog\nInputList2SigType64=Analog\nInputList2Cue65=Set Item 54 Icon Analog\nInputList2SigType65=Analog\nInputList2Cue66=Set Item 55 Icon Analog\nInputList2SigType66=Analog\nInputList2Cue67=Set Item 56 Icon Analog\nInputList2SigType67=Analog\nInputList2Cue68=Set Item 57 Icon Analog\nInputList2SigType68=Analog\nInputList2Cue69=Set Item 58 Icon Analog\nInputList2SigType69=Analog\nInputList2Cue70=Set Item 59 Icon Analog\nInputList2SigType70=Analog\nInputList2Cue71=Set Item 60 Icon Analog\nInputList2SigType71=Analog\nInputList2Cue72=Set Item 61 Icon Analog\nInputList2SigType72=Analog\nInputList2Cue73=Set Item 62 Icon Analog\nInputList2SigType73=Analog\nInputList2Cue74=Set Item 63 Icon Analog\nInputList2SigType74=Analog\nInputList2Cue75=Set Item 64 Icon Analog\nInputList2SigType75=Analog\nInputList2Cue76=Set Item 65 Icon Analog\nInputList2SigType76=Analog\nInputList2Cue77=Set Item 66 Icon Analog\nInputList2SigType77=Analog\nInputList2Cue78=Set Item 67 Icon Analog\nInputList2SigType78=Analog\nInputList2Cue79=Set Item 68 Icon Analog\nInputList2SigType79=Analog\nInputList2Cue80=Set Item 69 Icon Analog\nInputList2SigType80=Analog\nInputList2Cue81=Set Item 70 Icon Analog\nInputList2SigType81=Analog\nInputList2Cue82=Set Item 71 Icon Analog\nInputList2SigType82=Analog\nInputList2Cue83=Set Item 72 Icon Analog\nInputList2SigType83=Analog\nInputList2Cue84=Set Item 73 Icon Analog\nInputList2SigType84=Analog\nInputList2Cue85=Set Item 74 Icon Analog\nInputList2SigType85=Analog\nInputList2Cue86=Set Item 75 Icon Analog\nInputList2SigType86=Analog\nInputList2Cue87=Set Item 76 Icon Analog\nInputList2SigType87=Analog\nInputList2Cue88=Set Item 77 Icon Analog\nInputList2SigType88=Analog\nInputList2Cue89=Set Item 78 Icon Analog\nInputList2SigType89=Analog\nInputList2Cue90=Set Item 79 Icon Analog\nInputList2SigType90=Analog\nInputList2Cue91=Set Item 80 Icon Analog\nInputList2SigType91=Analog\nInputList2Cue92=Set Item 81 Icon Analog\nInputList2SigType92=Analog\nInputList2Cue93=Set Item 82 Icon Analog\nInputList2SigType93=Analog\nInputList2Cue94=Set Item 83 Icon Analog\nInputList2SigType94=Analog\nInputList2Cue95=Set Item 84 Icon Analog\nInputList2SigType95=Analog\nInputList2Cue96=Set Item 85 Icon Analog\nInputList2SigType96=Analog\nInputList2Cue97=Set Item 86 Icon Analog\nInputList2SigType97=Analog\nInputList2Cue98=Set Item 87 Icon Analog\nInputList2SigType98=Analog\nInputList2Cue99=Set Item 88 Icon Analog\nInputList2SigType99=Analog\nInputList2Cue100=Set Item 89 Icon Analog\nInputList2SigType100=Analog\nInputList2Cue101=Set Item 90 Icon Analog\nInputList2SigType101=Analog\nInputList2Cue102=Set Item 91 Icon Analog\nInputList2SigType102=Analog\nInputList2Cue103=Set Item 92 Icon Analog\nInputList2SigType103=Analog\nInputList2Cue104=Set Item 93 Icon Analog\nInputList2SigType104=Analog\nInputList2Cue105=Set Item 94 Icon Analog\nInputList2SigType105=Analog\nInputList2Cue106=Set Item 95 Icon Analog\nInputList2SigType106=Analog\nInputList2Cue107=Set Item 96 Icon Analog\nInputList2SigType107=Analog\nInputList2Cue108=Set Item 97 Icon Analog\nInputList2SigType108=Analog\nInputList2Cue109=Set Item 98 Icon Analog\nInputList2SigType109=Analog\nInputList2Cue110=Set Item 99 Icon Analog\nInputList2SigType110=Analog\nInputList2Cue111=Set Item 100 Icon Analog\nInputList2SigType111=Analog\nInputList2Cue112=[~UNUSED2~]\nInputList2SigType112=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED3~]\nOutputList2SigType22=Analog\nOutputList2Cue23=[~UNUSED3~]\nOutputList2SigType23=Analog\nOutputList2Cue24=[~UNUSED3~]\nOutputList2SigType24=Analog\nOutputList2Cue25=[~UNUSED3~]\nOutputList2SigType25=Analog\nOutputList2Cue26=[~UNUSED3~]\nOutputList2SigType26=Analog\nOutputList2Cue27=[~UNUSED3~]\nOutputList2SigType27=Analog\nOutputList2Cue28=[~UNUSED3~]\nOutputList2SigType28=Analog\nOutputList2Cue29=[~UNUSED3~]\nOutputList2SigType29=Analog\nOutputList2Cue30=[~UNUSED3~]\nOutputList2SigType30=Analog\nOutputList2Cue31=[~UNUSED3~]\nOutputList2SigType31=Analog\nOutputList2Cue32=[~UNUSED3~]\nOutputList2SigType32=Analog\nOutputList2Cue33=[~UNUSED3~]\nOutputList2SigType33=Analog\nOutputList2Cue34=[~UNUSED3~]\nOutputList2SigType34=Analog\nOutputList2Cue35=[~UNUSED3~]\nOutputList2SigType35=Analog\nOutputList2Cue36=[~UNUSED3~]\nOutputList2SigType36=Analog\nOutputList2Cue37=[~UNUSED3~]\nOutputList2SigType37=Analog\nOutputList2Cue38=[~UNUSED3~]\nOutputList2SigType38=Analog\nOutputList2Cue39=[~UNUSED3~]\nOutputList2SigType39=Analog\nOutputList2Cue40=[~UNUSED3~]\nOutputList2SigType40=Analog\nOutputList2Cue41=[~UNUSED3~]\nOutputList2SigType41=Analog\nOutputList2Cue42=[~UNUSED3~]\nOutputList2SigType42=Analog\nOutputList2Cue43=[~UNUSED3~]\nOutputList2SigType43=Analog\nOutputList2Cue44=[~UNUSED3~]\nOutputList2SigType44=Analog\nOutputList2Cue45=[~UNUSED3~]\nOutputList2SigType45=Analog\nOutputList2Cue46=[~UNUSED3~]\nOutputList2SigType46=Analog\nOutputList2Cue47=[~UNUSED3~]\nOutputList2SigType47=Analog\nOutputList2Cue48=[~UNUSED3~]\nOutputList2SigType48=Analog\nOutputList2Cue49=[~UNUSED3~]\nOutputList2SigType49=Analog\nOutputList2Cue50=[~UNUSED3~]\nOutputList2SigType50=Analog\nOutputList2Cue51=[~UNUSED3~]\nOutputList2SigType51=Analog\nOutputList2Cue52=[~UNUSED3~]\nOutputList2SigType52=Analog\nOutputList2Cue53=[~UNUSED3~]\nOutputList2SigType53=Analog\nOutputList2Cue54=[~UNUSED3~]\nOutputList2SigType54=Analog\nOutputList2Cue55=[~UNUSED3~]\nOutputList2SigType55=Analog\nOutputList2Cue56=[~UNUSED3~]\nOutputList2SigType56=Analog\nOutputList2Cue57=[~UNUSED3~]\nOutputList2SigType57=Analog\nOutputList2Cue58=[~UNUSED3~]\nOutputList2SigType58=Analog\nOutputList2Cue59=[~UNUSED3~]\nOutputList2SigType59=Analog\nOutputList2Cue60=[~UNUSED3~]\nOutputList2SigType60=Analog\nOutputList2Cue61=[~UNUSED3~]\nOutputList2SigType61=Analog\nOutputList2Cue62=[~UNUSED3~]\nOutputList2SigType62=Analog\nOutputList2Cue63=[~UNUSED3~]\nOutputList2SigType63=Analog\nOutputList2Cue64=[~UNUSED3~]\nOutputList2SigType64=Analog\nOutputList2Cue65=[~UNUSED3~]\nOutputList2SigType65=Analog\nOutputList2Cue66=[~UNUSED3~]\nOutputList2SigType66=Analog\nOutputList2Cue67=[~UNUSED3~]\nOutputList2SigType67=Analog\nOutputList2Cue68=[~UNUSED3~]\nOutputList2SigType68=Analog\nOutputList2Cue69=[~UNUSED3~]\nOutputList2SigType69=Analog\nOutputList2Cue70=[~UNUSED3~]\nOutputList2SigType70=Analog\nOutputList2Cue71=[~UNUSED3~]\nOutputList2SigType71=Analog\nOutputList2Cue72=[~UNUSED3~]\nOutputList2SigType72=Analog\nOutputList2Cue73=[~UNUSED3~]\nOutputList2SigType73=Analog\nOutputList2Cue74=[~UNUSED3~]\nOutputList2SigType74=Analog\nOutputList2Cue75=[~UNUSED3~]\nOutputList2SigType75=Analog\nOutputList2Cue76=[~UNUSED3~]\nOutputList2SigType76=Analog\nOutputList2Cue77=[~UNUSED3~]\nOutputList2SigType77=Analog\nOutputList2Cue78=[~UNUSED3~]\nOutputList2SigType78=Analog\nOutputList2Cue79=[~UNUSED3~]\nOutputList2SigType79=Analog\nOutputList2Cue80=[~UNUSED3~]\nOutputList2SigType80=Analog\nOutputList2Cue81=[~UNUSED3~]\nOutputList2SigType81=Analog\nOutputList2Cue82=[~UNUSED3~]\nOutputList2SigType82=Analog\nOutputList2Cue83=[~UNUSED3~]\nOutputList2SigType83=Analog\nOutputList2Cue84=[~UNUSED3~]\nOutputList2SigType84=Analog\nOutputList2Cue85=[~UNUSED3~]\nOutputList2SigType85=Analog\nOutputList2Cue86=[~UNUSED3~]\nOutputList2SigType86=Analog\nOutputList2Cue87=[~UNUSED3~]\nOutputList2SigType87=Analog\nOutputList2Cue88=[~UNUSED3~]\nOutputList2SigType88=Analog\nOutputList2Cue89=[~UNUSED3~]\nOutputList2SigType89=Analog\nOutputList2Cue90=[~UNUSED3~]\nOutputList2SigType90=Analog\nOutputList2Cue91=[~UNUSED3~]\nOutputList2SigType91=Analog\nOutputList2Cue92=[~UNUSED3~]\nOutputList2SigType92=Analog\nOutputList2Cue93=[~UNUSED3~]\nOutputList2SigType93=Analog\nOutputList2Cue94=[~UNUSED3~]\nOutputList2SigType94=Analog\nOutputList2Cue95=[~UNUSED3~]\nOutputList2SigType95=Analog\nOutputList2Cue96=[~UNUSED3~]\nOutputList2SigType96=Analog\nOutputList2Cue97=[~UNUSED3~]\nOutputList2SigType97=Analog\nOutputList2Cue98=[~UNUSED3~]\nOutputList2SigType98=Analog\nOutputList2Cue99=[~UNUSED3~]\nOutputList2SigType99=Analog\nOutputList2Cue100=[~UNUSED3~]\nOutputList2SigType100=Analog\nOutputList2Cue101=[~UNUSED3~]\nOutputList2SigType101=Analog\nOutputList2Cue102=[~UNUSED3~]\nOutputList2SigType102=Analog\nOutputList2Cue103=[~UNUSED3~]\nOutputList2SigType103=Analog\nOutputList2Cue104=[~UNUSED3~]\nOutputList2SigType104=Analog\nOutputList2Cue105=[~UNUSED3~]\nOutputList2SigType105=Analog\nOutputList2Cue106=[~UNUSED3~]\nOutputList2SigType106=Analog\nOutputList2Cue107=[~UNUSED3~]\nOutputList2SigType107=Analog\nOutputList2Cue108=[~UNUSED3~]\nOutputList2SigType108=Analog\nOutputList2Cue109=[~UNUSED3~]\nOutputList2SigType109=Analog\nOutputList2Cue110=[~UNUSED3~]\nOutputList2SigType110=Analog\nOutputList2Cue111=[~UNUSED3~]\nOutputList2SigType111=Analog\nOutputList2Cue112=[~UNUSED2~]\nOutputList2SigType112=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=Set Item 11 Text\nInputList3SigType22=Serial\nInputList3Cue23=Set Item 12 Text\nInputList3SigType23=Serial\nInputList3Cue24=Set Item 13 Text\nInputList3SigType24=Serial\nInputList3Cue25=Set Item 14 Text\nInputList3SigType25=Serial\nInputList3Cue26=Set Item 15 Text\nInputList3SigType26=Serial\nInputList3Cue27=Set Item 16 Text\nInputList3SigType27=Serial\nInputList3Cue28=Set Item 17 Text\nInputList3SigType28=Serial\nInputList3Cue29=Set Item 18 Text\nInputList3SigType29=Serial\nInputList3Cue30=Set Item 19 Text\nInputList3SigType30=Serial\nInputList3Cue31=Set Item 20 Text\nInputList3SigType31=Serial\nInputList3Cue32=Set Item 21 Text\nInputList3SigType32=Serial\nInputList3Cue33=Set Item 22 Text\nInputList3SigType33=Serial\nInputList3Cue34=Set Item 23 Text\nInputList3SigType34=Serial\nInputList3Cue35=Set Item 24 Text\nInputList3SigType35=Serial\nInputList3Cue36=Set Item 25 Text\nInputList3SigType36=Serial\nInputList3Cue37=Set Item 26 Text\nInputList3SigType37=Serial\nInputList3Cue38=Set Item 27 Text\nInputList3SigType38=Serial\nInputList3Cue39=Set Item 28 Text\nInputList3SigType39=Serial\nInputList3Cue40=Set Item 29 Text\nInputList3SigType40=Serial\nInputList3Cue41=Set Item 30 Text\nInputList3SigType41=Serial\nInputList3Cue42=Set Item 31 Text\nInputList3SigType42=Serial\nInputList3Cue43=Set Item 32 Text\nInputList3SigType43=Serial\nInputList3Cue44=Set Item 33 Text\nInputList3SigType44=Serial\nInputList3Cue45=Set Item 34 Text\nInputList3SigType45=Serial\nInputList3Cue46=Set Item 35 Text\nInputList3SigType46=Serial\nInputList3Cue47=Set Item 36 Text\nInputList3SigType47=Serial\nInputList3Cue48=Set Item 37 Text\nInputList3SigType48=Serial\nInputList3Cue49=Set Item 38 Text\nInputList3SigType49=Serial\nInputList3Cue50=Set Item 39 Text\nInputList3SigType50=Serial\nInputList3Cue51=Set Item 40 Text\nInputList3SigType51=Serial\nInputList3Cue52=Set Item 41 Text\nInputList3SigType52=Serial\nInputList3Cue53=Set Item 42 Text\nInputList3SigType53=Serial\nInputList3Cue54=Set Item 43 Text\nInputList3SigType54=Serial\nInputList3Cue55=Set Item 44 Text\nInputList3SigType55=Serial\nInputList3Cue56=Set Item 45 Text\nInputList3SigType56=Serial\nInputList3Cue57=Set Item 46 Text\nInputList3SigType57=Serial\nInputList3Cue58=Set Item 47 Text\nInputList3SigType58=Serial\nInputList3Cue59=Set Item 48 Text\nInputList3SigType59=Serial\nInputList3Cue60=Set Item 49 Text\nInputList3SigType60=Serial\nInputList3Cue61=Set Item 50 Text\nInputList3SigType61=Serial\nInputList3Cue62=Set Item 51 Text\nInputList3SigType62=Serial\nInputList3Cue63=Set Item 52 Text\nInputList3SigType63=Serial\nInputList3Cue64=Set Item 53 Text\nInputList3SigType64=Serial\nInputList3Cue65=Set Item 54 Text\nInputList3SigType65=Serial\nInputList3Cue66=Set Item 55 Text\nInputList3SigType66=Serial\nInputList3Cue67=Set Item 56 Text\nInputList3SigType67=Serial\nInputList3Cue68=Set Item 57 Text\nInputList3SigType68=Serial\nInputList3Cue69=Set Item 58 Text\nInputList3SigType69=Serial\nInputList3Cue70=Set Item 59 Text\nInputList3SigType70=Serial\nInputList3Cue71=Set Item 60 Text\nInputList3SigType71=Serial\nInputList3Cue72=Set Item 61 Text\nInputList3SigType72=Serial\nInputList3Cue73=Set Item 62 Text\nInputList3SigType73=Serial\nInputList3Cue74=Set Item 63 Text\nInputList3SigType74=Serial\nInputList3Cue75=Set Item 64 Text\nInputList3SigType75=Serial\nInputList3Cue76=Set Item 65 Text\nInputList3SigType76=Serial\nInputList3Cue77=Set Item 66 Text\nInputList3SigType77=Serial\nInputList3Cue78=Set Item 67 Text\nInputList3SigType78=Serial\nInputList3Cue79=Set Item 68 Text\nInputList3SigType79=Serial\nInputList3Cue80=Set Item 69 Text\nInputList3SigType80=Serial\nInputList3Cue81=Set Item 70 Text\nInputList3SigType81=Serial\nInputList3Cue82=Set Item 71 Text\nInputList3SigType82=Serial\nInputList3Cue83=Set Item 72 Text\nInputList3SigType83=Serial\nInputList3Cue84=Set Item 73 Text\nInputList3SigType84=Serial\nInputList3Cue85=Set Item 74 Text\nInputList3SigType85=Serial\nInputList3Cue86=Set Item 75 Text\nInputList3SigType86=Serial\nInputList3Cue87=Set Item 76 Text\nInputList3SigType87=Serial\nInputList3Cue88=Set Item 77 Text\nInputList3SigType88=Serial\nInputList3Cue89=Set Item 78 Text\nInputList3SigType89=Serial\nInputList3Cue90=Set Item 79 Text\nInputList3SigType90=Serial\nInputList3Cue91=Set Item 80 Text\nInputList3SigType91=Serial\nInputList3Cue92=Set Item 81 Text\nInputList3SigType92=Serial\nInputList3Cue93=Set Item 82 Text\nInputList3SigType93=Serial\nInputList3Cue94=Set Item 83 Text\nInputList3SigType94=Serial\nInputList3Cue95=Set Item 84 Text\nInputList3SigType95=Serial\nInputList3Cue96=Set Item 85 Text\nInputList3SigType96=Serial\nInputList3Cue97=Set Item 86 Text\nInputList3SigType97=Serial\nInputList3Cue98=Set Item 87 Text\nInputList3SigType98=Serial\nInputList3Cue99=Set Item 88 Text\nInputList3SigType99=Serial\nInputList3Cue100=Set Item 89 Text\nInputList3SigType100=Serial\nInputList3Cue101=Set Item 90 Text\nInputList3SigType101=Serial\nInputList3Cue102=Set Item 91 Text\nInputList3SigType102=Serial\nInputList3Cue103=Set Item 92 Text\nInputList3SigType103=Serial\nInputList3Cue104=Set Item 93 Text\nInputList3SigType104=Serial\nInputList3Cue105=Set Item 94 Text\nInputList3SigType105=Serial\nInputList3Cue106=Set Item 95 Text\nInputList3SigType106=Serial\nInputList3Cue107=Set Item 96 Text\nInputList3SigType107=Serial\nInputList3Cue108=Set Item 97 Text\nInputList3SigType108=Serial\nInputList3Cue109=Set Item 98 Text\nInputList3SigType109=Serial\nInputList3Cue110=Set Item 99 Text\nInputList3SigType110=Serial\nInputList3Cue111=Set Item 100 Text\nInputList3SigType111=Serial\nInputList3Cue112=[~UNUSED2~]\nInputList3SigType112=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=Set Item 11 Icon Serial\nInputList3SigType2024=Serial\nInputList3Cue2025=Set Item 12 Icon Serial\nInputList3SigType2025=Serial\nInputList3Cue2026=Set Item 13 Icon Serial\nInputList3SigType2026=Serial\nInputList3Cue2027=Set Item 14 Icon Serial\nInputList3SigType2027=Serial\nInputList3Cue2028=Set Item 15 Icon Serial\nInputList3SigType2028=Serial\nInputList3Cue2029=Set Item 16 Icon Serial\nInputList3SigType2029=Serial\nInputList3Cue2030=Set Item 17 Icon Serial\nInputList3SigType2030=Serial\nInputList3Cue2031=Set Item 18 Icon Serial\nInputList3SigType2031=Serial\nInputList3Cue2032=Set Item 19 Icon Serial\nInputList3SigType2032=Serial\nInputList3Cue2033=Set Item 20 Icon Serial\nInputList3SigType2033=Serial\nInputList3Cue2034=Set Item 21 Icon Serial\nInputList3SigType2034=Serial\nInputList3Cue2035=Set Item 22 Icon Serial\nInputList3SigType2035=Serial\nInputList3Cue2036=Set Item 23 Icon Serial\nInputList3SigType2036=Serial\nInputList3Cue2037=Set Item 24 Icon Serial\nInputList3SigType2037=Serial\nInputList3Cue2038=Set Item 25 Icon Serial\nInputList3SigType2038=Serial\nInputList3Cue2039=Set Item 26 Icon Serial\nInputList3SigType2039=Serial\nInputList3Cue2040=Set Item 27 Icon Serial\nInputList3SigType2040=Serial\nInputList3Cue2041=Set Item 28 Icon Serial\nInputList3SigType2041=Serial\nInputList3Cue2042=Set Item 29 Icon Serial\nInputList3SigType2042=Serial\nInputList3Cue2043=Set Item 30 Icon Serial\nInputList3SigType2043=Serial\nInputList3Cue2044=Set Item 31 Icon Serial\nInputList3SigType2044=Serial\nInputList3Cue2045=Set Item 32 Icon Serial\nInputList3SigType2045=Serial\nInputList3Cue2046=Set Item 33 Icon Serial\nInputList3SigType2046=Serial\nInputList3Cue2047=Set Item 34 Icon Serial\nInputList3SigType2047=Serial\nInputList3Cue2048=Set Item 35 Icon Serial\nInputList3SigType2048=Serial\nInputList3Cue2049=Set Item 36 Icon Serial\nInputList3SigType2049=Serial\nInputList3Cue2050=Set Item 37 Icon Serial\nInputList3SigType2050=Serial\nInputList3Cue2051=Set Item 38 Icon Serial\nInputList3SigType2051=Serial\nInputList3Cue2052=Set Item 39 Icon Serial\nInputList3SigType2052=Serial\nInputList3Cue2053=Set Item 40 Icon Serial\nInputList3SigType2053=Serial\nInputList3Cue2054=Set Item 41 Icon Serial\nInputList3SigType2054=Serial\nInputList3Cue2055=Set Item 42 Icon Serial\nInputList3SigType2055=Serial\nInputList3Cue2056=Set Item 43 Icon Serial\nInputList3SigType2056=Serial\nInputList3Cue2057=Set Item 44 Icon Serial\nInputList3SigType2057=Serial\nInputList3Cue2058=Set Item 45 Icon Serial\nInputList3SigType2058=Serial\nInputList3Cue2059=Set Item 46 Icon Serial\nInputList3SigType2059=Serial\nInputList3Cue2060=Set Item 47 Icon Serial\nInputList3SigType2060=Serial\nInputList3Cue2061=Set Item 48 Icon Serial\nInputList3SigType2061=Serial\nInputList3Cue2062=Set Item 49 Icon Serial\nInputList3SigType2062=Serial\nInputList3Cue2063=Set Item 50 Icon Serial\nInputList3SigType2063=Serial\nInputList3Cue2064=Set Item 51 Icon Serial\nInputList3SigType2064=Serial\nInputList3Cue2065=Set Item 52 Icon Serial\nInputList3SigType2065=Serial\nInputList3Cue2066=Set Item 53 Icon Serial\nInputList3SigType2066=Serial\nInputList3Cue2067=Set Item 54 Icon Serial\nInputList3SigType2067=Serial\nInputList3Cue2068=Set Item 55 Icon Serial\nInputList3SigType2068=Serial\nInputList3Cue2069=Set Item 56 Icon Serial\nInputList3SigType2069=Serial\nInputList3Cue2070=Set Item 57 Icon Serial\nInputList3SigType2070=Serial\nInputList3Cue2071=Set Item 58 Icon Serial\nInputList3SigType2071=Serial\nInputList3Cue2072=Set Item 59 Icon Serial\nInputList3SigType2072=Serial\nInputList3Cue2073=Set Item 60 Icon Serial\nInputList3SigType2073=Serial\nInputList3Cue2074=Set Item 61 Icon Serial\nInputList3SigType2074=Serial\nInputList3Cue2075=Set Item 62 Icon Serial\nInputList3SigType2075=Serial\nInputList3Cue2076=Set Item 63 Icon Serial\nInputList3SigType2076=Serial\nInputList3Cue2077=Set Item 64 Icon Serial\nInputList3SigType2077=Serial\nInputList3Cue2078=Set Item 65 Icon Serial\nInputList3SigType2078=Serial\nInputList3Cue2079=Set Item 66 Icon Serial\nInputList3SigType2079=Serial\nInputList3Cue2080=Set Item 67 Icon Serial\nInputList3SigType2080=Serial\nInputList3Cue2081=Set Item 68 Icon Serial\nInputList3SigType2081=Serial\nInputList3Cue2082=Set Item 69 Icon Serial\nInputList3SigType2082=Serial\nInputList3Cue2083=Set Item 70 Icon Serial\nInputList3SigType2083=Serial\nInputList3Cue2084=Set Item 71 Icon Serial\nInputList3SigType2084=Serial\nInputList3Cue2085=Set Item 72 Icon Serial\nInputList3SigType2085=Serial\nInputList3Cue2086=Set Item 73 Icon Serial\nInputList3SigType2086=Serial\nInputList3Cue2087=Set Item 74 Icon Serial\nInputList3SigType2087=Serial\nInputList3Cue2088=Set Item 75 Icon Serial\nInputList3SigType2088=Serial\nInputList3Cue2089=Set Item 76 Icon Serial\nInputList3SigType2089=Serial\nInputList3Cue2090=Set Item 77 Icon Serial\nInputList3SigType2090=Serial\nInputList3Cue2091=Set Item 78 Icon Serial\nInputList3SigType2091=Serial\nInputList3Cue2092=Set Item 79 Icon Serial\nInputList3SigType2092=Serial\nInputList3Cue2093=Set Item 80 Icon Serial\nInputList3SigType2093=Serial\nInputList3Cue2094=Set Item 81 Icon Serial\nInputList3SigType2094=Serial\nInputList3Cue2095=Set Item 82 Icon Serial\nInputList3SigType2095=Serial\nInputList3Cue2096=Set Item 83 Icon Serial\nInputList3SigType2096=Serial\nInputList3Cue2097=Set Item 84 Icon Serial\nInputList3SigType2097=Serial\nInputList3Cue2098=Set Item 85 Icon Serial\nInputList3SigType2098=Serial\nInputList3Cue2099=Set Item 86 Icon Serial\nInputList3SigType2099=Serial\nInputList3Cue2100=Set Item 87 Icon Serial\nInputList3SigType2100=Serial\nInputList3Cue2101=Set Item 88 Icon Serial\nInputList3SigType2101=Serial\nInputList3Cue2102=Set Item 89 Icon Serial\nInputList3SigType2102=Serial\nInputList3Cue2103=Set Item 90 Icon Serial\nInputList3SigType2103=Serial\nInputList3Cue2104=Set Item 91 Icon Serial\nInputList3SigType2104=Serial\nInputList3Cue2105=Set Item 92 Icon Serial\nInputList3SigType2105=Serial\nInputList3Cue2106=Set Item 93 Icon Serial\nInputList3SigType2106=Serial\nInputList3Cue2107=Set Item 94 Icon Serial\nInputList3SigType2107=Serial\nInputList3Cue2108=Set Item 95 Icon Serial\nInputList3SigType2108=Serial\nInputList3Cue2109=Set Item 96 Icon Serial\nInputList3SigType2109=Serial\nInputList3Cue2110=Set Item 97 Icon Serial\nInputList3SigType2110=Serial\nInputList3Cue2111=Set Item 98 Icon Serial\nInputList3SigType2111=Serial\nInputList3Cue2112=Set Item 99 Icon Serial\nInputList3SigType2112=Serial\nInputList3Cue2113=Set Item 100 Icon Serial\nInputList3SigType2113=Serial\nInputList3Cue2114=[~UNUSED2~]\nInputList3SigType2114=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED3~]\nOutputList3SigType22=Serial\nOutputList3Cue23=[~UNUSED3~]\nOutputList3SigType23=Serial\nOutputList3Cue24=[~UNUSED3~]\nOutputList3SigType24=Serial\nOutputList3Cue25=[~UNUSED3~]\nOutputList3SigType25=Serial\nOutputList3Cue26=[~UNUSED3~]\nOutputList3SigType26=Serial\nOutputList3Cue27=[~UNUSED3~]\nOutputList3SigType27=Serial\nOutputList3Cue28=[~UNUSED3~]\nOutputList3SigType28=Serial\nOutputList3Cue29=[~UNUSED3~]\nOutputList3SigType29=Serial\nOutputList3Cue30=[~UNUSED3~]\nOutputList3SigType30=Serial\nOutputList3Cue31=[~UNUSED3~]\nOutputList3SigType31=Serial\nOutputList3Cue32=[~UNUSED3~]\nOutputList3SigType32=Serial\nOutputList3Cue33=[~UNUSED3~]\nOutputList3SigType33=Serial\nOutputList3Cue34=[~UNUSED3~]\nOutputList3SigType34=Serial\nOutputList3Cue35=[~UNUSED3~]\nOutputList3SigType35=Serial\nOutputList3Cue36=[~UNUSED3~]\nOutputList3SigType36=Serial\nOutputList3Cue37=[~UNUSED3~]\nOutputList3SigType37=Serial\nOutputList3Cue38=[~UNUSED3~]\nOutputList3SigType38=Serial\nOutputList3Cue39=[~UNUSED3~]\nOutputList3SigType39=Serial\nOutputList3Cue40=[~UNUSED3~]\nOutputList3SigType40=Serial\nOutputList3Cue41=[~UNUSED3~]\nOutputList3SigType41=Serial\nOutputList3Cue42=[~UNUSED3~]\nOutputList3SigType42=Serial\nOutputList3Cue43=[~UNUSED3~]\nOutputList3SigType43=Serial\nOutputList3Cue44=[~UNUSED3~]\nOutputList3SigType44=Serial\nOutputList3Cue45=[~UNUSED3~]\nOutputList3SigType45=Serial\nOutputList3Cue46=[~UNUSED3~]\nOutputList3SigType46=Serial\nOutputList3Cue47=[~UNUSED3~]\nOutputList3SigType47=Serial\nOutputList3Cue48=[~UNUSED3~]\nOutputList3SigType48=Serial\nOutputList3Cue49=[~UNUSED3~]\nOutputList3SigType49=Serial\nOutputList3Cue50=[~UNUSED3~]\nOutputList3SigType50=Serial\nOutputList3Cue51=[~UNUSED3~]\nOutputList3SigType51=Serial\nOutputList3Cue52=[~UNUSED3~]\nOutputList3SigType52=Serial\nOutputList3Cue53=[~UNUSED3~]\nOutputList3SigType53=Serial\nOutputList3Cue54=[~UNUSED3~]\nOutputList3SigType54=Serial\nOutputList3Cue55=[~UNUSED3~]\nOutputList3SigType55=Serial\nOutputList3Cue56=[~UNUSED3~]\nOutputList3SigType56=Serial\nOutputList3Cue57=[~UNUSED3~]\nOutputList3SigType57=Serial\nOutputList3Cue58=[~UNUSED3~]\nOutputList3SigType58=Serial\nOutputList3Cue59=[~UNUSED3~]\nOutputList3SigType59=Serial\nOutputList3Cue60=[~UNUSED3~]\nOutputList3SigType60=Serial\nOutputList3Cue61=[~UNUSED3~]\nOutputList3SigType61=Serial\nOutputList3Cue62=[~UNUSED3~]\nOutputList3SigType62=Serial\nOutputList3Cue63=[~UNUSED3~]\nOutputList3SigType63=Serial\nOutputList3Cue64=[~UNUSED3~]\nOutputList3SigType64=Serial\nOutputList3Cue65=[~UNUSED3~]\nOutputList3SigType65=Serial\nOutputList3Cue66=[~UNUSED3~]\nOutputList3SigType66=Serial\nOutputList3Cue67=[~UNUSED3~]\nOutputList3SigType67=Serial\nOutputList3Cue68=[~UNUSED3~]\nOutputList3SigType68=Serial\nOutputList3Cue69=[~UNUSED3~]\nOutputList3SigType69=Serial\nOutputList3Cue70=[~UNUSED3~]\nOutputList3SigType70=Serial\nOutputList3Cue71=[~UNUSED3~]\nOutputList3SigType71=Serial\nOutputList3Cue72=[~UNUSED3~]\nOutputList3SigType72=Serial\nOutputList3Cue73=[~UNUSED3~]\nOutputList3SigType73=Serial\nOutputList3Cue74=[~UNUSED3~]\nOutputList3SigType74=Serial\nOutputList3Cue75=[~UNUSED3~]\nOutputList3SigType75=Serial\nOutputList3Cue76=[~UNUSED3~]\nOutputList3SigType76=Serial\nOutputList3Cue77=[~UNUSED3~]\nOutputList3SigType77=Serial\nOutputList3Cue78=[~UNUSED3~]\nOutputList3SigType78=Serial\nOutputList3Cue79=[~UNUSED3~]\nOutputList3SigType79=Serial\nOutputList3Cue80=[~UNUSED3~]\nOutputList3SigType80=Serial\nOutputList3Cue81=[~UNUSED3~]\nOutputList3SigType81=Serial\nOutputList3Cue82=[~UNUSED3~]\nOutputList3SigType82=Serial\nOutputList3Cue83=[~UNUSED3~]\nOutputList3SigType83=Serial\nOutputList3Cue84=[~UNUSED3~]\nOutputList3SigType84=Serial\nOutputList3Cue85=[~UNUSED3~]\nOutputList3SigType85=Serial\nOutputList3Cue86=[~UNUSED3~]\nOutputList3SigType86=Serial\nOutputList3Cue87=[~UNUSED3~]\nOutputList3SigType87=Serial\nOutputList3Cue88=[~UNUSED3~]\nOutputList3SigType88=Serial\nOutputList3Cue89=[~UNUSED3~]\nOutputList3SigType89=Serial\nOutputList3Cue90=[~UNUSED3~]\nOutputList3SigType90=Serial\nOutputList3Cue91=[~UNUSED3~]\nOutputList3SigType91=Serial\nOutputList3Cue92=[~UNUSED3~]\nOutputList3SigType92=Serial\nOutputList3Cue93=[~UNUSED3~]\nOutputList3SigType93=Serial\nOutputList3Cue94=[~UNUSED3~]\nOutputList3SigType94=Serial\nOutputList3Cue95=[~UNUSED3~]\nOutputList3SigType95=Serial\nOutputList3Cue96=[~UNUSED3~]\nOutputList3SigType96=Serial\nOutputList3Cue97=[~UNUSED3~]\nOutputList3SigType97=Serial\nOutputList3Cue98=[~UNUSED3~]\nOutputList3SigType98=Serial\nOutputList3Cue99=[~UNUSED3~]\nOutputList3SigType99=Serial\nOutputList3Cue100=[~UNUSED3~]\nOutputList3SigType100=Serial\nOutputList3Cue101=[~UNUSED3~]\nOutputList3SigType101=Serial\nOutputList3Cue102=[~UNUSED3~]\nOutputList3SigType102=Serial\nOutputList3Cue103=[~UNUSED3~]\nOutputList3SigType103=Serial\nOutputList3Cue104=[~UNUSED3~]\nOutputList3SigType104=Serial\nOutputList3Cue105=[~UNUSED3~]\nOutputList3SigType105=Serial\nOutputList3Cue106=[~UNUSED3~]\nOutputList3SigType106=Serial\nOutputList3Cue107=[~UNUSED3~]\nOutputList3SigType107=Serial\nOutputList3Cue108=[~UNUSED3~]\nOutputList3SigType108=Serial\nOutputList3Cue109=[~UNUSED3~]\nOutputList3SigType109=Serial\nOutputList3Cue110=[~UNUSED3~]\nOutputList3SigType110=Serial\nOutputList3Cue111=[~UNUSED3~]\nOutputList3SigType111=Serial\nOutputList3Cue112=[~UNUSED2~]\nOutputList3SigType112=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED3~]\nOutputList3SigType2024=Serial\nOutputList3Cue2025=[~UNUSED3~]\nOutputList3SigType2025=Serial\nOutputList3Cue2026=[~UNUSED3~]\nOutputList3SigType2026=Serial\nOutputList3Cue2027=[~UNUSED3~]\nOutputList3SigType2027=Serial\nOutputList3Cue2028=[~UNUSED3~]\nOutputList3SigType2028=Serial\nOutputList3Cue2029=[~UNUSED3~]\nOutputList3SigType2029=Serial\nOutputList3Cue2030=[~UNUSED3~]\nOutputList3SigType2030=Serial\nOutputList3Cue2031=[~UNUSED3~]\nOutputList3SigType2031=Serial\nOutputList3Cue2032=[~UNUSED3~]\nOutputList3SigType2032=Serial\nOutputList3Cue2033=[~UNUSED3~]\nOutputList3SigType2033=Serial\nOutputList3Cue2034=[~UNUSED3~]\nOutputList3SigType2034=Serial\nOutputList3Cue2035=[~UNUSED3~]\nOutputList3SigType2035=Serial\nOutputList3Cue2036=[~UNUSED3~]\nOutputList3SigType2036=Serial\nOutputList3Cue2037=[~UNUSED3~]\nOutputList3SigType2037=Serial\nOutputList3Cue2038=[~UNUSED3~]\nOutputList3SigType2038=Serial\nOutputList3Cue2039=[~UNUSED3~]\nOutputList3SigType2039=Serial\nOutputList3Cue2040=[~UNUSED3~]\nOutputList3SigType2040=Serial\nOutputList3Cue2041=[~UNUSED3~]\nOutputList3SigType2041=Serial\nOutputList3Cue2042=[~UNUSED3~]\nOutputList3SigType2042=Serial\nOutputList3Cue2043=[~UNUSED3~]\nOutputList3SigType2043=Serial\nOutputList3Cue2044=[~UNUSED3~]\nOutputList3SigType2044=Serial\nOutputList3Cue2045=[~UNUSED3~]\nOutputList3SigType2045=Serial\nOutputList3Cue2046=[~UNUSED3~]\nOutputList3SigType2046=Serial\nOutputList3Cue2047=[~UNUSED3~]\nOutputList3SigType2047=Serial\nOutputList3Cue2048=[~UNUSED3~]\nOutputList3SigType2048=Serial\nOutputList3Cue2049=[~UNUSED3~]\nOutputList3SigType2049=Serial\nOutputList3Cue2050=[~UNUSED3~]\nOutputList3SigType2050=Serial\nOutputList3Cue2051=[~UNUSED3~]\nOutputList3SigType2051=Serial\nOutputList3Cue2052=[~UNUSED3~]\nOutputList3SigType2052=Serial\nOutputList3Cue2053=[~UNUSED3~]\nOutputList3SigType2053=Serial\nOutputList3Cue2054=[~UNUSED3~]\nOutputList3SigType2054=Serial\nOutputList3Cue2055=[~UNUSED3~]\nOutputList3SigType2055=Serial\nOutputList3Cue2056=[~UNUSED3~]\nOutputList3SigType2056=Serial\nOutputList3Cue2057=[~UNUSED3~]\nOutputList3SigType2057=Serial\nOutputList3Cue2058=[~UNUSED3~]\nOutputList3SigType2058=Serial\nOutputList3Cue2059=[~UNUSED3~]\nOutputList3SigType2059=Serial\nOutputList3Cue2060=[~UNUSED3~]\nOutputList3SigType2060=Serial\nOutputList3Cue2061=[~UNUSED3~]\nOutputList3SigType2061=Serial\nOutputList3Cue2062=[~UNUSED3~]\nOutputList3SigType2062=Serial\nOutputList3Cue2063=[~UNUSED3~]\nOutputList3SigType2063=Serial\nOutputList3Cue2064=[~UNUSED3~]\nOutputList3SigType2064=Serial\nOutputList3Cue2065=[~UNUSED3~]\nOutputList3SigType2065=Serial\nOutputList3Cue2066=[~UNUSED3~]\nOutputList3SigType2066=Serial\nOutputList3Cue2067=[~UNUSED3~]\nOutputList3SigType2067=Serial\nOutputList3Cue2068=[~UNUSED3~]\nOutputList3SigType2068=Serial\nOutputList3Cue2069=[~UNUSED3~]\nOutputList3SigType2069=Serial\nOutputList3Cue2070=[~UNUSED3~]\nOutputList3SigType2070=Serial\nOutputList3Cue2071=[~UNUSED3~]\nOutputList3SigType2071=Serial\nOutputList3Cue2072=[~UNUSED3~]\nOutputList3SigType2072=Serial\nOutputList3Cue2073=[~UNUSED3~]\nOutputList3SigType2073=Serial\nOutputList3Cue2074=[~UNUSED3~]\nOutputList3SigType2074=Serial\nOutputList3Cue2075=[~UNUSED3~]\nOutputList3SigType2075=Serial\nOutputList3Cue2076=[~UNUSED3~]\nOutputList3SigType2076=Serial\nOutputList3Cue2077=[~UNUSED3~]\nOutputList3SigType2077=Serial\nOutputList3Cue2078=[~UNUSED3~]\nOutputList3SigType2078=Serial\nOutputList3Cue2079=[~UNUSED3~]\nOutputList3SigType2079=Serial\nOutputList3Cue2080=[~UNUSED3~]\nOutputList3SigType2080=Serial\nOutputList3Cue2081=[~UNUSED3~]\nOutputList3SigType2081=Serial\nOutputList3Cue2082=[~UNUSED3~]\nOutputList3SigType2082=Serial\nOutputList3Cue2083=[~UNUSED3~]\nOutputList3SigType2083=Serial\nOutputList3Cue2084=[~UNUSED3~]\nOutputList3SigType2084=Serial\nOutputList3Cue2085=[~UNUSED3~]\nOutputList3SigType2085=Serial\nOutputList3Cue2086=[~UNUSED3~]\nOutputList3SigType2086=Serial\nOutputList3Cue2087=[~UNUSED3~]\nOutputList3SigType2087=Serial\nOutputList3Cue2088=[~UNUSED3~]\nOutputList3SigType2088=Serial\nOutputList3Cue2089=[~UNUSED3~]\nOutputList3SigType2089=Serial\nOutputList3Cue2090=[~UNUSED3~]\nOutputList3SigType2090=Serial\nOutputList3Cue2091=[~UNUSED3~]\nOutputList3SigType2091=Serial\nOutputList3Cue2092=[~UNUSED3~]\nOutputList3SigType2092=Serial\nOutputList3Cue2093=[~UNUSED3~]\nOutputList3SigType2093=Serial\nOutputList3Cue2094=[~UNUSED3~]\nOutputList3SigType2094=Serial\nOutputList3Cue2095=[~UNUSED3~]\nOutputList3SigType2095=Serial\nOutputList3Cue2096=[~UNUSED3~]\nOutputList3SigType2096=Serial\nOutputList3Cue2097=[~UNUSED3~]\nOutputList3SigType2097=Serial\nOutputList3Cue2098=[~UNUSED3~]\nOutputList3SigType2098=Serial\nOutputList3Cue2099=[~UNUSED3~]\nOutputList3SigType2099=Serial\nOutputList3Cue2100=[~UNUSED3~]\nOutputList3SigType2100=Serial\nOutputList3Cue2101=[~UNUSED3~]\nOutputList3SigType2101=Serial\nOutputList3Cue2102=[~UNUSED3~]\nOutputList3SigType2102=Serial\nOutputList3Cue2103=[~UNUSED3~]\nOutputList3SigType2103=Serial\nOutputList3Cue2104=[~UNUSED3~]\nOutputList3SigType2104=Serial\nOutputList3Cue2105=[~UNUSED3~]\nOutputList3SigType2105=Serial\nOutputList3Cue2106=[~UNUSED3~]\nOutputList3SigType2106=Serial\nOutputList3Cue2107=[~UNUSED3~]\nOutputList3SigType2107=Serial\nOutputList3Cue2108=[~UNUSED3~]\nOutputList3SigType2108=Serial\nOutputList3Cue2109=[~UNUSED3~]\nOutputList3SigType2109=Serial\nOutputList3Cue2110=[~UNUSED3~]\nOutputList3SigType2110=Serial\nOutputList3Cue2111=[~UNUSED3~]\nOutputList3SigType2111=Serial\nOutputList3Cue2112=[~UNUSED3~]\nOutputList3SigType2112=Serial\nOutputList3Cue2113=[~UNUSED3~]\nOutputList3SigType2113=Serial\nOutputList3Cue2114=[~UNUSED2~]\nOutputList3SigType2114=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=2\nCedH=2\nSmartObjId=1202d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=2\nTp=1\nHD=TRUE\nDV=1202d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=2\nName=PepperDash Essentials TSW-760_[D.VC] Directory_Dynamic Button List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\nHint=VC DTMF Keypad (Smart Object ID=1201)\nCode=3\nSGControlType=Simple Keypad\nSGControlName=VC DTMF Keypad\nGUID=B4D53525-26EF-4326-8234-1D53F6E9BF04\nSmplCName=PepperDash Essentials TSW-760_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=*\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=/#\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=3\nCedH=3\nSmartObjId=1201d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=3\nTp=1\nHD=TRUE\nDV=1201d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=3\nName=PepperDash Essentials TSW-760_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[Z.Dialogs] PIN_PIN Keypad.ced\nHint=PIN Keypad (Smart Object ID=3903)\nCode=4\nSGControlType=Simple Keypad\nSGControlName=PIN Keypad\nGUID=D85E5DFB-DE45-45FE-93C5-2FA177B68BFB\nSmplCName=PepperDash Essentials TSW-760_[Z.Dialogs] PIN_PIN Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=Misc_1\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=Misc_2\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=4\nCedH=4\nSmartObjId=3903d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=4\nTp=1\nHD=TRUE\nDV=3903d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=4\nName=PepperDash Essentials TSW-760_[Z.Dialogs] PIN_PIN Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\nHint=Subpage Reference List Vertical_3 (Smart Object ID=3902)\nCode=5\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_3\nGUID=3490D547-0B98-444B-A284-D6C1BAB2FDCE\nSmplCName=PepperDash Essentials TSW-760_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4076\nMaxVariableInputs=4076\nMinVariableOutputs=4076\nMaxVariableOutputs=4076\nNumFixedParams=1\nMinVariableInputsList2=72\nMaxVariableInputsList2=72\nMinVariableOutputsList2=72\nMaxVariableOutputsList2=72\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=[~UNUSED2~]\nInputSigType32=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=[~UNUSED2~]\nInputSigType2034=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=[~EndGroup~]fb\nInputSigType4076=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED2~]\nOutputSigType32=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED2~]\nOutputSigType2034=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=[~EndGroup~]Press\nOutputSigType4076=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=an_fb37\nInputList2SigType48=Analog\nInputList2Cue49=an_fb38\nInputList2SigType49=Analog\nInputList2Cue50=an_fb39\nInputList2SigType50=Analog\nInputList2Cue51=an_fb40\nInputList2SigType51=Analog\nInputList2Cue52=an_fb41\nInputList2SigType52=Analog\nInputList2Cue53=an_fb42\nInputList2SigType53=Analog\nInputList2Cue54=an_fb43\nInputList2SigType54=Analog\nInputList2Cue55=an_fb44\nInputList2SigType55=Analog\nInputList2Cue56=an_fb45\nInputList2SigType56=Analog\nInputList2Cue57=an_fb46\nInputList2SigType57=Analog\nInputList2Cue58=an_fb47\nInputList2SigType58=Analog\nInputList2Cue59=an_fb48\nInputList2SigType59=Analog\nInputList2Cue60=an_fb49\nInputList2SigType60=Analog\nInputList2Cue61=an_fb50\nInputList2SigType61=Analog\nInputList2Cue62=an_fb51\nInputList2SigType62=Analog\nInputList2Cue63=an_fb52\nInputList2SigType63=Analog\nInputList2Cue64=an_fb53\nInputList2SigType64=Analog\nInputList2Cue65=an_fb54\nInputList2SigType65=Analog\nInputList2Cue66=an_fb55\nInputList2SigType66=Analog\nInputList2Cue67=an_fb56\nInputList2SigType67=Analog\nInputList2Cue68=an_fb57\nInputList2SigType68=Analog\nInputList2Cue69=an_fb58\nInputList2SigType69=Analog\nInputList2Cue70=an_fb59\nInputList2SigType70=Analog\nInputList2Cue71=an_fb60\nInputList2SigType71=Analog\nInputList2Cue72=[~EndGroup~]an_fb\nInputList2SigType72=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=an_act37\nOutputList2SigType48=Analog\nOutputList2Cue49=an_act38\nOutputList2SigType49=Analog\nOutputList2Cue50=an_act39\nOutputList2SigType50=Analog\nOutputList2Cue51=an_act40\nOutputList2SigType51=Analog\nOutputList2Cue52=an_act41\nOutputList2SigType52=Analog\nOutputList2Cue53=an_act42\nOutputList2SigType53=Analog\nOutputList2Cue54=an_act43\nOutputList2SigType54=Analog\nOutputList2Cue55=an_act44\nOutputList2SigType55=Analog\nOutputList2Cue56=an_act45\nOutputList2SigType56=Analog\nOutputList2Cue57=an_act46\nOutputList2SigType57=Analog\nOutputList2Cue58=an_act47\nOutputList2SigType58=Analog\nOutputList2Cue59=an_act48\nOutputList2SigType59=Analog\nOutputList2Cue60=an_act49\nOutputList2SigType60=Analog\nOutputList2Cue61=an_act50\nOutputList2SigType61=Analog\nOutputList2Cue62=an_act51\nOutputList2SigType62=Analog\nOutputList2Cue63=an_act52\nOutputList2SigType63=Analog\nOutputList2Cue64=an_act53\nOutputList2SigType64=Analog\nOutputList2Cue65=an_act54\nOutputList2SigType65=Analog\nOutputList2Cue66=an_act55\nOutputList2SigType66=Analog\nOutputList2Cue67=an_act56\nOutputList2SigType67=Analog\nOutputList2Cue68=an_act57\nOutputList2SigType68=Analog\nOutputList2Cue69=an_act58\nOutputList2SigType69=Analog\nOutputList2Cue70=an_act59\nOutputList2SigType70=Analog\nOutputList2Cue71=an_act60\nOutputList2SigType71=Analog\nOutputList2Cue72=[~EndGroup~]an_act\nOutputList2SigType72=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=5\nCedH=5\nSmartObjId=3902d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=5\nTp=1\nHD=TRUE\nDV=3902d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=5\nName=PepperDash Essentials TSW-760_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\nHint=Subpage Reference List Vertical_4 (Smart Object ID=3904)\nCode=6\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_4\nGUID=AC10DF35-3FD4-4F62-9708-0A06AE7A5A14\nSmplCName=PepperDash Essentials TSW-760_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4116\nMaxVariableInputs=4116\nMinVariableOutputs=4116\nMaxVariableOutputs=4116\nNumFixedParams=1\nMinVariableInputsList2=42\nMaxVariableInputsList2=42\nMinVariableOutputsList2=42\nMaxVariableOutputsList2=42\nMinVariableInputsList3=42\nMaxVariableInputsList3=42\nMinVariableOutputsList3=42\nMaxVariableOutputsList3=42\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=[~UNUSED2~]\nInputSigType22=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=[~UNUSED2~]\nInputSigType2024=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=fb91\nInputSigType4106=Digital\nInputCue4107=fb92\nInputSigType4107=Digital\nInputCue4108=fb93\nInputSigType4108=Digital\nInputCue4109=fb94\nInputSigType4109=Digital\nInputCue4110=fb95\nInputSigType4110=Digital\nInputCue4111=fb96\nInputSigType4111=Digital\nInputCue4112=fb97\nInputSigType4112=Digital\nInputCue4113=fb98\nInputSigType4113=Digital\nInputCue4114=fb99\nInputSigType4114=Digital\nInputCue4115=fb100\nInputSigType4115=Digital\nInputCue4116=[~EndGroup~]fb\nInputSigType4116=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED2~]\nOutputSigType22=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED2~]\nOutputSigType2024=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=press91\nOutputSigType4106=Digital\nOutputCue4107=press92\nOutputSigType4107=Digital\nOutputCue4108=press93\nOutputSigType4108=Digital\nOutputCue4109=press94\nOutputSigType4109=Digital\nOutputCue4110=press95\nOutputSigType4110=Digital\nOutputCue4111=press96\nOutputSigType4111=Digital\nOutputCue4112=press97\nOutputSigType4112=Digital\nOutputCue4113=press98\nOutputSigType4113=Digital\nOutputCue4114=press99\nOutputSigType4114=Digital\nOutputCue4115=press100\nOutputSigType4115=Digital\nOutputCue4116=[~EndGroup~]Press\nOutputSigType4116=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=[~EndGroup~]an_fb\nInputList2SigType42=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=[~EndGroup~]an_act\nOutputList2SigType42=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=[~EndGroup~]text-o\nInputList3SigType42=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=[~EndGroup~]text-i\nOutputList3SigType42=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=6\nCedH=6\nSmartObjId=3904d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=6\nTp=1\nHD=TRUE\nDV=3904d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=6\nName=PepperDash Essentials TSW-760_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\nHint=Subpage Reference List Horizontal_3 (Smart Object ID=3200)\nCode=7\nSGControlType=Subpage Reference List Horizontal\nSGControlName=Subpage Reference List Horizontal_3\nGUID=D638D437-6722-43E1-B435-EF13DCE5ECF0\nSmplCName=PepperDash Essentials TSW-760_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10124\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4076\nMaxVariableInputs=4076\nMinVariableOutputs=4076\nMaxVariableOutputs=4076\nNumFixedParams=1\nMinVariableInputsList2=72\nMaxVariableInputsList2=72\nMinVariableOutputsList2=72\nMaxVariableOutputsList2=72\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=[~UNUSED2~]\nInputSigType32=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=[~UNUSED2~]\nInputSigType2034=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=[~EndGroup~]fb\nInputSigType4076=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED2~]\nOutputSigType32=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED2~]\nOutputSigType2034=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=[~EndGroup~]Press\nOutputSigType4076=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=an_fb37\nInputList2SigType48=Analog\nInputList2Cue49=an_fb38\nInputList2SigType49=Analog\nInputList2Cue50=an_fb39\nInputList2SigType50=Analog\nInputList2Cue51=an_fb40\nInputList2SigType51=Analog\nInputList2Cue52=an_fb41\nInputList2SigType52=Analog\nInputList2Cue53=an_fb42\nInputList2SigType53=Analog\nInputList2Cue54=an_fb43\nInputList2SigType54=Analog\nInputList2Cue55=an_fb44\nInputList2SigType55=Analog\nInputList2Cue56=an_fb45\nInputList2SigType56=Analog\nInputList2Cue57=an_fb46\nInputList2SigType57=Analog\nInputList2Cue58=an_fb47\nInputList2SigType58=Analog\nInputList2Cue59=an_fb48\nInputList2SigType59=Analog\nInputList2Cue60=an_fb49\nInputList2SigType60=Analog\nInputList2Cue61=an_fb50\nInputList2SigType61=Analog\nInputList2Cue62=an_fb51\nInputList2SigType62=Analog\nInputList2Cue63=an_fb52\nInputList2SigType63=Analog\nInputList2Cue64=an_fb53\nInputList2SigType64=Analog\nInputList2Cue65=an_fb54\nInputList2SigType65=Analog\nInputList2Cue66=an_fb55\nInputList2SigType66=Analog\nInputList2Cue67=an_fb56\nInputList2SigType67=Analog\nInputList2Cue68=an_fb57\nInputList2SigType68=Analog\nInputList2Cue69=an_fb58\nInputList2SigType69=Analog\nInputList2Cue70=an_fb59\nInputList2SigType70=Analog\nInputList2Cue71=an_fb60\nInputList2SigType71=Analog\nInputList2Cue72=[~EndGroup~]an_fb\nInputList2SigType72=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=an_act37\nOutputList2SigType48=Analog\nOutputList2Cue49=an_act38\nOutputList2SigType49=Analog\nOutputList2Cue50=an_act39\nOutputList2SigType50=Analog\nOutputList2Cue51=an_act40\nOutputList2SigType51=Analog\nOutputList2Cue52=an_act41\nOutputList2SigType52=Analog\nOutputList2Cue53=an_act42\nOutputList2SigType53=Analog\nOutputList2Cue54=an_act43\nOutputList2SigType54=Analog\nOutputList2Cue55=an_act44\nOutputList2SigType55=Analog\nOutputList2Cue56=an_act45\nOutputList2SigType56=Analog\nOutputList2Cue57=an_act46\nOutputList2SigType57=Analog\nOutputList2Cue58=an_act47\nOutputList2SigType58=Analog\nOutputList2Cue59=an_act48\nOutputList2SigType59=Analog\nOutputList2Cue60=an_act49\nOutputList2SigType60=Analog\nOutputList2Cue61=an_act50\nOutputList2SigType61=Analog\nOutputList2Cue62=an_act51\nOutputList2SigType62=Analog\nOutputList2Cue63=an_act52\nOutputList2SigType63=Analog\nOutputList2Cue64=an_act53\nOutputList2SigType64=Analog\nOutputList2Cue65=an_act54\nOutputList2SigType65=Analog\nOutputList2Cue66=an_act55\nOutputList2SigType66=Analog\nOutputList2Cue67=an_act56\nOutputList2SigType67=Analog\nOutputList2Cue68=an_act57\nOutputList2SigType68=Analog\nOutputList2Cue69=an_act58\nOutputList2SigType69=Analog\nOutputList2Cue70=an_act59\nOutputList2SigType70=Analog\nOutputList2Cue71=an_act60\nOutputList2SigType71=Analog\nOutputList2Cue72=[~EndGroup~]an_act\nOutputList2SigType72=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=7\nCedH=7\nSmartObjId=3200d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=7\nTp=1\nHD=TRUE\nDV=3200d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=7\nName=PepperDash Essentials TSW-760_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\nHint=Channel Preset List Basic (Smart Object ID=1806)\nCode=8\nSGControlType=Subpage Reference List Vertical\nSGControlName=Channel Preset List Basic\nGUID=46C43CE4-6BB9-4B76-A13E-B4279202F998\nSmplCName=PepperDash Essentials TSW-760_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4106\nMaxVariableInputs=4106\nMinVariableOutputs=4106\nMaxVariableOutputs=4106\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=132\nMaxVariableInputsList3=132\nMinVariableOutputsList3=132\nMaxVariableOutputsList3=132\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=[~EndGroup~]fb\nInputSigType4106=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=[~EndGroup~]Press\nOutputSigType4106=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=[~EndGroup~]text-o\nInputList3SigType132=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=[~EndGroup~]text-i\nOutputList3SigType132=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=8\nCedH=8\nSmartObjId=1806d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=8\nTp=1\nHD=TRUE\nDV=1806d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=8\nName=PepperDash Essentials TSW-760_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\nHint=Modes Bottom Bar SRL (Smart Object ID=15022)\nCode=9\nSGControlType=Subpage Reference List Horizontal\nSGControlName=Modes Bottom Bar SRL\nGUID=812FF0C4-486D-4ABC-90FA-405F19104323\nSmplCName=PepperDash Essentials TSW-760_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10124\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4031\nMaxVariableInputs=4031\nMinVariableOutputs=4031\nMaxVariableOutputs=4031\nNumFixedParams=1\nMinVariableInputsList2=27\nMaxVariableInputsList2=27\nMinVariableOutputsList2=27\nMaxVariableOutputsList2=27\nMinVariableInputsList3=27\nMaxVariableInputsList3=27\nMinVariableOutputsList3=27\nMaxVariableOutputsList3=27\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=[~UNUSED2~]\nInputSigType17=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=[~UNUSED2~]\nInputSigType2019=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=[~EndGroup~]fb\nInputSigType4031=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED2~]\nOutputSigType17=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED2~]\nOutputSigType2019=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=[~EndGroup~]Press\nOutputSigType4031=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=[~EndGroup~]an_fb\nInputList2SigType27=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=[~EndGroup~]an_act\nOutputList2SigType27=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=[~EndGroup~]text-o\nInputList3SigType27=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=[~EndGroup~]text-i\nOutputList3SigType27=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=9\nCedH=9\nSmartObjId=15022d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=9\nTp=1\nHD=TRUE\nDV=15022d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=9\nName=PepperDash Essentials TSW-760_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[B.AV] Object - Presets_CATV Icon List Vertical.ced\nHint=CATV Icon List Vertical (Smart Object ID=10012)\nCode=10\nSGControlType=Subpage Reference List Vertical\nSGControlName=CATV Icon List Vertical\nGUID=B34C4DC0-DD10-4C49-AEBA-F968934EFDC6\nSmplCName=PepperDash Essentials TSW-760_[B.AV] Object - Presets_CATV Icon List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4316\nMaxVariableInputs=4316\nMinVariableOutputs=4316\nMaxVariableOutputs=4316\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=412\nMaxVariableInputsList3=412\nMinVariableOutputsList3=412\nMaxVariableOutputsList3=412\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=Item 31 Enable\nInputSigType42=Digital\nInputCue43=Item 32 Enable\nInputSigType43=Digital\nInputCue44=Item 33 Enable\nInputSigType44=Digital\nInputCue45=Item 34 Enable\nInputSigType45=Digital\nInputCue46=Item 35 Enable\nInputSigType46=Digital\nInputCue47=Item 36 Enable\nInputSigType47=Digital\nInputCue48=Item 37 Enable\nInputSigType48=Digital\nInputCue49=Item 38 Enable\nInputSigType49=Digital\nInputCue50=Item 39 Enable\nInputSigType50=Digital\nInputCue51=Item 40 Enable\nInputSigType51=Digital\nInputCue52=Item 41 Enable\nInputSigType52=Digital\nInputCue53=Item 42 Enable\nInputSigType53=Digital\nInputCue54=Item 43 Enable\nInputSigType54=Digital\nInputCue55=Item 44 Enable\nInputSigType55=Digital\nInputCue56=Item 45 Enable\nInputSigType56=Digital\nInputCue57=Item 46 Enable\nInputSigType57=Digital\nInputCue58=Item 47 Enable\nInputSigType58=Digital\nInputCue59=Item 48 Enable\nInputSigType59=Digital\nInputCue60=Item 49 Enable\nInputSigType60=Digital\nInputCue61=Item 50 Enable\nInputSigType61=Digital\nInputCue62=Item 51 Enable\nInputSigType62=Digital\nInputCue63=Item 52 Enable\nInputSigType63=Digital\nInputCue64=Item 53 Enable\nInputSigType64=Digital\nInputCue65=Item 54 Enable\nInputSigType65=Digital\nInputCue66=Item 55 Enable\nInputSigType66=Digital\nInputCue67=Item 56 Enable\nInputSigType67=Digital\nInputCue68=Item 57 Enable\nInputSigType68=Digital\nInputCue69=Item 58 Enable\nInputSigType69=Digital\nInputCue70=Item 59 Enable\nInputSigType70=Digital\nInputCue71=Item 60 Enable\nInputSigType71=Digital\nInputCue72=Item 61 Enable\nInputSigType72=Digital\nInputCue73=Item 62 Enable\nInputSigType73=Digital\nInputCue74=Item 63 Enable\nInputSigType74=Digital\nInputCue75=Item 64 Enable\nInputSigType75=Digital\nInputCue76=Item 65 Enable\nInputSigType76=Digital\nInputCue77=Item 66 Enable\nInputSigType77=Digital\nInputCue78=Item 67 Enable\nInputSigType78=Digital\nInputCue79=Item 68 Enable\nInputSigType79=Digital\nInputCue80=Item 69 Enable\nInputSigType80=Digital\nInputCue81=Item 70 Enable\nInputSigType81=Digital\nInputCue82=Item 71 Enable\nInputSigType82=Digital\nInputCue83=Item 72 Enable\nInputSigType83=Digital\nInputCue84=Item 73 Enable\nInputSigType84=Digital\nInputCue85=Item 74 Enable\nInputSigType85=Digital\nInputCue86=Item 75 Enable\nInputSigType86=Digital\nInputCue87=Item 76 Enable\nInputSigType87=Digital\nInputCue88=Item 77 Enable\nInputSigType88=Digital\nInputCue89=Item 78 Enable\nInputSigType89=Digital\nInputCue90=Item 79 Enable\nInputSigType90=Digital\nInputCue91=Item 80 Enable\nInputSigType91=Digital\nInputCue92=Item 81 Enable\nInputSigType92=Digital\nInputCue93=Item 82 Enable\nInputSigType93=Digital\nInputCue94=Item 83 Enable\nInputSigType94=Digital\nInputCue95=Item 84 Enable\nInputSigType95=Digital\nInputCue96=Item 85 Enable\nInputSigType96=Digital\nInputCue97=Item 86 Enable\nInputSigType97=Digital\nInputCue98=Item 87 Enable\nInputSigType98=Digital\nInputCue99=Item 88 Enable\nInputSigType99=Digital\nInputCue100=Item 89 Enable\nInputSigType100=Digital\nInputCue101=Item 90 Enable\nInputSigType101=Digital\nInputCue102=Item 91 Enable\nInputSigType102=Digital\nInputCue103=Item 92 Enable\nInputSigType103=Digital\nInputCue104=Item 93 Enable\nInputSigType104=Digital\nInputCue105=Item 94 Enable\nInputSigType105=Digital\nInputCue106=Item 95 Enable\nInputSigType106=Digital\nInputCue107=Item 96 Enable\nInputSigType107=Digital\nInputCue108=Item 97 Enable\nInputSigType108=Digital\nInputCue109=Item 98 Enable\nInputSigType109=Digital\nInputCue110=Item 99 Enable\nInputSigType110=Digital\nInputCue111=Item 100 Enable\nInputSigType111=Digital\nInputCue112=[~UNUSED2~]\nInputSigType112=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=Item 31 Visible\nInputSigType2044=Digital\nInputCue2045=Item 32 Visible\nInputSigType2045=Digital\nInputCue2046=Item 33 Visible\nInputSigType2046=Digital\nInputCue2047=Item 34 Visible\nInputSigType2047=Digital\nInputCue2048=Item 35 Visible\nInputSigType2048=Digital\nInputCue2049=Item 36 Visible\nInputSigType2049=Digital\nInputCue2050=Item 37 Visible\nInputSigType2050=Digital\nInputCue2051=Item 38 Visible\nInputSigType2051=Digital\nInputCue2052=Item 39 Visible\nInputSigType2052=Digital\nInputCue2053=Item 40 Visible\nInputSigType2053=Digital\nInputCue2054=Item 41 Visible\nInputSigType2054=Digital\nInputCue2055=Item 42 Visible\nInputSigType2055=Digital\nInputCue2056=Item 43 Visible\nInputSigType2056=Digital\nInputCue2057=Item 44 Visible\nInputSigType2057=Digital\nInputCue2058=Item 45 Visible\nInputSigType2058=Digital\nInputCue2059=Item 46 Visible\nInputSigType2059=Digital\nInputCue2060=Item 47 Visible\nInputSigType2060=Digital\nInputCue2061=Item 48 Visible\nInputSigType2061=Digital\nInputCue2062=Item 49 Visible\nInputSigType2062=Digital\nInputCue2063=Item 50 Visible\nInputSigType2063=Digital\nInputCue2064=Item 51 Visible\nInputSigType2064=Digital\nInputCue2065=Item 52 Visible\nInputSigType2065=Digital\nInputCue2066=Item 53 Visible\nInputSigType2066=Digital\nInputCue2067=Item 54 Visible\nInputSigType2067=Digital\nInputCue2068=Item 55 Visible\nInputSigType2068=Digital\nInputCue2069=Item 56 Visible\nInputSigType2069=Digital\nInputCue2070=Item 57 Visible\nInputSigType2070=Digital\nInputCue2071=Item 58 Visible\nInputSigType2071=Digital\nInputCue2072=Item 59 Visible\nInputSigType2072=Digital\nInputCue2073=Item 60 Visible\nInputSigType2073=Digital\nInputCue2074=Item 61 Visible\nInputSigType2074=Digital\nInputCue2075=Item 62 Visible\nInputSigType2075=Digital\nInputCue2076=Item 63 Visible\nInputSigType2076=Digital\nInputCue2077=Item 64 Visible\nInputSigType2077=Digital\nInputCue2078=Item 65 Visible\nInputSigType2078=Digital\nInputCue2079=Item 66 Visible\nInputSigType2079=Digital\nInputCue2080=Item 67 Visible\nInputSigType2080=Digital\nInputCue2081=Item 68 Visible\nInputSigType2081=Digital\nInputCue2082=Item 69 Visible\nInputSigType2082=Digital\nInputCue2083=Item 70 Visible\nInputSigType2083=Digital\nInputCue2084=Item 71 Visible\nInputSigType2084=Digital\nInputCue2085=Item 72 Visible\nInputSigType2085=Digital\nInputCue2086=Item 73 Visible\nInputSigType2086=Digital\nInputCue2087=Item 74 Visible\nInputSigType2087=Digital\nInputCue2088=Item 75 Visible\nInputSigType2088=Digital\nInputCue2089=Item 76 Visible\nInputSigType2089=Digital\nInputCue2090=Item 77 Visible\nInputSigType2090=Digital\nInputCue2091=Item 78 Visible\nInputSigType2091=Digital\nInputCue2092=Item 79 Visible\nInputSigType2092=Digital\nInputCue2093=Item 80 Visible\nInputSigType2093=Digital\nInputCue2094=Item 81 Visible\nInputSigType2094=Digital\nInputCue2095=Item 82 Visible\nInputSigType2095=Digital\nInputCue2096=Item 83 Visible\nInputSigType2096=Digital\nInputCue2097=Item 84 Visible\nInputSigType2097=Digital\nInputCue2098=Item 85 Visible\nInputSigType2098=Digital\nInputCue2099=Item 86 Visible\nInputSigType2099=Digital\nInputCue2100=Item 87 Visible\nInputSigType2100=Digital\nInputCue2101=Item 88 Visible\nInputSigType2101=Digital\nInputCue2102=Item 89 Visible\nInputSigType2102=Digital\nInputCue2103=Item 90 Visible\nInputSigType2103=Digital\nInputCue2104=Item 91 Visible\nInputSigType2104=Digital\nInputCue2105=Item 92 Visible\nInputSigType2105=Digital\nInputCue2106=Item 93 Visible\nInputSigType2106=Digital\nInputCue2107=Item 94 Visible\nInputSigType2107=Digital\nInputCue2108=Item 95 Visible\nInputSigType2108=Digital\nInputCue2109=Item 96 Visible\nInputSigType2109=Digital\nInputCue2110=Item 97 Visible\nInputSigType2110=Digital\nInputCue2111=Item 98 Visible\nInputSigType2111=Digital\nInputCue2112=Item 99 Visible\nInputSigType2112=Digital\nInputCue2113=Item 100 Visible\nInputSigType2113=Digital\nInputCue2114=[~UNUSED2~]\nInputSigType2114=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=fb91\nInputSigType4106=Digital\nInputCue4107=fb92\nInputSigType4107=Digital\nInputCue4108=fb93\nInputSigType4108=Digital\nInputCue4109=fb94\nInputSigType4109=Digital\nInputCue4110=fb95\nInputSigType4110=Digital\nInputCue4111=fb96\nInputSigType4111=Digital\nInputCue4112=fb97\nInputSigType4112=Digital\nInputCue4113=fb98\nInputSigType4113=Digital\nInputCue4114=fb99\nInputSigType4114=Digital\nInputCue4115=fb100\nInputSigType4115=Digital\nInputCue4116=fb101\nInputSigType4116=Digital\nInputCue4117=fb102\nInputSigType4117=Digital\nInputCue4118=fb103\nInputSigType4118=Digital\nInputCue4119=fb104\nInputSigType4119=Digital\nInputCue4120=fb105\nInputSigType4120=Digital\nInputCue4121=fb106\nInputSigType4121=Digital\nInputCue4122=fb107\nInputSigType4122=Digital\nInputCue4123=fb108\nInputSigType4123=Digital\nInputCue4124=fb109\nInputSigType4124=Digital\nInputCue4125=fb110\nInputSigType4125=Digital\nInputCue4126=fb111\nInputSigType4126=Digital\nInputCue4127=fb112\nInputSigType4127=Digital\nInputCue4128=fb113\nInputSigType4128=Digital\nInputCue4129=fb114\nInputSigType4129=Digital\nInputCue4130=fb115\nInputSigType4130=Digital\nInputCue4131=fb116\nInputSigType4131=Digital\nInputCue4132=fb117\nInputSigType4132=Digital\nInputCue4133=fb118\nInputSigType4133=Digital\nInputCue4134=fb119\nInputSigType4134=Digital\nInputCue4135=fb120\nInputSigType4135=Digital\nInputCue4136=fb121\nInputSigType4136=Digital\nInputCue4137=fb122\nInputSigType4137=Digital\nInputCue4138=fb123\nInputSigType4138=Digital\nInputCue4139=fb124\nInputSigType4139=Digital\nInputCue4140=fb125\nInputSigType4140=Digital\nInputCue4141=fb126\nInputSigType4141=Digital\nInputCue4142=fb127\nInputSigType4142=Digital\nInputCue4143=fb128\nInputSigType4143=Digital\nInputCue4144=fb129\nInputSigType4144=Digital\nInputCue4145=fb130\nInputSigType4145=Digital\nInputCue4146=fb131\nInputSigType4146=Digital\nInputCue4147=fb132\nInputSigType4147=Digital\nInputCue4148=fb133\nInputSigType4148=Digital\nInputCue4149=fb134\nInputSigType4149=Digital\nInputCue4150=fb135\nInputSigType4150=Digital\nInputCue4151=fb136\nInputSigType4151=Digital\nInputCue4152=fb137\nInputSigType4152=Digital\nInputCue4153=fb138\nInputSigType4153=Digital\nInputCue4154=fb139\nInputSigType4154=Digital\nInputCue4155=fb140\nInputSigType4155=Digital\nInputCue4156=fb141\nInputSigType4156=Digital\nInputCue4157=fb142\nInputSigType4157=Digital\nInputCue4158=fb143\nInputSigType4158=Digital\nInputCue4159=fb144\nInputSigType4159=Digital\nInputCue4160=fb145\nInputSigType4160=Digital\nInputCue4161=fb146\nInputSigType4161=Digital\nInputCue4162=fb147\nInputSigType4162=Digital\nInputCue4163=fb148\nInputSigType4163=Digital\nInputCue4164=fb149\nInputSigType4164=Digital\nInputCue4165=fb150\nInputSigType4165=Digital\nInputCue4166=fb151\nInputSigType4166=Digital\nInputCue4167=fb152\nInputSigType4167=Digital\nInputCue4168=fb153\nInputSigType4168=Digital\nInputCue4169=fb154\nInputSigType4169=Digital\nInputCue4170=fb155\nInputSigType4170=Digital\nInputCue4171=fb156\nInputSigType4171=Digital\nInputCue4172=fb157\nInputSigType4172=Digital\nInputCue4173=fb158\nInputSigType4173=Digital\nInputCue4174=fb159\nInputSigType4174=Digital\nInputCue4175=fb160\nInputSigType4175=Digital\nInputCue4176=fb161\nInputSigType4176=Digital\nInputCue4177=fb162\nInputSigType4177=Digital\nInputCue4178=fb163\nInputSigType4178=Digital\nInputCue4179=fb164\nInputSigType4179=Digital\nInputCue4180=fb165\nInputSigType4180=Digital\nInputCue4181=fb166\nInputSigType4181=Digital\nInputCue4182=fb167\nInputSigType4182=Digital\nInputCue4183=fb168\nInputSigType4183=Digital\nInputCue4184=fb169\nInputSigType4184=Digital\nInputCue4185=fb170\nInputSigType4185=Digital\nInputCue4186=fb171\nInputSigType4186=Digital\nInputCue4187=fb172\nInputSigType4187=Digital\nInputCue4188=fb173\nInputSigType4188=Digital\nInputCue4189=fb174\nInputSigType4189=Digital\nInputCue4190=fb175\nInputSigType4190=Digital\nInputCue4191=fb176\nInputSigType4191=Digital\nInputCue4192=fb177\nInputSigType4192=Digital\nInputCue4193=fb178\nInputSigType4193=Digital\nInputCue4194=fb179\nInputSigType4194=Digital\nInputCue4195=fb180\nInputSigType4195=Digital\nInputCue4196=fb181\nInputSigType4196=Digital\nInputCue4197=fb182\nInputSigType4197=Digital\nInputCue4198=fb183\nInputSigType4198=Digital\nInputCue4199=fb184\nInputSigType4199=Digital\nInputCue4200=fb185\nInputSigType4200=Digital\nInputCue4201=fb186\nInputSigType4201=Digital\nInputCue4202=fb187\nInputSigType4202=Digital\nInputCue4203=fb188\nInputSigType4203=Digital\nInputCue4204=fb189\nInputSigType4204=Digital\nInputCue4205=fb190\nInputSigType4205=Digital\nInputCue4206=fb191\nInputSigType4206=Digital\nInputCue4207=fb192\nInputSigType4207=Digital\nInputCue4208=fb193\nInputSigType4208=Digital\nInputCue4209=fb194\nInputSigType4209=Digital\nInputCue4210=fb195\nInputSigType4210=Digital\nInputCue4211=fb196\nInputSigType4211=Digital\nInputCue4212=fb197\nInputSigType4212=Digital\nInputCue4213=fb198\nInputSigType4213=Digital\nInputCue4214=fb199\nInputSigType4214=Digital\nInputCue4215=fb200\nInputSigType4215=Digital\nInputCue4216=fb201\nInputSigType4216=Digital\nInputCue4217=fb202\nInputSigType4217=Digital\nInputCue4218=fb203\nInputSigType4218=Digital\nInputCue4219=fb204\nInputSigType4219=Digital\nInputCue4220=fb205\nInputSigType4220=Digital\nInputCue4221=fb206\nInputSigType4221=Digital\nInputCue4222=fb207\nInputSigType4222=Digital\nInputCue4223=fb208\nInputSigType4223=Digital\nInputCue4224=fb209\nInputSigType4224=Digital\nInputCue4225=fb210\nInputSigType4225=Digital\nInputCue4226=fb211\nInputSigType4226=Digital\nInputCue4227=fb212\nInputSigType4227=Digital\nInputCue4228=fb213\nInputSigType4228=Digital\nInputCue4229=fb214\nInputSigType4229=Digital\nInputCue4230=fb215\nInputSigType4230=Digital\nInputCue4231=fb216\nInputSigType4231=Digital\nInputCue4232=fb217\nInputSigType4232=Digital\nInputCue4233=fb218\nInputSigType4233=Digital\nInputCue4234=fb219\nInputSigType4234=Digital\nInputCue4235=fb220\nInputSigType4235=Digital\nInputCue4236=fb221\nInputSigType4236=Digital\nInputCue4237=fb222\nInputSigType4237=Digital\nInputCue4238=fb223\nInputSigType4238=Digital\nInputCue4239=fb224\nInputSigType4239=Digital\nInputCue4240=fb225\nInputSigType4240=Digital\nInputCue4241=fb226\nInputSigType4241=Digital\nInputCue4242=fb227\nInputSigType4242=Digital\nInputCue4243=fb228\nInputSigType4243=Digital\nInputCue4244=fb229\nInputSigType4244=Digital\nInputCue4245=fb230\nInputSigType4245=Digital\nInputCue4246=fb231\nInputSigType4246=Digital\nInputCue4247=fb232\nInputSigType4247=Digital\nInputCue4248=fb233\nInputSigType4248=Digital\nInputCue4249=fb234\nInputSigType4249=Digital\nInputCue4250=fb235\nInputSigType4250=Digital\nInputCue4251=fb236\nInputSigType4251=Digital\nInputCue4252=fb237\nInputSigType4252=Digital\nInputCue4253=fb238\nInputSigType4253=Digital\nInputCue4254=fb239\nInputSigType4254=Digital\nInputCue4255=fb240\nInputSigType4255=Digital\nInputCue4256=fb241\nInputSigType4256=Digital\nInputCue4257=fb242\nInputSigType4257=Digital\nInputCue4258=fb243\nInputSigType4258=Digital\nInputCue4259=fb244\nInputSigType4259=Digital\nInputCue4260=fb245\nInputSigType4260=Digital\nInputCue4261=fb246\nInputSigType4261=Digital\nInputCue4262=fb247\nInputSigType4262=Digital\nInputCue4263=fb248\nInputSigType4263=Digital\nInputCue4264=fb249\nInputSigType4264=Digital\nInputCue4265=fb250\nInputSigType4265=Digital\nInputCue4266=fb251\nInputSigType4266=Digital\nInputCue4267=fb252\nInputSigType4267=Digital\nInputCue4268=fb253\nInputSigType4268=Digital\nInputCue4269=fb254\nInputSigType4269=Digital\nInputCue4270=fb255\nInputSigType4270=Digital\nInputCue4271=fb256\nInputSigType4271=Digital\nInputCue4272=fb257\nInputSigType4272=Digital\nInputCue4273=fb258\nInputSigType4273=Digital\nInputCue4274=fb259\nInputSigType4274=Digital\nInputCue4275=fb260\nInputSigType4275=Digital\nInputCue4276=fb261\nInputSigType4276=Digital\nInputCue4277=fb262\nInputSigType4277=Digital\nInputCue4278=fb263\nInputSigType4278=Digital\nInputCue4279=fb264\nInputSigType4279=Digital\nInputCue4280=fb265\nInputSigType4280=Digital\nInputCue4281=fb266\nInputSigType4281=Digital\nInputCue4282=fb267\nInputSigType4282=Digital\nInputCue4283=fb268\nInputSigType4283=Digital\nInputCue4284=fb269\nInputSigType4284=Digital\nInputCue4285=fb270\nInputSigType4285=Digital\nInputCue4286=fb271\nInputSigType4286=Digital\nInputCue4287=fb272\nInputSigType4287=Digital\nInputCue4288=fb273\nInputSigType4288=Digital\nInputCue4289=fb274\nInputSigType4289=Digital\nInputCue4290=fb275\nInputSigType4290=Digital\nInputCue4291=fb276\nInputSigType4291=Digital\nInputCue4292=fb277\nInputSigType4292=Digital\nInputCue4293=fb278\nInputSigType4293=Digital\nInputCue4294=fb279\nInputSigType4294=Digital\nInputCue4295=fb280\nInputSigType4295=Digital\nInputCue4296=fb281\nInputSigType4296=Digital\nInputCue4297=fb282\nInputSigType4297=Digital\nInputCue4298=fb283\nInputSigType4298=Digital\nInputCue4299=fb284\nInputSigType4299=Digital\nInputCue4300=fb285\nInputSigType4300=Digital\nInputCue4301=fb286\nInputSigType4301=Digital\nInputCue4302=fb287\nInputSigType4302=Digital\nInputCue4303=fb288\nInputSigType4303=Digital\nInputCue4304=fb289\nInputSigType4304=Digital\nInputCue4305=fb290\nInputSigType4305=Digital\nInputCue4306=fb291\nInputSigType4306=Digital\nInputCue4307=fb292\nInputSigType4307=Digital\nInputCue4308=fb293\nInputSigType4308=Digital\nInputCue4309=fb294\nInputSigType4309=Digital\nInputCue4310=fb295\nInputSigType4310=Digital\nInputCue4311=fb296\nInputSigType4311=Digital\nInputCue4312=fb297\nInputSigType4312=Digital\nInputCue4313=fb298\nInputSigType4313=Digital\nInputCue4314=fb299\nInputSigType4314=Digital\nInputCue4315=fb300\nInputSigType4315=Digital\nInputCue4316=[~EndGroup~]fb\nInputSigType4316=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED3~]\nOutputSigType42=Digital\nOutputCue43=[~UNUSED3~]\nOutputSigType43=Digital\nOutputCue44=[~UNUSED3~]\nOutputSigType44=Digital\nOutputCue45=[~UNUSED3~]\nOutputSigType45=Digital\nOutputCue46=[~UNUSED3~]\nOutputSigType46=Digital\nOutputCue47=[~UNUSED3~]\nOutputSigType47=Digital\nOutputCue48=[~UNUSED3~]\nOutputSigType48=Digital\nOutputCue49=[~UNUSED3~]\nOutputSigType49=Digital\nOutputCue50=[~UNUSED3~]\nOutputSigType50=Digital\nOutputCue51=[~UNUSED3~]\nOutputSigType51=Digital\nOutputCue52=[~UNUSED3~]\nOutputSigType52=Digital\nOutputCue53=[~UNUSED3~]\nOutputSigType53=Digital\nOutputCue54=[~UNUSED3~]\nOutputSigType54=Digital\nOutputCue55=[~UNUSED3~]\nOutputSigType55=Digital\nOutputCue56=[~UNUSED3~]\nOutputSigType56=Digital\nOutputCue57=[~UNUSED3~]\nOutputSigType57=Digital\nOutputCue58=[~UNUSED3~]\nOutputSigType58=Digital\nOutputCue59=[~UNUSED3~]\nOutputSigType59=Digital\nOutputCue60=[~UNUSED3~]\nOutputSigType60=Digital\nOutputCue61=[~UNUSED3~]\nOutputSigType61=Digital\nOutputCue62=[~UNUSED3~]\nOutputSigType62=Digital\nOutputCue63=[~UNUSED3~]\nOutputSigType63=Digital\nOutputCue64=[~UNUSED3~]\nOutputSigType64=Digital\nOutputCue65=[~UNUSED3~]\nOutputSigType65=Digital\nOutputCue66=[~UNUSED3~]\nOutputSigType66=Digital\nOutputCue67=[~UNUSED3~]\nOutputSigType67=Digital\nOutputCue68=[~UNUSED3~]\nOutputSigType68=Digital\nOutputCue69=[~UNUSED3~]\nOutputSigType69=Digital\nOutputCue70=[~UNUSED3~]\nOutputSigType70=Digital\nOutputCue71=[~UNUSED3~]\nOutputSigType71=Digital\nOutputCue72=[~UNUSED3~]\nOutputSigType72=Digital\nOutputCue73=[~UNUSED3~]\nOutputSigType73=Digital\nOutputCue74=[~UNUSED3~]\nOutputSigType74=Digital\nOutputCue75=[~UNUSED3~]\nOutputSigType75=Digital\nOutputCue76=[~UNUSED3~]\nOutputSigType76=Digital\nOutputCue77=[~UNUSED3~]\nOutputSigType77=Digital\nOutputCue78=[~UNUSED3~]\nOutputSigType78=Digital\nOutputCue79=[~UNUSED3~]\nOutputSigType79=Digital\nOutputCue80=[~UNUSED3~]\nOutputSigType80=Digital\nOutputCue81=[~UNUSED3~]\nOutputSigType81=Digital\nOutputCue82=[~UNUSED3~]\nOutputSigType82=Digital\nOutputCue83=[~UNUSED3~]\nOutputSigType83=Digital\nOutputCue84=[~UNUSED3~]\nOutputSigType84=Digital\nOutputCue85=[~UNUSED3~]\nOutputSigType85=Digital\nOutputCue86=[~UNUSED3~]\nOutputSigType86=Digital\nOutputCue87=[~UNUSED3~]\nOutputSigType87=Digital\nOutputCue88=[~UNUSED3~]\nOutputSigType88=Digital\nOutputCue89=[~UNUSED3~]\nOutputSigType89=Digital\nOutputCue90=[~UNUSED3~]\nOutputSigType90=Digital\nOutputCue91=[~UNUSED3~]\nOutputSigType91=Digital\nOutputCue92=[~UNUSED3~]\nOutputSigType92=Digital\nOutputCue93=[~UNUSED3~]\nOutputSigType93=Digital\nOutputCue94=[~UNUSED3~]\nOutputSigType94=Digital\nOutputCue95=[~UNUSED3~]\nOutputSigType95=Digital\nOutputCue96=[~UNUSED3~]\nOutputSigType96=Digital\nOutputCue97=[~UNUSED3~]\nOutputSigType97=Digital\nOutputCue98=[~UNUSED3~]\nOutputSigType98=Digital\nOutputCue99=[~UNUSED3~]\nOutputSigType99=Digital\nOutputCue100=[~UNUSED3~]\nOutputSigType100=Digital\nOutputCue101=[~UNUSED3~]\nOutputSigType101=Digital\nOutputCue102=[~UNUSED3~]\nOutputSigType102=Digital\nOutputCue103=[~UNUSED3~]\nOutputSigType103=Digital\nOutputCue104=[~UNUSED3~]\nOutputSigType104=Digital\nOutputCue105=[~UNUSED3~]\nOutputSigType105=Digital\nOutputCue106=[~UNUSED3~]\nOutputSigType106=Digital\nOutputCue107=[~UNUSED3~]\nOutputSigType107=Digital\nOutputCue108=[~UNUSED3~]\nOutputSigType108=Digital\nOutputCue109=[~UNUSED3~]\nOutputSigType109=Digital\nOutputCue110=[~UNUSED3~]\nOutputSigType110=Digital\nOutputCue111=[~UNUSED3~]\nOutputSigType111=Digital\nOutputCue112=[~UNUSED2~]\nOutputSigType112=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED3~]\nOutputSigType2044=Digital\nOutputCue2045=[~UNUSED3~]\nOutputSigType2045=Digital\nOutputCue2046=[~UNUSED3~]\nOutputSigType2046=Digital\nOutputCue2047=[~UNUSED3~]\nOutputSigType2047=Digital\nOutputCue2048=[~UNUSED3~]\nOutputSigType2048=Digital\nOutputCue2049=[~UNUSED3~]\nOutputSigType2049=Digital\nOutputCue2050=[~UNUSED3~]\nOutputSigType2050=Digital\nOutputCue2051=[~UNUSED3~]\nOutputSigType2051=Digital\nOutputCue2052=[~UNUSED3~]\nOutputSigType2052=Digital\nOutputCue2053=[~UNUSED3~]\nOutputSigType2053=Digital\nOutputCue2054=[~UNUSED3~]\nOutputSigType2054=Digital\nOutputCue2055=[~UNUSED3~]\nOutputSigType2055=Digital\nOutputCue2056=[~UNUSED3~]\nOutputSigType2056=Digital\nOutputCue2057=[~UNUSED3~]\nOutputSigType2057=Digital\nOutputCue2058=[~UNUSED3~]\nOutputSigType2058=Digital\nOutputCue2059=[~UNUSED3~]\nOutputSigType2059=Digital\nOutputCue2060=[~UNUSED3~]\nOutputSigType2060=Digital\nOutputCue2061=[~UNUSED3~]\nOutputSigType2061=Digital\nOutputCue2062=[~UNUSED3~]\nOutputSigType2062=Digital\nOutputCue2063=[~UNUSED3~]\nOutputSigType2063=Digital\nOutputCue2064=[~UNUSED3~]\nOutputSigType2064=Digital\nOutputCue2065=[~UNUSED3~]\nOutputSigType2065=Digital\nOutputCue2066=[~UNUSED3~]\nOutputSigType2066=Digital\nOutputCue2067=[~UNUSED3~]\nOutputSigType2067=Digital\nOutputCue2068=[~UNUSED3~]\nOutputSigType2068=Digital\nOutputCue2069=[~UNUSED3~]\nOutputSigType2069=Digital\nOutputCue2070=[~UNUSED3~]\nOutputSigType2070=Digital\nOutputCue2071=[~UNUSED3~]\nOutputSigType2071=Digital\nOutputCue2072=[~UNUSED3~]\nOutputSigType2072=Digital\nOutputCue2073=[~UNUSED3~]\nOutputSigType2073=Digital\nOutputCue2074=[~UNUSED3~]\nOutputSigType2074=Digital\nOutputCue2075=[~UNUSED3~]\nOutputSigType2075=Digital\nOutputCue2076=[~UNUSED3~]\nOutputSigType2076=Digital\nOutputCue2077=[~UNUSED3~]\nOutputSigType2077=Digital\nOutputCue2078=[~UNUSED3~]\nOutputSigType2078=Digital\nOutputCue2079=[~UNUSED3~]\nOutputSigType2079=Digital\nOutputCue2080=[~UNUSED3~]\nOutputSigType2080=Digital\nOutputCue2081=[~UNUSED3~]\nOutputSigType2081=Digital\nOutputCue2082=[~UNUSED3~]\nOutputSigType2082=Digital\nOutputCue2083=[~UNUSED3~]\nOutputSigType2083=Digital\nOutputCue2084=[~UNUSED3~]\nOutputSigType2084=Digital\nOutputCue2085=[~UNUSED3~]\nOutputSigType2085=Digital\nOutputCue2086=[~UNUSED3~]\nOutputSigType2086=Digital\nOutputCue2087=[~UNUSED3~]\nOutputSigType2087=Digital\nOutputCue2088=[~UNUSED3~]\nOutputSigType2088=Digital\nOutputCue2089=[~UNUSED3~]\nOutputSigType2089=Digital\nOutputCue2090=[~UNUSED3~]\nOutputSigType2090=Digital\nOutputCue2091=[~UNUSED3~]\nOutputSigType2091=Digital\nOutputCue2092=[~UNUSED3~]\nOutputSigType2092=Digital\nOutputCue2093=[~UNUSED3~]\nOutputSigType2093=Digital\nOutputCue2094=[~UNUSED3~]\nOutputSigType2094=Digital\nOutputCue2095=[~UNUSED3~]\nOutputSigType2095=Digital\nOutputCue2096=[~UNUSED3~]\nOutputSigType2096=Digital\nOutputCue2097=[~UNUSED3~]\nOutputSigType2097=Digital\nOutputCue2098=[~UNUSED3~]\nOutputSigType2098=Digital\nOutputCue2099=[~UNUSED3~]\nOutputSigType2099=Digital\nOutputCue2100=[~UNUSED3~]\nOutputSigType2100=Digital\nOutputCue2101=[~UNUSED3~]\nOutputSigType2101=Digital\nOutputCue2102=[~UNUSED3~]\nOutputSigType2102=Digital\nOutputCue2103=[~UNUSED3~]\nOutputSigType2103=Digital\nOutputCue2104=[~UNUSED3~]\nOutputSigType2104=Digital\nOutputCue2105=[~UNUSED3~]\nOutputSigType2105=Digital\nOutputCue2106=[~UNUSED3~]\nOutputSigType2106=Digital\nOutputCue2107=[~UNUSED3~]\nOutputSigType2107=Digital\nOutputCue2108=[~UNUSED3~]\nOutputSigType2108=Digital\nOutputCue2109=[~UNUSED3~]\nOutputSigType2109=Digital\nOutputCue2110=[~UNUSED3~]\nOutputSigType2110=Digital\nOutputCue2111=[~UNUSED3~]\nOutputSigType2111=Digital\nOutputCue2112=[~UNUSED3~]\nOutputSigType2112=Digital\nOutputCue2113=[~UNUSED3~]\nOutputSigType2113=Digital\nOutputCue2114=[~UNUSED2~]\nOutputSigType2114=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=press91\nOutputSigType4106=Digital\nOutputCue4107=press92\nOutputSigType4107=Digital\nOutputCue4108=press93\nOutputSigType4108=Digital\nOutputCue4109=press94\nOutputSigType4109=Digital\nOutputCue4110=press95\nOutputSigType4110=Digital\nOutputCue4111=press96\nOutputSigType4111=Digital\nOutputCue4112=press97\nOutputSigType4112=Digital\nOutputCue4113=press98\nOutputSigType4113=Digital\nOutputCue4114=press99\nOutputSigType4114=Digital\nOutputCue4115=press100\nOutputSigType4115=Digital\nOutputCue4116=press101\nOutputSigType4116=Digital\nOutputCue4117=press102\nOutputSigType4117=Digital\nOutputCue4118=press103\nOutputSigType4118=Digital\nOutputCue4119=press104\nOutputSigType4119=Digital\nOutputCue4120=press105\nOutputSigType4120=Digital\nOutputCue4121=press106\nOutputSigType4121=Digital\nOutputCue4122=press107\nOutputSigType4122=Digital\nOutputCue4123=press108\nOutputSigType4123=Digital\nOutputCue4124=press109\nOutputSigType4124=Digital\nOutputCue4125=press110\nOutputSigType4125=Digital\nOutputCue4126=press111\nOutputSigType4126=Digital\nOutputCue4127=press112\nOutputSigType4127=Digital\nOutputCue4128=press113\nOutputSigType4128=Digital\nOutputCue4129=press114\nOutputSigType4129=Digital\nOutputCue4130=press115\nOutputSigType4130=Digital\nOutputCue4131=press116\nOutputSigType4131=Digital\nOutputCue4132=press117\nOutputSigType4132=Digital\nOutputCue4133=press118\nOutputSigType4133=Digital\nOutputCue4134=press119\nOutputSigType4134=Digital\nOutputCue4135=press120\nOutputSigType4135=Digital\nOutputCue4136=press121\nOutputSigType4136=Digital\nOutputCue4137=press122\nOutputSigType4137=Digital\nOutputCue4138=press123\nOutputSigType4138=Digital\nOutputCue4139=press124\nOutputSigType4139=Digital\nOutputCue4140=press125\nOutputSigType4140=Digital\nOutputCue4141=press126\nOutputSigType4141=Digital\nOutputCue4142=press127\nOutputSigType4142=Digital\nOutputCue4143=press128\nOutputSigType4143=Digital\nOutputCue4144=press129\nOutputSigType4144=Digital\nOutputCue4145=press130\nOutputSigType4145=Digital\nOutputCue4146=press131\nOutputSigType4146=Digital\nOutputCue4147=press132\nOutputSigType4147=Digital\nOutputCue4148=press133\nOutputSigType4148=Digital\nOutputCue4149=press134\nOutputSigType4149=Digital\nOutputCue4150=press135\nOutputSigType4150=Digital\nOutputCue4151=press136\nOutputSigType4151=Digital\nOutputCue4152=press137\nOutputSigType4152=Digital\nOutputCue4153=press138\nOutputSigType4153=Digital\nOutputCue4154=press139\nOutputSigType4154=Digital\nOutputCue4155=press140\nOutputSigType4155=Digital\nOutputCue4156=press141\nOutputSigType4156=Digital\nOutputCue4157=press142\nOutputSigType4157=Digital\nOutputCue4158=press143\nOutputSigType4158=Digital\nOutputCue4159=press144\nOutputSigType4159=Digital\nOutputCue4160=press145\nOutputSigType4160=Digital\nOutputCue4161=press146\nOutputSigType4161=Digital\nOutputCue4162=press147\nOutputSigType4162=Digital\nOutputCue4163=press148\nOutputSigType4163=Digital\nOutputCue4164=press149\nOutputSigType4164=Digital\nOutputCue4165=press150\nOutputSigType4165=Digital\nOutputCue4166=press151\nOutputSigType4166=Digital\nOutputCue4167=press152\nOutputSigType4167=Digital\nOutputCue4168=press153\nOutputSigType4168=Digital\nOutputCue4169=press154\nOutputSigType4169=Digital\nOutputCue4170=press155\nOutputSigType4170=Digital\nOutputCue4171=press156\nOutputSigType4171=Digital\nOutputCue4172=press157\nOutputSigType4172=Digital\nOutputCue4173=press158\nOutputSigType4173=Digital\nOutputCue4174=press159\nOutputSigType4174=Digital\nOutputCue4175=press160\nOutputSigType4175=Digital\nOutputCue4176=press161\nOutputSigType4176=Digital\nOutputCue4177=press162\nOutputSigType4177=Digital\nOutputCue4178=press163\nOutputSigType4178=Digital\nOutputCue4179=press164\nOutputSigType4179=Digital\nOutputCue4180=press165\nOutputSigType4180=Digital\nOutputCue4181=press166\nOutputSigType4181=Digital\nOutputCue4182=press167\nOutputSigType4182=Digital\nOutputCue4183=press168\nOutputSigType4183=Digital\nOutputCue4184=press169\nOutputSigType4184=Digital\nOutputCue4185=press170\nOutputSigType4185=Digital\nOutputCue4186=press171\nOutputSigType4186=Digital\nOutputCue4187=press172\nOutputSigType4187=Digital\nOutputCue4188=press173\nOutputSigType4188=Digital\nOutputCue4189=press174\nOutputSigType4189=Digital\nOutputCue4190=press175\nOutputSigType4190=Digital\nOutputCue4191=press176\nOutputSigType4191=Digital\nOutputCue4192=press177\nOutputSigType4192=Digital\nOutputCue4193=press178\nOutputSigType4193=Digital\nOutputCue4194=press179\nOutputSigType4194=Digital\nOutputCue4195=press180\nOutputSigType4195=Digital\nOutputCue4196=press181\nOutputSigType4196=Digital\nOutputCue4197=press182\nOutputSigType4197=Digital\nOutputCue4198=press183\nOutputSigType4198=Digital\nOutputCue4199=press184\nOutputSigType4199=Digital\nOutputCue4200=press185\nOutputSigType4200=Digital\nOutputCue4201=press186\nOutputSigType4201=Digital\nOutputCue4202=press187\nOutputSigType4202=Digital\nOutputCue4203=press188\nOutputSigType4203=Digital\nOutputCue4204=press189\nOutputSigType4204=Digital\nOutputCue4205=press190\nOutputSigType4205=Digital\nOutputCue4206=press191\nOutputSigType4206=Digital\nOutputCue4207=press192\nOutputSigType4207=Digital\nOutputCue4208=press193\nOutputSigType4208=Digital\nOutputCue4209=press194\nOutputSigType4209=Digital\nOutputCue4210=press195\nOutputSigType4210=Digital\nOutputCue4211=press196\nOutputSigType4211=Digital\nOutputCue4212=press197\nOutputSigType4212=Digital\nOutputCue4213=press198\nOutputSigType4213=Digital\nOutputCue4214=press199\nOutputSigType4214=Digital\nOutputCue4215=press200\nOutputSigType4215=Digital\nOutputCue4216=press201\nOutputSigType4216=Digital\nOutputCue4217=press202\nOutputSigType4217=Digital\nOutputCue4218=press203\nOutputSigType4218=Digital\nOutputCue4219=press204\nOutputSigType4219=Digital\nOutputCue4220=press205\nOutputSigType4220=Digital\nOutputCue4221=press206\nOutputSigType4221=Digital\nOutputCue4222=press207\nOutputSigType4222=Digital\nOutputCue4223=press208\nOutputSigType4223=Digital\nOutputCue4224=press209\nOutputSigType4224=Digital\nOutputCue4225=press210\nOutputSigType4225=Digital\nOutputCue4226=press211\nOutputSigType4226=Digital\nOutputCue4227=press212\nOutputSigType4227=Digital\nOutputCue4228=press213\nOutputSigType4228=Digital\nOutputCue4229=press214\nOutputSigType4229=Digital\nOutputCue4230=press215\nOutputSigType4230=Digital\nOutputCue4231=press216\nOutputSigType4231=Digital\nOutputCue4232=press217\nOutputSigType4232=Digital\nOutputCue4233=press218\nOutputSigType4233=Digital\nOutputCue4234=press219\nOutputSigType4234=Digital\nOutputCue4235=press220\nOutputSigType4235=Digital\nOutputCue4236=press221\nOutputSigType4236=Digital\nOutputCue4237=press222\nOutputSigType4237=Digital\nOutputCue4238=press223\nOutputSigType4238=Digital\nOutputCue4239=press224\nOutputSigType4239=Digital\nOutputCue4240=press225\nOutputSigType4240=Digital\nOutputCue4241=press226\nOutputSigType4241=Digital\nOutputCue4242=press227\nOutputSigType4242=Digital\nOutputCue4243=press228\nOutputSigType4243=Digital\nOutputCue4244=press229\nOutputSigType4244=Digital\nOutputCue4245=press230\nOutputSigType4245=Digital\nOutputCue4246=press231\nOutputSigType4246=Digital\nOutputCue4247=press232\nOutputSigType4247=Digital\nOutputCue4248=press233\nOutputSigType4248=Digital\nOutputCue4249=press234\nOutputSigType4249=Digital\nOutputCue4250=press235\nOutputSigType4250=Digital\nOutputCue4251=press236\nOutputSigType4251=Digital\nOutputCue4252=press237\nOutputSigType4252=Digital\nOutputCue4253=press238\nOutputSigType4253=Digital\nOutputCue4254=press239\nOutputSigType4254=Digital\nOutputCue4255=press240\nOutputSigType4255=Digital\nOutputCue4256=press241\nOutputSigType4256=Digital\nOutputCue4257=press242\nOutputSigType4257=Digital\nOutputCue4258=press243\nOutputSigType4258=Digital\nOutputCue4259=press244\nOutputSigType4259=Digital\nOutputCue4260=press245\nOutputSigType4260=Digital\nOutputCue4261=press246\nOutputSigType4261=Digital\nOutputCue4262=press247\nOutputSigType4262=Digital\nOutputCue4263=press248\nOutputSigType4263=Digital\nOutputCue4264=press249\nOutputSigType4264=Digital\nOutputCue4265=press250\nOutputSigType4265=Digital\nOutputCue4266=press251\nOutputSigType4266=Digital\nOutputCue4267=press252\nOutputSigType4267=Digital\nOutputCue4268=press253\nOutputSigType4268=Digital\nOutputCue4269=press254\nOutputSigType4269=Digital\nOutputCue4270=press255\nOutputSigType4270=Digital\nOutputCue4271=press256\nOutputSigType4271=Digital\nOutputCue4272=press257\nOutputSigType4272=Digital\nOutputCue4273=press258\nOutputSigType4273=Digital\nOutputCue4274=press259\nOutputSigType4274=Digital\nOutputCue4275=press260\nOutputSigType4275=Digital\nOutputCue4276=press261\nOutputSigType4276=Digital\nOutputCue4277=press262\nOutputSigType4277=Digital\nOutputCue4278=press263\nOutputSigType4278=Digital\nOutputCue4279=press264\nOutputSigType4279=Digital\nOutputCue4280=press265\nOutputSigType4280=Digital\nOutputCue4281=press266\nOutputSigType4281=Digital\nOutputCue4282=press267\nOutputSigType4282=Digital\nOutputCue4283=press268\nOutputSigType4283=Digital\nOutputCue4284=press269\nOutputSigType4284=Digital\nOutputCue4285=press270\nOutputSigType4285=Digital\nOutputCue4286=press271\nOutputSigType4286=Digital\nOutputCue4287=press272\nOutputSigType4287=Digital\nOutputCue4288=press273\nOutputSigType4288=Digital\nOutputCue4289=press274\nOutputSigType4289=Digital\nOutputCue4290=press275\nOutputSigType4290=Digital\nOutputCue4291=press276\nOutputSigType4291=Digital\nOutputCue4292=press277\nOutputSigType4292=Digital\nOutputCue4293=press278\nOutputSigType4293=Digital\nOutputCue4294=press279\nOutputSigType4294=Digital\nOutputCue4295=press280\nOutputSigType4295=Digital\nOutputCue4296=press281\nOutputSigType4296=Digital\nOutputCue4297=press282\nOutputSigType4297=Digital\nOutputCue4298=press283\nOutputSigType4298=Digital\nOutputCue4299=press284\nOutputSigType4299=Digital\nOutputCue4300=press285\nOutputSigType4300=Digital\nOutputCue4301=press286\nOutputSigType4301=Digital\nOutputCue4302=press287\nOutputSigType4302=Digital\nOutputCue4303=press288\nOutputSigType4303=Digital\nOutputCue4304=press289\nOutputSigType4304=Digital\nOutputCue4305=press290\nOutputSigType4305=Digital\nOutputCue4306=press291\nOutputSigType4306=Digital\nOutputCue4307=press292\nOutputSigType4307=Digital\nOutputCue4308=press293\nOutputSigType4308=Digital\nOutputCue4309=press294\nOutputSigType4309=Digital\nOutputCue4310=press295\nOutputSigType4310=Digital\nOutputCue4311=press296\nOutputSigType4311=Digital\nOutputCue4312=press297\nOutputSigType4312=Digital\nOutputCue4313=press298\nOutputSigType4313=Digital\nOutputCue4314=press299\nOutputSigType4314=Digital\nOutputCue4315=press300\nOutputSigType4315=Digital\nOutputCue4316=[~EndGroup~]Press\nOutputSigType4316=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=text-o121\nInputList3SigType132=Serial\nInputList3Cue133=text-o122\nInputList3SigType133=Serial\nInputList3Cue134=text-o123\nInputList3SigType134=Serial\nInputList3Cue135=text-o124\nInputList3SigType135=Serial\nInputList3Cue136=text-o125\nInputList3SigType136=Serial\nInputList3Cue137=text-o126\nInputList3SigType137=Serial\nInputList3Cue138=text-o127\nInputList3SigType138=Serial\nInputList3Cue139=text-o128\nInputList3SigType139=Serial\nInputList3Cue140=text-o129\nInputList3SigType140=Serial\nInputList3Cue141=text-o130\nInputList3SigType141=Serial\nInputList3Cue142=text-o131\nInputList3SigType142=Serial\nInputList3Cue143=text-o132\nInputList3SigType143=Serial\nInputList3Cue144=text-o133\nInputList3SigType144=Serial\nInputList3Cue145=text-o134\nInputList3SigType145=Serial\nInputList3Cue146=text-o135\nInputList3SigType146=Serial\nInputList3Cue147=text-o136\nInputList3SigType147=Serial\nInputList3Cue148=text-o137\nInputList3SigType148=Serial\nInputList3Cue149=text-o138\nInputList3SigType149=Serial\nInputList3Cue150=text-o139\nInputList3SigType150=Serial\nInputList3Cue151=text-o140\nInputList3SigType151=Serial\nInputList3Cue152=text-o141\nInputList3SigType152=Serial\nInputList3Cue153=text-o142\nInputList3SigType153=Serial\nInputList3Cue154=text-o143\nInputList3SigType154=Serial\nInputList3Cue155=text-o144\nInputList3SigType155=Serial\nInputList3Cue156=text-o145\nInputList3SigType156=Serial\nInputList3Cue157=text-o146\nInputList3SigType157=Serial\nInputList3Cue158=text-o147\nInputList3SigType158=Serial\nInputList3Cue159=text-o148\nInputList3SigType159=Serial\nInputList3Cue160=text-o149\nInputList3SigType160=Serial\nInputList3Cue161=text-o150\nInputList3SigType161=Serial\nInputList3Cue162=text-o151\nInputList3SigType162=Serial\nInputList3Cue163=text-o152\nInputList3SigType163=Serial\nInputList3Cue164=text-o153\nInputList3SigType164=Serial\nInputList3Cue165=text-o154\nInputList3SigType165=Serial\nInputList3Cue166=text-o155\nInputList3SigType166=Serial\nInputList3Cue167=text-o156\nInputList3SigType167=Serial\nInputList3Cue168=text-o157\nInputList3SigType168=Serial\nInputList3Cue169=text-o158\nInputList3SigType169=Serial\nInputList3Cue170=text-o159\nInputList3SigType170=Serial\nInputList3Cue171=text-o160\nInputList3SigType171=Serial\nInputList3Cue172=text-o161\nInputList3SigType172=Serial\nInputList3Cue173=text-o162\nInputList3SigType173=Serial\nInputList3Cue174=text-o163\nInputList3SigType174=Serial\nInputList3Cue175=text-o164\nInputList3SigType175=Serial\nInputList3Cue176=text-o165\nInputList3SigType176=Serial\nInputList3Cue177=text-o166\nInputList3SigType177=Serial\nInputList3Cue178=text-o167\nInputList3SigType178=Serial\nInputList3Cue179=text-o168\nInputList3SigType179=Serial\nInputList3Cue180=text-o169\nInputList3SigType180=Serial\nInputList3Cue181=text-o170\nInputList3SigType181=Serial\nInputList3Cue182=text-o171\nInputList3SigType182=Serial\nInputList3Cue183=text-o172\nInputList3SigType183=Serial\nInputList3Cue184=text-o173\nInputList3SigType184=Serial\nInputList3Cue185=text-o174\nInputList3SigType185=Serial\nInputList3Cue186=text-o175\nInputList3SigType186=Serial\nInputList3Cue187=text-o176\nInputList3SigType187=Serial\nInputList3Cue188=text-o177\nInputList3SigType188=Serial\nInputList3Cue189=text-o178\nInputList3SigType189=Serial\nInputList3Cue190=text-o179\nInputList3SigType190=Serial\nInputList3Cue191=text-o180\nInputList3SigType191=Serial\nInputList3Cue192=text-o181\nInputList3SigType192=Serial\nInputList3Cue193=text-o182\nInputList3SigType193=Serial\nInputList3Cue194=text-o183\nInputList3SigType194=Serial\nInputList3Cue195=text-o184\nInputList3SigType195=Serial\nInputList3Cue196=text-o185\nInputList3SigType196=Serial\nInputList3Cue197=text-o186\nInputList3SigType197=Serial\nInputList3Cue198=text-o187\nInputList3SigType198=Serial\nInputList3Cue199=text-o188\nInputList3SigType199=Serial\nInputList3Cue200=text-o189\nInputList3SigType200=Serial\nInputList3Cue201=text-o190\nInputList3SigType201=Serial\nInputList3Cue202=text-o191\nInputList3SigType202=Serial\nInputList3Cue203=text-o192\nInputList3SigType203=Serial\nInputList3Cue204=text-o193\nInputList3SigType204=Serial\nInputList3Cue205=text-o194\nInputList3SigType205=Serial\nInputList3Cue206=text-o195\nInputList3SigType206=Serial\nInputList3Cue207=text-o196\nInputList3SigType207=Serial\nInputList3Cue208=text-o197\nInputList3SigType208=Serial\nInputList3Cue209=text-o198\nInputList3SigType209=Serial\nInputList3Cue210=text-o199\nInputList3SigType210=Serial\nInputList3Cue211=text-o200\nInputList3SigType211=Serial\nInputList3Cue212=text-o201\nInputList3SigType212=Serial\nInputList3Cue213=text-o202\nInputList3SigType213=Serial\nInputList3Cue214=text-o203\nInputList3SigType214=Serial\nInputList3Cue215=text-o204\nInputList3SigType215=Serial\nInputList3Cue216=text-o205\nInputList3SigType216=Serial\nInputList3Cue217=text-o206\nInputList3SigType217=Serial\nInputList3Cue218=text-o207\nInputList3SigType218=Serial\nInputList3Cue219=text-o208\nInputList3SigType219=Serial\nInputList3Cue220=text-o209\nInputList3SigType220=Serial\nInputList3Cue221=text-o210\nInputList3SigType221=Serial\nInputList3Cue222=text-o211\nInputList3SigType222=Serial\nInputList3Cue223=text-o212\nInputList3SigType223=Serial\nInputList3Cue224=text-o213\nInputList3SigType224=Serial\nInputList3Cue225=text-o214\nInputList3SigType225=Serial\nInputList3Cue226=text-o215\nInputList3SigType226=Serial\nInputList3Cue227=text-o216\nInputList3SigType227=Serial\nInputList3Cue228=text-o217\nInputList3SigType228=Serial\nInputList3Cue229=text-o218\nInputList3SigType229=Serial\nInputList3Cue230=text-o219\nInputList3SigType230=Serial\nInputList3Cue231=text-o220\nInputList3SigType231=Serial\nInputList3Cue232=text-o221\nInputList3SigType232=Serial\nInputList3Cue233=text-o222\nInputList3SigType233=Serial\nInputList3Cue234=text-o223\nInputList3SigType234=Serial\nInputList3Cue235=text-o224\nInputList3SigType235=Serial\nInputList3Cue236=text-o225\nInputList3SigType236=Serial\nInputList3Cue237=text-o226\nInputList3SigType237=Serial\nInputList3Cue238=text-o227\nInputList3SigType238=Serial\nInputList3Cue239=text-o228\nInputList3SigType239=Serial\nInputList3Cue240=text-o229\nInputList3SigType240=Serial\nInputList3Cue241=text-o230\nInputList3SigType241=Serial\nInputList3Cue242=text-o231\nInputList3SigType242=Serial\nInputList3Cue243=text-o232\nInputList3SigType243=Serial\nInputList3Cue244=text-o233\nInputList3SigType244=Serial\nInputList3Cue245=text-o234\nInputList3SigType245=Serial\nInputList3Cue246=text-o235\nInputList3SigType246=Serial\nInputList3Cue247=text-o236\nInputList3SigType247=Serial\nInputList3Cue248=text-o237\nInputList3SigType248=Serial\nInputList3Cue249=text-o238\nInputList3SigType249=Serial\nInputList3Cue250=text-o239\nInputList3SigType250=Serial\nInputList3Cue251=text-o240\nInputList3SigType251=Serial\nInputList3Cue252=text-o241\nInputList3SigType252=Serial\nInputList3Cue253=text-o242\nInputList3SigType253=Serial\nInputList3Cue254=text-o243\nInputList3SigType254=Serial\nInputList3Cue255=text-o244\nInputList3SigType255=Serial\nInputList3Cue256=text-o245\nInputList3SigType256=Serial\nInputList3Cue257=text-o246\nInputList3SigType257=Serial\nInputList3Cue258=text-o247\nInputList3SigType258=Serial\nInputList3Cue259=text-o248\nInputList3SigType259=Serial\nInputList3Cue260=text-o249\nInputList3SigType260=Serial\nInputList3Cue261=text-o250\nInputList3SigType261=Serial\nInputList3Cue262=text-o251\nInputList3SigType262=Serial\nInputList3Cue263=text-o252\nInputList3SigType263=Serial\nInputList3Cue264=text-o253\nInputList3SigType264=Serial\nInputList3Cue265=text-o254\nInputList3SigType265=Serial\nInputList3Cue266=text-o255\nInputList3SigType266=Serial\nInputList3Cue267=text-o256\nInputList3SigType267=Serial\nInputList3Cue268=text-o257\nInputList3SigType268=Serial\nInputList3Cue269=text-o258\nInputList3SigType269=Serial\nInputList3Cue270=text-o259\nInputList3SigType270=Serial\nInputList3Cue271=text-o260\nInputList3SigType271=Serial\nInputList3Cue272=text-o261\nInputList3SigType272=Serial\nInputList3Cue273=text-o262\nInputList3SigType273=Serial\nInputList3Cue274=text-o263\nInputList3SigType274=Serial\nInputList3Cue275=text-o264\nInputList3SigType275=Serial\nInputList3Cue276=text-o265\nInputList3SigType276=Serial\nInputList3Cue277=text-o266\nInputList3SigType277=Serial\nInputList3Cue278=text-o267\nInputList3SigType278=Serial\nInputList3Cue279=text-o268\nInputList3SigType279=Serial\nInputList3Cue280=text-o269\nInputList3SigType280=Serial\nInputList3Cue281=text-o270\nInputList3SigType281=Serial\nInputList3Cue282=text-o271\nInputList3SigType282=Serial\nInputList3Cue283=text-o272\nInputList3SigType283=Serial\nInputList3Cue284=text-o273\nInputList3SigType284=Serial\nInputList3Cue285=text-o274\nInputList3SigType285=Serial\nInputList3Cue286=text-o275\nInputList3SigType286=Serial\nInputList3Cue287=text-o276\nInputList3SigType287=Serial\nInputList3Cue288=text-o277\nInputList3SigType288=Serial\nInputList3Cue289=text-o278\nInputList3SigType289=Serial\nInputList3Cue290=text-o279\nInputList3SigType290=Serial\nInputList3Cue291=text-o280\nInputList3SigType291=Serial\nInputList3Cue292=text-o281\nInputList3SigType292=Serial\nInputList3Cue293=text-o282\nInputList3SigType293=Serial\nInputList3Cue294=text-o283\nInputList3SigType294=Serial\nInputList3Cue295=text-o284\nInputList3SigType295=Serial\nInputList3Cue296=text-o285\nInputList3SigType296=Serial\nInputList3Cue297=text-o286\nInputList3SigType297=Serial\nInputList3Cue298=text-o287\nInputList3SigType298=Serial\nInputList3Cue299=text-o288\nInputList3SigType299=Serial\nInputList3Cue300=text-o289\nInputList3SigType300=Serial\nInputList3Cue301=text-o290\nInputList3SigType301=Serial\nInputList3Cue302=text-o291\nInputList3SigType302=Serial\nInputList3Cue303=text-o292\nInputList3SigType303=Serial\nInputList3Cue304=text-o293\nInputList3SigType304=Serial\nInputList3Cue305=text-o294\nInputList3SigType305=Serial\nInputList3Cue306=text-o295\nInputList3SigType306=Serial\nInputList3Cue307=text-o296\nInputList3SigType307=Serial\nInputList3Cue308=text-o297\nInputList3SigType308=Serial\nInputList3Cue309=text-o298\nInputList3SigType309=Serial\nInputList3Cue310=text-o299\nInputList3SigType310=Serial\nInputList3Cue311=text-o300\nInputList3SigType311=Serial\nInputList3Cue312=text-o301\nInputList3SigType312=Serial\nInputList3Cue313=text-o302\nInputList3SigType313=Serial\nInputList3Cue314=text-o303\nInputList3SigType314=Serial\nInputList3Cue315=text-o304\nInputList3SigType315=Serial\nInputList3Cue316=text-o305\nInputList3SigType316=Serial\nInputList3Cue317=text-o306\nInputList3SigType317=Serial\nInputList3Cue318=text-o307\nInputList3SigType318=Serial\nInputList3Cue319=text-o308\nInputList3SigType319=Serial\nInputList3Cue320=text-o309\nInputList3SigType320=Serial\nInputList3Cue321=text-o310\nInputList3SigType321=Serial\nInputList3Cue322=text-o311\nInputList3SigType322=Serial\nInputList3Cue323=text-o312\nInputList3SigType323=Serial\nInputList3Cue324=text-o313\nInputList3SigType324=Serial\nInputList3Cue325=text-o314\nInputList3SigType325=Serial\nInputList3Cue326=text-o315\nInputList3SigType326=Serial\nInputList3Cue327=text-o316\nInputList3SigType327=Serial\nInputList3Cue328=text-o317\nInputList3SigType328=Serial\nInputList3Cue329=text-o318\nInputList3SigType329=Serial\nInputList3Cue330=text-o319\nInputList3SigType330=Serial\nInputList3Cue331=text-o320\nInputList3SigType331=Serial\nInputList3Cue332=text-o321\nInputList3SigType332=Serial\nInputList3Cue333=text-o322\nInputList3SigType333=Serial\nInputList3Cue334=text-o323\nInputList3SigType334=Serial\nInputList3Cue335=text-o324\nInputList3SigType335=Serial\nInputList3Cue336=text-o325\nInputList3SigType336=Serial\nInputList3Cue337=text-o326\nInputList3SigType337=Serial\nInputList3Cue338=text-o327\nInputList3SigType338=Serial\nInputList3Cue339=text-o328\nInputList3SigType339=Serial\nInputList3Cue340=text-o329\nInputList3SigType340=Serial\nInputList3Cue341=text-o330\nInputList3SigType341=Serial\nInputList3Cue342=text-o331\nInputList3SigType342=Serial\nInputList3Cue343=text-o332\nInputList3SigType343=Serial\nInputList3Cue344=text-o333\nInputList3SigType344=Serial\nInputList3Cue345=text-o334\nInputList3SigType345=Serial\nInputList3Cue346=text-o335\nInputList3SigType346=Serial\nInputList3Cue347=text-o336\nInputList3SigType347=Serial\nInputList3Cue348=text-o337\nInputList3SigType348=Serial\nInputList3Cue349=text-o338\nInputList3SigType349=Serial\nInputList3Cue350=text-o339\nInputList3SigType350=Serial\nInputList3Cue351=text-o340\nInputList3SigType351=Serial\nInputList3Cue352=text-o341\nInputList3SigType352=Serial\nInputList3Cue353=text-o342\nInputList3SigType353=Serial\nInputList3Cue354=text-o343\nInputList3SigType354=Serial\nInputList3Cue355=text-o344\nInputList3SigType355=Serial\nInputList3Cue356=text-o345\nInputList3SigType356=Serial\nInputList3Cue357=text-o346\nInputList3SigType357=Serial\nInputList3Cue358=text-o347\nInputList3SigType358=Serial\nInputList3Cue359=text-o348\nInputList3SigType359=Serial\nInputList3Cue360=text-o349\nInputList3SigType360=Serial\nInputList3Cue361=text-o350\nInputList3SigType361=Serial\nInputList3Cue362=text-o351\nInputList3SigType362=Serial\nInputList3Cue363=text-o352\nInputList3SigType363=Serial\nInputList3Cue364=text-o353\nInputList3SigType364=Serial\nInputList3Cue365=text-o354\nInputList3SigType365=Serial\nInputList3Cue366=text-o355\nInputList3SigType366=Serial\nInputList3Cue367=text-o356\nInputList3SigType367=Serial\nInputList3Cue368=text-o357\nInputList3SigType368=Serial\nInputList3Cue369=text-o358\nInputList3SigType369=Serial\nInputList3Cue370=text-o359\nInputList3SigType370=Serial\nInputList3Cue371=text-o360\nInputList3SigType371=Serial\nInputList3Cue372=text-o361\nInputList3SigType372=Serial\nInputList3Cue373=text-o362\nInputList3SigType373=Serial\nInputList3Cue374=text-o363\nInputList3SigType374=Serial\nInputList3Cue375=text-o364\nInputList3SigType375=Serial\nInputList3Cue376=text-o365\nInputList3SigType376=Serial\nInputList3Cue377=text-o366\nInputList3SigType377=Serial\nInputList3Cue378=text-o367\nInputList3SigType378=Serial\nInputList3Cue379=text-o368\nInputList3SigType379=Serial\nInputList3Cue380=text-o369\nInputList3SigType380=Serial\nInputList3Cue381=text-o370\nInputList3SigType381=Serial\nInputList3Cue382=text-o371\nInputList3SigType382=Serial\nInputList3Cue383=text-o372\nInputList3SigType383=Serial\nInputList3Cue384=text-o373\nInputList3SigType384=Serial\nInputList3Cue385=text-o374\nInputList3SigType385=Serial\nInputList3Cue386=text-o375\nInputList3SigType386=Serial\nInputList3Cue387=text-o376\nInputList3SigType387=Serial\nInputList3Cue388=text-o377\nInputList3SigType388=Serial\nInputList3Cue389=text-o378\nInputList3SigType389=Serial\nInputList3Cue390=text-o379\nInputList3SigType390=Serial\nInputList3Cue391=text-o380\nInputList3SigType391=Serial\nInputList3Cue392=text-o381\nInputList3SigType392=Serial\nInputList3Cue393=text-o382\nInputList3SigType393=Serial\nInputList3Cue394=text-o383\nInputList3SigType394=Serial\nInputList3Cue395=text-o384\nInputList3SigType395=Serial\nInputList3Cue396=text-o385\nInputList3SigType396=Serial\nInputList3Cue397=text-o386\nInputList3SigType397=Serial\nInputList3Cue398=text-o387\nInputList3SigType398=Serial\nInputList3Cue399=text-o388\nInputList3SigType399=Serial\nInputList3Cue400=text-o389\nInputList3SigType400=Serial\nInputList3Cue401=text-o390\nInputList3SigType401=Serial\nInputList3Cue402=text-o391\nInputList3SigType402=Serial\nInputList3Cue403=text-o392\nInputList3SigType403=Serial\nInputList3Cue404=text-o393\nInputList3SigType404=Serial\nInputList3Cue405=text-o394\nInputList3SigType405=Serial\nInputList3Cue406=text-o395\nInputList3SigType406=Serial\nInputList3Cue407=text-o396\nInputList3SigType407=Serial\nInputList3Cue408=text-o397\nInputList3SigType408=Serial\nInputList3Cue409=text-o398\nInputList3SigType409=Serial\nInputList3Cue410=text-o399\nInputList3SigType410=Serial\nInputList3Cue411=text-o400\nInputList3SigType411=Serial\nInputList3Cue412=[~EndGroup~]text-o\nInputList3SigType412=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=text-i121\nOutputList3SigType132=Serial\nOutputList3Cue133=text-i122\nOutputList3SigType133=Serial\nOutputList3Cue134=text-i123\nOutputList3SigType134=Serial\nOutputList3Cue135=text-i124\nOutputList3SigType135=Serial\nOutputList3Cue136=text-i125\nOutputList3SigType136=Serial\nOutputList3Cue137=text-i126\nOutputList3SigType137=Serial\nOutputList3Cue138=text-i127\nOutputList3SigType138=Serial\nOutputList3Cue139=text-i128\nOutputList3SigType139=Serial\nOutputList3Cue140=text-i129\nOutputList3SigType140=Serial\nOutputList3Cue141=text-i130\nOutputList3SigType141=Serial\nOutputList3Cue142=text-i131\nOutputList3SigType142=Serial\nOutputList3Cue143=text-i132\nOutputList3SigType143=Serial\nOutputList3Cue144=text-i133\nOutputList3SigType144=Serial\nOutputList3Cue145=text-i134\nOutputList3SigType145=Serial\nOutputList3Cue146=text-i135\nOutputList3SigType146=Serial\nOutputList3Cue147=text-i136\nOutputList3SigType147=Serial\nOutputList3Cue148=text-i137\nOutputList3SigType148=Serial\nOutputList3Cue149=text-i138\nOutputList3SigType149=Serial\nOutputList3Cue150=text-i139\nOutputList3SigType150=Serial\nOutputList3Cue151=text-i140\nOutputList3SigType151=Serial\nOutputList3Cue152=text-i141\nOutputList3SigType152=Serial\nOutputList3Cue153=text-i142\nOutputList3SigType153=Serial\nOutputList3Cue154=text-i143\nOutputList3SigType154=Serial\nOutputList3Cue155=text-i144\nOutputList3SigType155=Serial\nOutputList3Cue156=text-i145\nOutputList3SigType156=Serial\nOutputList3Cue157=text-i146\nOutputList3SigType157=Serial\nOutputList3Cue158=text-i147\nOutputList3SigType158=Serial\nOutputList3Cue159=text-i148\nOutputList3SigType159=Serial\nOutputList3Cue160=text-i149\nOutputList3SigType160=Serial\nOutputList3Cue161=text-i150\nOutputList3SigType161=Serial\nOutputList3Cue162=text-i151\nOutputList3SigType162=Serial\nOutputList3Cue163=text-i152\nOutputList3SigType163=Serial\nOutputList3Cue164=text-i153\nOutputList3SigType164=Serial\nOutputList3Cue165=text-i154\nOutputList3SigType165=Serial\nOutputList3Cue166=text-i155\nOutputList3SigType166=Serial\nOutputList3Cue167=text-i156\nOutputList3SigType167=Serial\nOutputList3Cue168=text-i157\nOutputList3SigType168=Serial\nOutputList3Cue169=text-i158\nOutputList3SigType169=Serial\nOutputList3Cue170=text-i159\nOutputList3SigType170=Serial\nOutputList3Cue171=text-i160\nOutputList3SigType171=Serial\nOutputList3Cue172=text-i161\nOutputList3SigType172=Serial\nOutputList3Cue173=text-i162\nOutputList3SigType173=Serial\nOutputList3Cue174=text-i163\nOutputList3SigType174=Serial\nOutputList3Cue175=text-i164\nOutputList3SigType175=Serial\nOutputList3Cue176=text-i165\nOutputList3SigType176=Serial\nOutputList3Cue177=text-i166\nOutputList3SigType177=Serial\nOutputList3Cue178=text-i167\nOutputList3SigType178=Serial\nOutputList3Cue179=text-i168\nOutputList3SigType179=Serial\nOutputList3Cue180=text-i169\nOutputList3SigType180=Serial\nOutputList3Cue181=text-i170\nOutputList3SigType181=Serial\nOutputList3Cue182=text-i171\nOutputList3SigType182=Serial\nOutputList3Cue183=text-i172\nOutputList3SigType183=Serial\nOutputList3Cue184=text-i173\nOutputList3SigType184=Serial\nOutputList3Cue185=text-i174\nOutputList3SigType185=Serial\nOutputList3Cue186=text-i175\nOutputList3SigType186=Serial\nOutputList3Cue187=text-i176\nOutputList3SigType187=Serial\nOutputList3Cue188=text-i177\nOutputList3SigType188=Serial\nOutputList3Cue189=text-i178\nOutputList3SigType189=Serial\nOutputList3Cue190=text-i179\nOutputList3SigType190=Serial\nOutputList3Cue191=text-i180\nOutputList3SigType191=Serial\nOutputList3Cue192=text-i181\nOutputList3SigType192=Serial\nOutputList3Cue193=text-i182\nOutputList3SigType193=Serial\nOutputList3Cue194=text-i183\nOutputList3SigType194=Serial\nOutputList3Cue195=text-i184\nOutputList3SigType195=Serial\nOutputList3Cue196=text-i185\nOutputList3SigType196=Serial\nOutputList3Cue197=text-i186\nOutputList3SigType197=Serial\nOutputList3Cue198=text-i187\nOutputList3SigType198=Serial\nOutputList3Cue199=text-i188\nOutputList3SigType199=Serial\nOutputList3Cue200=text-i189\nOutputList3SigType200=Serial\nOutputList3Cue201=text-i190\nOutputList3SigType201=Serial\nOutputList3Cue202=text-i191\nOutputList3SigType202=Serial\nOutputList3Cue203=text-i192\nOutputList3SigType203=Serial\nOutputList3Cue204=text-i193\nOutputList3SigType204=Serial\nOutputList3Cue205=text-i194\nOutputList3SigType205=Serial\nOutputList3Cue206=text-i195\nOutputList3SigType206=Serial\nOutputList3Cue207=text-i196\nOutputList3SigType207=Serial\nOutputList3Cue208=text-i197\nOutputList3SigType208=Serial\nOutputList3Cue209=text-i198\nOutputList3SigType209=Serial\nOutputList3Cue210=text-i199\nOutputList3SigType210=Serial\nOutputList3Cue211=text-i200\nOutputList3SigType211=Serial\nOutputList3Cue212=text-i201\nOutputList3SigType212=Serial\nOutputList3Cue213=text-i202\nOutputList3SigType213=Serial\nOutputList3Cue214=text-i203\nOutputList3SigType214=Serial\nOutputList3Cue215=text-i204\nOutputList3SigType215=Serial\nOutputList3Cue216=text-i205\nOutputList3SigType216=Serial\nOutputList3Cue217=text-i206\nOutputList3SigType217=Serial\nOutputList3Cue218=text-i207\nOutputList3SigType218=Serial\nOutputList3Cue219=text-i208\nOutputList3SigType219=Serial\nOutputList3Cue220=text-i209\nOutputList3SigType220=Serial\nOutputList3Cue221=text-i210\nOutputList3SigType221=Serial\nOutputList3Cue222=text-i211\nOutputList3SigType222=Serial\nOutputList3Cue223=text-i212\nOutputList3SigType223=Serial\nOutputList3Cue224=text-i213\nOutputList3SigType224=Serial\nOutputList3Cue225=text-i214\nOutputList3SigType225=Serial\nOutputList3Cue226=text-i215\nOutputList3SigType226=Serial\nOutputList3Cue227=text-i216\nOutputList3SigType227=Serial\nOutputList3Cue228=text-i217\nOutputList3SigType228=Serial\nOutputList3Cue229=text-i218\nOutputList3SigType229=Serial\nOutputList3Cue230=text-i219\nOutputList3SigType230=Serial\nOutputList3Cue231=text-i220\nOutputList3SigType231=Serial\nOutputList3Cue232=text-i221\nOutputList3SigType232=Serial\nOutputList3Cue233=text-i222\nOutputList3SigType233=Serial\nOutputList3Cue234=text-i223\nOutputList3SigType234=Serial\nOutputList3Cue235=text-i224\nOutputList3SigType235=Serial\nOutputList3Cue236=text-i225\nOutputList3SigType236=Serial\nOutputList3Cue237=text-i226\nOutputList3SigType237=Serial\nOutputList3Cue238=text-i227\nOutputList3SigType238=Serial\nOutputList3Cue239=text-i228\nOutputList3SigType239=Serial\nOutputList3Cue240=text-i229\nOutputList3SigType240=Serial\nOutputList3Cue241=text-i230\nOutputList3SigType241=Serial\nOutputList3Cue242=text-i231\nOutputList3SigType242=Serial\nOutputList3Cue243=text-i232\nOutputList3SigType243=Serial\nOutputList3Cue244=text-i233\nOutputList3SigType244=Serial\nOutputList3Cue245=text-i234\nOutputList3SigType245=Serial\nOutputList3Cue246=text-i235\nOutputList3SigType246=Serial\nOutputList3Cue247=text-i236\nOutputList3SigType247=Serial\nOutputList3Cue248=text-i237\nOutputList3SigType248=Serial\nOutputList3Cue249=text-i238\nOutputList3SigType249=Serial\nOutputList3Cue250=text-i239\nOutputList3SigType250=Serial\nOutputList3Cue251=text-i240\nOutputList3SigType251=Serial\nOutputList3Cue252=text-i241\nOutputList3SigType252=Serial\nOutputList3Cue253=text-i242\nOutputList3SigType253=Serial\nOutputList3Cue254=text-i243\nOutputList3SigType254=Serial\nOutputList3Cue255=text-i244\nOutputList3SigType255=Serial\nOutputList3Cue256=text-i245\nOutputList3SigType256=Serial\nOutputList3Cue257=text-i246\nOutputList3SigType257=Serial\nOutputList3Cue258=text-i247\nOutputList3SigType258=Serial\nOutputList3Cue259=text-i248\nOutputList3SigType259=Serial\nOutputList3Cue260=text-i249\nOutputList3SigType260=Serial\nOutputList3Cue261=text-i250\nOutputList3SigType261=Serial\nOutputList3Cue262=text-i251\nOutputList3SigType262=Serial\nOutputList3Cue263=text-i252\nOutputList3SigType263=Serial\nOutputList3Cue264=text-i253\nOutputList3SigType264=Serial\nOutputList3Cue265=text-i254\nOutputList3SigType265=Serial\nOutputList3Cue266=text-i255\nOutputList3SigType266=Serial\nOutputList3Cue267=text-i256\nOutputList3SigType267=Serial\nOutputList3Cue268=text-i257\nOutputList3SigType268=Serial\nOutputList3Cue269=text-i258\nOutputList3SigType269=Serial\nOutputList3Cue270=text-i259\nOutputList3SigType270=Serial\nOutputList3Cue271=text-i260\nOutputList3SigType271=Serial\nOutputList3Cue272=text-i261\nOutputList3SigType272=Serial\nOutputList3Cue273=text-i262\nOutputList3SigType273=Serial\nOutputList3Cue274=text-i263\nOutputList3SigType274=Serial\nOutputList3Cue275=text-i264\nOutputList3SigType275=Serial\nOutputList3Cue276=text-i265\nOutputList3SigType276=Serial\nOutputList3Cue277=text-i266\nOutputList3SigType277=Serial\nOutputList3Cue278=text-i267\nOutputList3SigType278=Serial\nOutputList3Cue279=text-i268\nOutputList3SigType279=Serial\nOutputList3Cue280=text-i269\nOutputList3SigType280=Serial\nOutputList3Cue281=text-i270\nOutputList3SigType281=Serial\nOutputList3Cue282=text-i271\nOutputList3SigType282=Serial\nOutputList3Cue283=text-i272\nOutputList3SigType283=Serial\nOutputList3Cue284=text-i273\nOutputList3SigType284=Serial\nOutputList3Cue285=text-i274\nOutputList3SigType285=Serial\nOutputList3Cue286=text-i275\nOutputList3SigType286=Serial\nOutputList3Cue287=text-i276\nOutputList3SigType287=Serial\nOutputList3Cue288=text-i277\nOutputList3SigType288=Serial\nOutputList3Cue289=text-i278\nOutputList3SigType289=Serial\nOutputList3Cue290=text-i279\nOutputList3SigType290=Serial\nOutputList3Cue291=text-i280\nOutputList3SigType291=Serial\nOutputList3Cue292=text-i281\nOutputList3SigType292=Serial\nOutputList3Cue293=text-i282\nOutputList3SigType293=Serial\nOutputList3Cue294=text-i283\nOutputList3SigType294=Serial\nOutputList3Cue295=text-i284\nOutputList3SigType295=Serial\nOutputList3Cue296=text-i285\nOutputList3SigType296=Serial\nOutputList3Cue297=text-i286\nOutputList3SigType297=Serial\nOutputList3Cue298=text-i287\nOutputList3SigType298=Serial\nOutputList3Cue299=text-i288\nOutputList3SigType299=Serial\nOutputList3Cue300=text-i289\nOutputList3SigType300=Serial\nOutputList3Cue301=text-i290\nOutputList3SigType301=Serial\nOutputList3Cue302=text-i291\nOutputList3SigType302=Serial\nOutputList3Cue303=text-i292\nOutputList3SigType303=Serial\nOutputList3Cue304=text-i293\nOutputList3SigType304=Serial\nOutputList3Cue305=text-i294\nOutputList3SigType305=Serial\nOutputList3Cue306=text-i295\nOutputList3SigType306=Serial\nOutputList3Cue307=text-i296\nOutputList3SigType307=Serial\nOutputList3Cue308=text-i297\nOutputList3SigType308=Serial\nOutputList3Cue309=text-i298\nOutputList3SigType309=Serial\nOutputList3Cue310=text-i299\nOutputList3SigType310=Serial\nOutputList3Cue311=text-i300\nOutputList3SigType311=Serial\nOutputList3Cue312=text-i301\nOutputList3SigType312=Serial\nOutputList3Cue313=text-i302\nOutputList3SigType313=Serial\nOutputList3Cue314=text-i303\nOutputList3SigType314=Serial\nOutputList3Cue315=text-i304\nOutputList3SigType315=Serial\nOutputList3Cue316=text-i305\nOutputList3SigType316=Serial\nOutputList3Cue317=text-i306\nOutputList3SigType317=Serial\nOutputList3Cue318=text-i307\nOutputList3SigType318=Serial\nOutputList3Cue319=text-i308\nOutputList3SigType319=Serial\nOutputList3Cue320=text-i309\nOutputList3SigType320=Serial\nOutputList3Cue321=text-i310\nOutputList3SigType321=Serial\nOutputList3Cue322=text-i311\nOutputList3SigType322=Serial\nOutputList3Cue323=text-i312\nOutputList3SigType323=Serial\nOutputList3Cue324=text-i313\nOutputList3SigType324=Serial\nOutputList3Cue325=text-i314\nOutputList3SigType325=Serial\nOutputList3Cue326=text-i315\nOutputList3SigType326=Serial\nOutputList3Cue327=text-i316\nOutputList3SigType327=Serial\nOutputList3Cue328=text-i317\nOutputList3SigType328=Serial\nOutputList3Cue329=text-i318\nOutputList3SigType329=Serial\nOutputList3Cue330=text-i319\nOutputList3SigType330=Serial\nOutputList3Cue331=text-i320\nOutputList3SigType331=Serial\nOutputList3Cue332=text-i321\nOutputList3SigType332=Serial\nOutputList3Cue333=text-i322\nOutputList3SigType333=Serial\nOutputList3Cue334=text-i323\nOutputList3SigType334=Serial\nOutputList3Cue335=text-i324\nOutputList3SigType335=Serial\nOutputList3Cue336=text-i325\nOutputList3SigType336=Serial\nOutputList3Cue337=text-i326\nOutputList3SigType337=Serial\nOutputList3Cue338=text-i327\nOutputList3SigType338=Serial\nOutputList3Cue339=text-i328\nOutputList3SigType339=Serial\nOutputList3Cue340=text-i329\nOutputList3SigType340=Serial\nOutputList3Cue341=text-i330\nOutputList3SigType341=Serial\nOutputList3Cue342=text-i331\nOutputList3SigType342=Serial\nOutputList3Cue343=text-i332\nOutputList3SigType343=Serial\nOutputList3Cue344=text-i333\nOutputList3SigType344=Serial\nOutputList3Cue345=text-i334\nOutputList3SigType345=Serial\nOutputList3Cue346=text-i335\nOutputList3SigType346=Serial\nOutputList3Cue347=text-i336\nOutputList3SigType347=Serial\nOutputList3Cue348=text-i337\nOutputList3SigType348=Serial\nOutputList3Cue349=text-i338\nOutputList3SigType349=Serial\nOutputList3Cue350=text-i339\nOutputList3SigType350=Serial\nOutputList3Cue351=text-i340\nOutputList3SigType351=Serial\nOutputList3Cue352=text-i341\nOutputList3SigType352=Serial\nOutputList3Cue353=text-i342\nOutputList3SigType353=Serial\nOutputList3Cue354=text-i343\nOutputList3SigType354=Serial\nOutputList3Cue355=text-i344\nOutputList3SigType355=Serial\nOutputList3Cue356=text-i345\nOutputList3SigType356=Serial\nOutputList3Cue357=text-i346\nOutputList3SigType357=Serial\nOutputList3Cue358=text-i347\nOutputList3SigType358=Serial\nOutputList3Cue359=text-i348\nOutputList3SigType359=Serial\nOutputList3Cue360=text-i349\nOutputList3SigType360=Serial\nOutputList3Cue361=text-i350\nOutputList3SigType361=Serial\nOutputList3Cue362=text-i351\nOutputList3SigType362=Serial\nOutputList3Cue363=text-i352\nOutputList3SigType363=Serial\nOutputList3Cue364=text-i353\nOutputList3SigType364=Serial\nOutputList3Cue365=text-i354\nOutputList3SigType365=Serial\nOutputList3Cue366=text-i355\nOutputList3SigType366=Serial\nOutputList3Cue367=text-i356\nOutputList3SigType367=Serial\nOutputList3Cue368=text-i357\nOutputList3SigType368=Serial\nOutputList3Cue369=text-i358\nOutputList3SigType369=Serial\nOutputList3Cue370=text-i359\nOutputList3SigType370=Serial\nOutputList3Cue371=text-i360\nOutputList3SigType371=Serial\nOutputList3Cue372=text-i361\nOutputList3SigType372=Serial\nOutputList3Cue373=text-i362\nOutputList3SigType373=Serial\nOutputList3Cue374=text-i363\nOutputList3SigType374=Serial\nOutputList3Cue375=text-i364\nOutputList3SigType375=Serial\nOutputList3Cue376=text-i365\nOutputList3SigType376=Serial\nOutputList3Cue377=text-i366\nOutputList3SigType377=Serial\nOutputList3Cue378=text-i367\nOutputList3SigType378=Serial\nOutputList3Cue379=text-i368\nOutputList3SigType379=Serial\nOutputList3Cue380=text-i369\nOutputList3SigType380=Serial\nOutputList3Cue381=text-i370\nOutputList3SigType381=Serial\nOutputList3Cue382=text-i371\nOutputList3SigType382=Serial\nOutputList3Cue383=text-i372\nOutputList3SigType383=Serial\nOutputList3Cue384=text-i373\nOutputList3SigType384=Serial\nOutputList3Cue385=text-i374\nOutputList3SigType385=Serial\nOutputList3Cue386=text-i375\nOutputList3SigType386=Serial\nOutputList3Cue387=text-i376\nOutputList3SigType387=Serial\nOutputList3Cue388=text-i377\nOutputList3SigType388=Serial\nOutputList3Cue389=text-i378\nOutputList3SigType389=Serial\nOutputList3Cue390=text-i379\nOutputList3SigType390=Serial\nOutputList3Cue391=text-i380\nOutputList3SigType391=Serial\nOutputList3Cue392=text-i381\nOutputList3SigType392=Serial\nOutputList3Cue393=text-i382\nOutputList3SigType393=Serial\nOutputList3Cue394=text-i383\nOutputList3SigType394=Serial\nOutputList3Cue395=text-i384\nOutputList3SigType395=Serial\nOutputList3Cue396=text-i385\nOutputList3SigType396=Serial\nOutputList3Cue397=text-i386\nOutputList3SigType397=Serial\nOutputList3Cue398=text-i387\nOutputList3SigType398=Serial\nOutputList3Cue399=text-i388\nOutputList3SigType399=Serial\nOutputList3Cue400=text-i389\nOutputList3SigType400=Serial\nOutputList3Cue401=text-i390\nOutputList3SigType401=Serial\nOutputList3Cue402=text-i391\nOutputList3SigType402=Serial\nOutputList3Cue403=text-i392\nOutputList3SigType403=Serial\nOutputList3Cue404=text-i393\nOutputList3SigType404=Serial\nOutputList3Cue405=text-i394\nOutputList3SigType405=Serial\nOutputList3Cue406=text-i395\nOutputList3SigType406=Serial\nOutputList3Cue407=text-i396\nOutputList3SigType407=Serial\nOutputList3Cue408=text-i397\nOutputList3SigType408=Serial\nOutputList3Cue409=text-i398\nOutputList3SigType409=Serial\nOutputList3Cue410=text-i399\nOutputList3SigType410=Serial\nOutputList3Cue411=text-i400\nOutputList3SigType411=Serial\nOutputList3Cue412=[~EndGroup~]text-i\nOutputList3SigType412=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=10\nCedH=10\nSmartObjId=10012d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=10\nTp=1\nHD=TRUE\nDV=10012d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=10\nName=PepperDash Essentials TSW-760_[B.AV] Object - Presets_CATV Icon List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[B.AV] Object - DPad STB_STB DPad.ced\nHint=STB DPad (Smart Object ID=10011)\nCode=11\nSGControlType=DPad\nSGControlName=STB DPad\nGUID=F2340FAD-85E9-4E49-B46E-15E9B096F58B\nSmplCName=PepperDash Essentials TSW-760_[B.AV] Object - DPad STB_STB DPad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10015\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=5\nMaxVariableInputs=5\nMinVariableOutputs=5\nMaxVariableOutputs=5\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Up\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=Down\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Left\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=Right\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=OK\nOutputSigType5=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=11\nCedH=11\nSmartObjId=10011d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=11\nTp=1\nHD=TRUE\nDV=10011d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=11\nName=PepperDash Essentials TSW-760_[B.AV] Object - DPad STB_STB DPad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\nHint=Tab Button Horizontal (Smart Object ID=10081)\nCode=12\nSGControlType=Horizontal Tab Button\nSGControlName=Tab Button Horizontal\nGUID=198C97B0-9D0C-46C2-9557-27EA33C0BE9F\nSmplCName=PepperDash Essentials TSW-760_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10031\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4\nMaxVariableInputs=4\nMinVariableOutputs=4\nMaxVariableOutputs=4\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Tab Button 1 Press\nOutputSigType1=Digital\nInputCue2=Tab Button 1 Select\nInputSigType2=Digital\nOutputCue2=[~UNUSED3~]\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Tab Button 2 Press\nOutputSigType3=Digital\nInputCue4=Tab Button 2 Select\nInputSigType4=Digital\nOutputCue4=[~UNUSED3~]\nOutputSigType4=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=12\nCedH=12\nSmartObjId=10081d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=12\nTp=1\nHD=TRUE\nDV=10081d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=12\nName=PepperDash Essentials TSW-760_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[B.AV] Object - Dpad DVD_DVD DPad.ced\nHint=DVD DPad (Smart Object ID=10411)\nCode=13\nSGControlType=DPad\nSGControlName=DVD DPad\nGUID=D49BCF22-C770-4146-93E6-00867D885E1E\nSmplCName=PepperDash Essentials TSW-760_[B.AV] Object - Dpad DVD_DVD DPad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10015\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=5\nMaxVariableInputs=5\nMinVariableOutputs=5\nMaxVariableOutputs=5\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Up\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=Down\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Left\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=Right\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=OK\nOutputSigType5=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=13\nCedH=13\nSmartObjId=10411d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=13\nTp=1\nHD=TRUE\nDV=10411d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=13\nName=PepperDash Essentials TSW-760_[B.AV] Object - Dpad DVD_DVD DPad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\nHint=CATV Keypad (Smart Object ID=10014)\nCode=14\nSGControlType=Simple Keypad\nSGControlName=CATV Keypad\nGUID=5E33D4F4-5C5F-430C-879A-AE82529B3943\nSmplCName=PepperDash Essentials TSW-760_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=Misc_1\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=Misc_2\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=14\nCedH=14\nSmartObjId=10014d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=14\nTp=1\nHD=TRUE\nDV=10014d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=14\nName=PepperDash Essentials TSW-760_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\nHint=CATV Icon List Vertical (Smart Object ID=3333)\nCode=15\nSGControlType=Subpage Reference List Vertical\nSGControlName=CATV Icon List Vertical\nGUID=D6A79678-BAD2-4F6D-8104-A941FBA9432F\nSmplCName=PepperDash Essentials TSW-760_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4106\nMaxVariableInputs=4106\nMinVariableOutputs=4106\nMaxVariableOutputs=4106\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=132\nMaxVariableInputsList3=132\nMinVariableOutputsList3=132\nMaxVariableOutputsList3=132\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=[~EndGroup~]fb\nInputSigType4106=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=[~EndGroup~]Press\nOutputSigType4106=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=[~EndGroup~]text-o\nInputList3SigType132=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=[~EndGroup~]text-i\nOutputList3SigType132=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=15\nCedH=15\nSmartObjId=3333d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=15\nTp=1\nHD=TRUE\nDV=3333d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=15\nName=PepperDash Essentials TSW-760_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\nHint=Subpage Reference List Vertical_2 (Smart Object ID=1001)\nCode=16\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_2\nGUID=5F63F5C2-A987-4957-9243-C28805CE297E\nSmplCName=PepperDash Essentials TSW-760_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4036\nMaxVariableInputs=4036\nMinVariableOutputs=4036\nMaxVariableOutputs=4036\nNumFixedParams=1\nMinVariableInputsList2=32\nMaxVariableInputsList2=32\nMinVariableOutputsList2=32\nMaxVariableOutputsList2=32\nMinVariableInputsList3=32\nMaxVariableInputsList3=32\nMinVariableOutputsList3=32\nMaxVariableOutputsList3=32\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=[~UNUSED2~]\nInputSigType16=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=[~UNUSED2~]\nInputSigType2018=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=[~EndGroup~]fb\nInputSigType4036=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED2~]\nOutputSigType16=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED2~]\nOutputSigType2018=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=[~EndGroup~]Press\nOutputSigType4036=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=[~EndGroup~]an_fb\nInputList2SigType32=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=[~EndGroup~]an_act\nOutputList2SigType32=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=[~EndGroup~]text-o\nInputList3SigType32=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=[~EndGroup~]text-i\nOutputList3SigType32=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=16\nCedH=16\nSmartObjId=1001d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=16\nTp=1\nHD=TRUE\nDV=1001d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=16\nName=PepperDash Essentials TSW-760_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\nHint=Dynamic Button List Vertical (Smart Object ID=3901)\nCode=17\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical\nGUID=15B95903-FB30-472E-9D10-82A9C3A9D7C7\nSmplCName=PepperDash Essentials TSW-760_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=[~UNUSED2~]\nInputSigType22=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=[~UNUSED2~]\nInputSigType2024=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=[~UNUSED2~]\nInputSigType4026=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=[~UNUSED2~]\nOutputSigType22=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED2~]\nOutputSigType2024=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED2~]\nOutputSigType4026=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=[~UNUSED2~]\nInputList2SigType22=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED2~]\nOutputList2SigType22=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=[~UNUSED2~]\nInputList3SigType22=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=[~UNUSED2~]\nInputList3SigType2024=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED2~]\nOutputList3SigType22=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED2~]\nOutputList3SigType2024=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=17\nCedH=17\nSmartObjId=3901d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=17\nTp=1\nHD=TRUE\nDV=3901d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=17\nName=PepperDash Essentials TSW-760_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\nHint=Subpage Reference List Vertical (Smart Object ID=15023)\nCode=18\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical\nGUID=EC6E6B35-F033-4F14-9465-FEEBE20455CC\nSmplCName=PepperDash Essentials TSW-760_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4052\nMaxVariableInputs=4052\nMinVariableOutputs=4052\nMaxVariableOutputs=4052\nNumFixedParams=1\nMinVariableInputsList2=48\nMaxVariableInputsList2=48\nMinVariableOutputsList2=48\nMaxVariableOutputsList2=48\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=[~UNUSED2~]\nInputSigType24=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=[~UNUSED2~]\nInputSigType2026=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=[~EndGroup~]fb\nInputSigType4052=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED2~]\nOutputSigType24=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED2~]\nOutputSigType2026=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=[~EndGroup~]Press\nOutputSigType4052=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=[~EndGroup~]an_fb\nInputList2SigType48=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=[~EndGroup~]an_act\nOutputList2SigType48=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=18\nCedH=18\nSmartObjId=15023d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=18\nTp=1\nHD=TRUE\nDV=15023d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=18\nName=PepperDash Essentials TSW-760_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials TSW-760_[D.VC] Recents_Dynamic Button List Vertical_2.ced\nHint=Dynamic Button List Vertical_2 (Smart Object ID=1203)\nCode=19\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical_2\nGUID=2C4B55E4-42AC-486D-BC6E-C7DD7D24C974\nSmplCName=PepperDash Essentials TSW-760_[D.VC] Recents_Dynamic Button List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=Item 11 Selected\nInputSigType22=Digital\nInputCue23=Item 12 Selected\nInputSigType23=Digital\nInputCue24=Item 13 Selected\nInputSigType24=Digital\nInputCue25=Item 14 Selected\nInputSigType25=Digital\nInputCue26=Item 15 Selected\nInputSigType26=Digital\nInputCue27=Item 16 Selected\nInputSigType27=Digital\nInputCue28=Item 17 Selected\nInputSigType28=Digital\nInputCue29=Item 18 Selected\nInputSigType29=Digital\nInputCue30=Item 19 Selected\nInputSigType30=Digital\nInputCue31=Item 20 Selected\nInputSigType31=Digital\nInputCue32=Item 21 Selected\nInputSigType32=Digital\nInputCue33=Item 22 Selected\nInputSigType33=Digital\nInputCue34=Item 23 Selected\nInputSigType34=Digital\nInputCue35=Item 24 Selected\nInputSigType35=Digital\nInputCue36=Item 25 Selected\nInputSigType36=Digital\nInputCue37=Item 26 Selected\nInputSigType37=Digital\nInputCue38=Item 27 Selected\nInputSigType38=Digital\nInputCue39=Item 28 Selected\nInputSigType39=Digital\nInputCue40=Item 29 Selected\nInputSigType40=Digital\nInputCue41=Item 30 Selected\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=Item 11 Enabled\nInputSigType2024=Digital\nInputCue2025=Item 12 Enabled\nInputSigType2025=Digital\nInputCue2026=Item 13 Enabled\nInputSigType2026=Digital\nInputCue2027=Item 14 Enabled\nInputSigType2027=Digital\nInputCue2028=Item 15 Enabled\nInputSigType2028=Digital\nInputCue2029=Item 16 Enabled\nInputSigType2029=Digital\nInputCue2030=Item 17 Enabled\nInputSigType2030=Digital\nInputCue2031=Item 18 Enabled\nInputSigType2031=Digital\nInputCue2032=Item 19 Enabled\nInputSigType2032=Digital\nInputCue2033=Item 20 Enabled\nInputSigType2033=Digital\nInputCue2034=Item 21 Enabled\nInputSigType2034=Digital\nInputCue2035=Item 22 Enabled\nInputSigType2035=Digital\nInputCue2036=Item 23 Enabled\nInputSigType2036=Digital\nInputCue2037=Item 24 Enabled\nInputSigType2037=Digital\nInputCue2038=Item 25 Enabled\nInputSigType2038=Digital\nInputCue2039=Item 26 Enabled\nInputSigType2039=Digital\nInputCue2040=Item 27 Enabled\nInputSigType2040=Digital\nInputCue2041=Item 28 Enabled\nInputSigType2041=Digital\nInputCue2042=Item 29 Enabled\nInputSigType2042=Digital\nInputCue2043=Item 30 Enabled\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=Item 11 Visible\nInputSigType4026=Digital\nInputCue4027=Item 12 Visible\nInputSigType4027=Digital\nInputCue4028=Item 13 Visible\nInputSigType4028=Digital\nInputCue4029=Item 14 Visible\nInputSigType4029=Digital\nInputCue4030=Item 15 Visible\nInputSigType4030=Digital\nInputCue4031=Item 16 Visible\nInputSigType4031=Digital\nInputCue4032=Item 17 Visible\nInputSigType4032=Digital\nInputCue4033=Item 18 Visible\nInputSigType4033=Digital\nInputCue4034=Item 19 Visible\nInputSigType4034=Digital\nInputCue4035=Item 20 Visible\nInputSigType4035=Digital\nInputCue4036=Item 21 Visible\nInputSigType4036=Digital\nInputCue4037=Item 22 Visible\nInputSigType4037=Digital\nInputCue4038=Item 23 Visible\nInputSigType4038=Digital\nInputCue4039=Item 24 Visible\nInputSigType4039=Digital\nInputCue4040=Item 25 Visible\nInputSigType4040=Digital\nInputCue4041=Item 26 Visible\nInputSigType4041=Digital\nInputCue4042=Item 27 Visible\nInputSigType4042=Digital\nInputCue4043=Item 28 Visible\nInputSigType4043=Digital\nInputCue4044=Item 29 Visible\nInputSigType4044=Digital\nInputCue4045=Item 30 Visible\nInputSigType4045=Digital\nInputCue4046=[~UNUSED2~]\nInputSigType4046=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=Item 11 Pressed\nOutputSigType22=Digital\nOutputCue23=Item 12 Pressed\nOutputSigType23=Digital\nOutputCue24=Item 13 Pressed\nOutputSigType24=Digital\nOutputCue25=Item 14 Pressed\nOutputSigType25=Digital\nOutputCue26=Item 15 Pressed\nOutputSigType26=Digital\nOutputCue27=Item 16 Pressed\nOutputSigType27=Digital\nOutputCue28=Item 17 Pressed\nOutputSigType28=Digital\nOutputCue29=Item 18 Pressed\nOutputSigType29=Digital\nOutputCue30=Item 19 Pressed\nOutputSigType30=Digital\nOutputCue31=Item 20 Pressed\nOutputSigType31=Digital\nOutputCue32=Item 21 Pressed\nOutputSigType32=Digital\nOutputCue33=Item 22 Pressed\nOutputSigType33=Digital\nOutputCue34=Item 23 Pressed\nOutputSigType34=Digital\nOutputCue35=Item 24 Pressed\nOutputSigType35=Digital\nOutputCue36=Item 25 Pressed\nOutputSigType36=Digital\nOutputCue37=Item 26 Pressed\nOutputSigType37=Digital\nOutputCue38=Item 27 Pressed\nOutputSigType38=Digital\nOutputCue39=Item 28 Pressed\nOutputSigType39=Digital\nOutputCue40=Item 29 Pressed\nOutputSigType40=Digital\nOutputCue41=Item 30 Pressed\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED3~]\nOutputSigType4026=Digital\nOutputCue4027=[~UNUSED3~]\nOutputSigType4027=Digital\nOutputCue4028=[~UNUSED3~]\nOutputSigType4028=Digital\nOutputCue4029=[~UNUSED3~]\nOutputSigType4029=Digital\nOutputCue4030=[~UNUSED3~]\nOutputSigType4030=Digital\nOutputCue4031=[~UNUSED3~]\nOutputSigType4031=Digital\nOutputCue4032=[~UNUSED3~]\nOutputSigType4032=Digital\nOutputCue4033=[~UNUSED3~]\nOutputSigType4033=Digital\nOutputCue4034=[~UNUSED3~]\nOutputSigType4034=Digital\nOutputCue4035=[~UNUSED3~]\nOutputSigType4035=Digital\nOutputCue4036=[~UNUSED3~]\nOutputSigType4036=Digital\nOutputCue4037=[~UNUSED3~]\nOutputSigType4037=Digital\nOutputCue4038=[~UNUSED3~]\nOutputSigType4038=Digital\nOutputCue4039=[~UNUSED3~]\nOutputSigType4039=Digital\nOutputCue4040=[~UNUSED3~]\nOutputSigType4040=Digital\nOutputCue4041=[~UNUSED3~]\nOutputSigType4041=Digital\nOutputCue4042=[~UNUSED3~]\nOutputSigType4042=Digital\nOutputCue4043=[~UNUSED3~]\nOutputSigType4043=Digital\nOutputCue4044=[~UNUSED3~]\nOutputSigType4044=Digital\nOutputCue4045=[~UNUSED3~]\nOutputSigType4045=Digital\nOutputCue4046=[~UNUSED2~]\nOutputSigType4046=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=Set Item 11 Icon Analog\nInputList2SigType22=Analog\nInputList2Cue23=Set Item 12 Icon Analog\nInputList2SigType23=Analog\nInputList2Cue24=Set Item 13 Icon Analog\nInputList2SigType24=Analog\nInputList2Cue25=Set Item 14 Icon Analog\nInputList2SigType25=Analog\nInputList2Cue26=Set Item 15 Icon Analog\nInputList2SigType26=Analog\nInputList2Cue27=Set Item 16 Icon Analog\nInputList2SigType27=Analog\nInputList2Cue28=Set Item 17 Icon Analog\nInputList2SigType28=Analog\nInputList2Cue29=Set Item 18 Icon Analog\nInputList2SigType29=Analog\nInputList2Cue30=Set Item 19 Icon Analog\nInputList2SigType30=Analog\nInputList2Cue31=Set Item 20 Icon Analog\nInputList2SigType31=Analog\nInputList2Cue32=Set Item 21 Icon Analog\nInputList2SigType32=Analog\nInputList2Cue33=Set Item 22 Icon Analog\nInputList2SigType33=Analog\nInputList2Cue34=Set Item 23 Icon Analog\nInputList2SigType34=Analog\nInputList2Cue35=Set Item 24 Icon Analog\nInputList2SigType35=Analog\nInputList2Cue36=Set Item 25 Icon Analog\nInputList2SigType36=Analog\nInputList2Cue37=Set Item 26 Icon Analog\nInputList2SigType37=Analog\nInputList2Cue38=Set Item 27 Icon Analog\nInputList2SigType38=Analog\nInputList2Cue39=Set Item 28 Icon Analog\nInputList2SigType39=Analog\nInputList2Cue40=Set Item 29 Icon Analog\nInputList2SigType40=Analog\nInputList2Cue41=Set Item 30 Icon Analog\nInputList2SigType41=Analog\nInputList2Cue42=[~UNUSED2~]\nInputList2SigType42=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED3~]\nOutputList2SigType22=Analog\nOutputList2Cue23=[~UNUSED3~]\nOutputList2SigType23=Analog\nOutputList2Cue24=[~UNUSED3~]\nOutputList2SigType24=Analog\nOutputList2Cue25=[~UNUSED3~]\nOutputList2SigType25=Analog\nOutputList2Cue26=[~UNUSED3~]\nOutputList2SigType26=Analog\nOutputList2Cue27=[~UNUSED3~]\nOutputList2SigType27=Analog\nOutputList2Cue28=[~UNUSED3~]\nOutputList2SigType28=Analog\nOutputList2Cue29=[~UNUSED3~]\nOutputList2SigType29=Analog\nOutputList2Cue30=[~UNUSED3~]\nOutputList2SigType30=Analog\nOutputList2Cue31=[~UNUSED3~]\nOutputList2SigType31=Analog\nOutputList2Cue32=[~UNUSED3~]\nOutputList2SigType32=Analog\nOutputList2Cue33=[~UNUSED3~]\nOutputList2SigType33=Analog\nOutputList2Cue34=[~UNUSED3~]\nOutputList2SigType34=Analog\nOutputList2Cue35=[~UNUSED3~]\nOutputList2SigType35=Analog\nOutputList2Cue36=[~UNUSED3~]\nOutputList2SigType36=Analog\nOutputList2Cue37=[~UNUSED3~]\nOutputList2SigType37=Analog\nOutputList2Cue38=[~UNUSED3~]\nOutputList2SigType38=Analog\nOutputList2Cue39=[~UNUSED3~]\nOutputList2SigType39=Analog\nOutputList2Cue40=[~UNUSED3~]\nOutputList2SigType40=Analog\nOutputList2Cue41=[~UNUSED3~]\nOutputList2SigType41=Analog\nOutputList2Cue42=[~UNUSED2~]\nOutputList2SigType42=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=Set Item 11 Text\nInputList3SigType22=Serial\nInputList3Cue23=Set Item 12 Text\nInputList3SigType23=Serial\nInputList3Cue24=Set Item 13 Text\nInputList3SigType24=Serial\nInputList3Cue25=Set Item 14 Text\nInputList3SigType25=Serial\nInputList3Cue26=Set Item 15 Text\nInputList3SigType26=Serial\nInputList3Cue27=Set Item 16 Text\nInputList3SigType27=Serial\nInputList3Cue28=Set Item 17 Text\nInputList3SigType28=Serial\nInputList3Cue29=Set Item 18 Text\nInputList3SigType29=Serial\nInputList3Cue30=Set Item 19 Text\nInputList3SigType30=Serial\nInputList3Cue31=Set Item 20 Text\nInputList3SigType31=Serial\nInputList3Cue32=Set Item 21 Text\nInputList3SigType32=Serial\nInputList3Cue33=Set Item 22 Text\nInputList3SigType33=Serial\nInputList3Cue34=Set Item 23 Text\nInputList3SigType34=Serial\nInputList3Cue35=Set Item 24 Text\nInputList3SigType35=Serial\nInputList3Cue36=Set Item 25 Text\nInputList3SigType36=Serial\nInputList3Cue37=Set Item 26 Text\nInputList3SigType37=Serial\nInputList3Cue38=Set Item 27 Text\nInputList3SigType38=Serial\nInputList3Cue39=Set Item 28 Text\nInputList3SigType39=Serial\nInputList3Cue40=Set Item 29 Text\nInputList3SigType40=Serial\nInputList3Cue41=Set Item 30 Text\nInputList3SigType41=Serial\nInputList3Cue42=[~UNUSED2~]\nInputList3SigType42=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=Set Item 11 Icon Serial\nInputList3SigType2024=Serial\nInputList3Cue2025=Set Item 12 Icon Serial\nInputList3SigType2025=Serial\nInputList3Cue2026=Set Item 13 Icon Serial\nInputList3SigType2026=Serial\nInputList3Cue2027=Set Item 14 Icon Serial\nInputList3SigType2027=Serial\nInputList3Cue2028=Set Item 15 Icon Serial\nInputList3SigType2028=Serial\nInputList3Cue2029=Set Item 16 Icon Serial\nInputList3SigType2029=Serial\nInputList3Cue2030=Set Item 17 Icon Serial\nInputList3SigType2030=Serial\nInputList3Cue2031=Set Item 18 Icon Serial\nInputList3SigType2031=Serial\nInputList3Cue2032=Set Item 19 Icon Serial\nInputList3SigType2032=Serial\nInputList3Cue2033=Set Item 20 Icon Serial\nInputList3SigType2033=Serial\nInputList3Cue2034=Set Item 21 Icon Serial\nInputList3SigType2034=Serial\nInputList3Cue2035=Set Item 22 Icon Serial\nInputList3SigType2035=Serial\nInputList3Cue2036=Set Item 23 Icon Serial\nInputList3SigType2036=Serial\nInputList3Cue2037=Set Item 24 Icon Serial\nInputList3SigType2037=Serial\nInputList3Cue2038=Set Item 25 Icon Serial\nInputList3SigType2038=Serial\nInputList3Cue2039=Set Item 26 Icon Serial\nInputList3SigType2039=Serial\nInputList3Cue2040=Set Item 27 Icon Serial\nInputList3SigType2040=Serial\nInputList3Cue2041=Set Item 28 Icon Serial\nInputList3SigType2041=Serial\nInputList3Cue2042=Set Item 29 Icon Serial\nInputList3SigType2042=Serial\nInputList3Cue2043=Set Item 30 Icon Serial\nInputList3SigType2043=Serial\nInputList3Cue2044=[~UNUSED2~]\nInputList3SigType2044=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED3~]\nOutputList3SigType22=Serial\nOutputList3Cue23=[~UNUSED3~]\nOutputList3SigType23=Serial\nOutputList3Cue24=[~UNUSED3~]\nOutputList3SigType24=Serial\nOutputList3Cue25=[~UNUSED3~]\nOutputList3SigType25=Serial\nOutputList3Cue26=[~UNUSED3~]\nOutputList3SigType26=Serial\nOutputList3Cue27=[~UNUSED3~]\nOutputList3SigType27=Serial\nOutputList3Cue28=[~UNUSED3~]\nOutputList3SigType28=Serial\nOutputList3Cue29=[~UNUSED3~]\nOutputList3SigType29=Serial\nOutputList3Cue30=[~UNUSED3~]\nOutputList3SigType30=Serial\nOutputList3Cue31=[~UNUSED3~]\nOutputList3SigType31=Serial\nOutputList3Cue32=[~UNUSED3~]\nOutputList3SigType32=Serial\nOutputList3Cue33=[~UNUSED3~]\nOutputList3SigType33=Serial\nOutputList3Cue34=[~UNUSED3~]\nOutputList3SigType34=Serial\nOutputList3Cue35=[~UNUSED3~]\nOutputList3SigType35=Serial\nOutputList3Cue36=[~UNUSED3~]\nOutputList3SigType36=Serial\nOutputList3Cue37=[~UNUSED3~]\nOutputList3SigType37=Serial\nOutputList3Cue38=[~UNUSED3~]\nOutputList3SigType38=Serial\nOutputList3Cue39=[~UNUSED3~]\nOutputList3SigType39=Serial\nOutputList3Cue40=[~UNUSED3~]\nOutputList3SigType40=Serial\nOutputList3Cue41=[~UNUSED3~]\nOutputList3SigType41=Serial\nOutputList3Cue42=[~UNUSED2~]\nOutputList3SigType42=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED3~]\nOutputList3SigType2024=Serial\nOutputList3Cue2025=[~UNUSED3~]\nOutputList3SigType2025=Serial\nOutputList3Cue2026=[~UNUSED3~]\nOutputList3SigType2026=Serial\nOutputList3Cue2027=[~UNUSED3~]\nOutputList3SigType2027=Serial\nOutputList3Cue2028=[~UNUSED3~]\nOutputList3SigType2028=Serial\nOutputList3Cue2029=[~UNUSED3~]\nOutputList3SigType2029=Serial\nOutputList3Cue2030=[~UNUSED3~]\nOutputList3SigType2030=Serial\nOutputList3Cue2031=[~UNUSED3~]\nOutputList3SigType2031=Serial\nOutputList3Cue2032=[~UNUSED3~]\nOutputList3SigType2032=Serial\nOutputList3Cue2033=[~UNUSED3~]\nOutputList3SigType2033=Serial\nOutputList3Cue2034=[~UNUSED3~]\nOutputList3SigType2034=Serial\nOutputList3Cue2035=[~UNUSED3~]\nOutputList3SigType2035=Serial\nOutputList3Cue2036=[~UNUSED3~]\nOutputList3SigType2036=Serial\nOutputList3Cue2037=[~UNUSED3~]\nOutputList3SigType2037=Serial\nOutputList3Cue2038=[~UNUSED3~]\nOutputList3SigType2038=Serial\nOutputList3Cue2039=[~UNUSED3~]\nOutputList3SigType2039=Serial\nOutputList3Cue2040=[~UNUSED3~]\nOutputList3SigType2040=Serial\nOutputList3Cue2041=[~UNUSED3~]\nOutputList3SigType2041=Serial\nOutputList3Cue2042=[~UNUSED3~]\nOutputList3SigType2042=Serial\nOutputList3Cue2043=[~UNUSED3~]\nOutputList3SigType2043=Serial\nOutputList3Cue2044=[~UNUSED2~]\nOutputList3SigType2044=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=19\nCedH=19\nSmartObjId=1203d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=19\nTp=1\nHD=TRUE\nDV=1203d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=19\nName=PepperDash Essentials TSW-760_[D.VC] Recents_Dynamic Button List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n"
  },
  {
    "path": "src/PepperDash.Essentials/SGD/PepperDash Essentials iPad.sgd",
    "content": "[\nObjTp=FSgntr\nSgntr=SGD\nRelVrs=3\nVTProeVer=6.1.05\nSchema=1\nCRCGUID=BA116BF9-17DA-41E0-BF8A-42945AAA686D\n]\n;================================================================================\n[\nObjTp=Hd\nProjectFile=PepperDash Essentials iPad.vtp\nVtpGuid=D8D5F125-CB35-42E9-8AE3-4142597FD2C5\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[C.AC] Keypad_AC DTMF Keypad_2.ced\nHint=AC DTMF Keypad_2 (Smart Object ID=1101)\nCode=1\nSGControlType=Simple Keypad\nSGControlName=AC DTMF Keypad_2\nGUID=F2A259FE-82AD-4085-B5A6-22D7B034458C\nSmplCName=PepperDash Essentials iPad_[C.AC] Keypad_AC DTMF Keypad_2.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=*\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=/#\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=1\nCedH=1\nSmartObjId=1101d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=1\nTp=1\nHD=TRUE\nDV=1101d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=1\nName=PepperDash Essentials iPad_[C.AC] Keypad_AC DTMF Keypad_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[D.VC] Directory_Dynamic Button List Vertical_2.ced\nHint=Dynamic Button List Vertical_2 (Smart Object ID=1202)\nCode=2\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical_2\nGUID=79060F3C-D3A4-4A8D-ACF0-CB2376A31702\nSmplCName=PepperDash Essentials iPad_[D.VC] Directory_Dynamic Button List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=Item 11 Selected\nInputSigType22=Digital\nInputCue23=Item 12 Selected\nInputSigType23=Digital\nInputCue24=Item 13 Selected\nInputSigType24=Digital\nInputCue25=Item 14 Selected\nInputSigType25=Digital\nInputCue26=Item 15 Selected\nInputSigType26=Digital\nInputCue27=Item 16 Selected\nInputSigType27=Digital\nInputCue28=Item 17 Selected\nInputSigType28=Digital\nInputCue29=Item 18 Selected\nInputSigType29=Digital\nInputCue30=Item 19 Selected\nInputSigType30=Digital\nInputCue31=Item 20 Selected\nInputSigType31=Digital\nInputCue32=Item 21 Selected\nInputSigType32=Digital\nInputCue33=Item 22 Selected\nInputSigType33=Digital\nInputCue34=Item 23 Selected\nInputSigType34=Digital\nInputCue35=Item 24 Selected\nInputSigType35=Digital\nInputCue36=Item 25 Selected\nInputSigType36=Digital\nInputCue37=Item 26 Selected\nInputSigType37=Digital\nInputCue38=Item 27 Selected\nInputSigType38=Digital\nInputCue39=Item 28 Selected\nInputSigType39=Digital\nInputCue40=Item 29 Selected\nInputSigType40=Digital\nInputCue41=Item 30 Selected\nInputSigType41=Digital\nInputCue42=Item 31 Selected\nInputSigType42=Digital\nInputCue43=Item 32 Selected\nInputSigType43=Digital\nInputCue44=Item 33 Selected\nInputSigType44=Digital\nInputCue45=Item 34 Selected\nInputSigType45=Digital\nInputCue46=Item 35 Selected\nInputSigType46=Digital\nInputCue47=Item 36 Selected\nInputSigType47=Digital\nInputCue48=Item 37 Selected\nInputSigType48=Digital\nInputCue49=Item 38 Selected\nInputSigType49=Digital\nInputCue50=Item 39 Selected\nInputSigType50=Digital\nInputCue51=Item 40 Selected\nInputSigType51=Digital\nInputCue52=Item 41 Selected\nInputSigType52=Digital\nInputCue53=Item 42 Selected\nInputSigType53=Digital\nInputCue54=Item 43 Selected\nInputSigType54=Digital\nInputCue55=Item 44 Selected\nInputSigType55=Digital\nInputCue56=Item 45 Selected\nInputSigType56=Digital\nInputCue57=Item 46 Selected\nInputSigType57=Digital\nInputCue58=Item 47 Selected\nInputSigType58=Digital\nInputCue59=Item 48 Selected\nInputSigType59=Digital\nInputCue60=Item 49 Selected\nInputSigType60=Digital\nInputCue61=Item 50 Selected\nInputSigType61=Digital\nInputCue62=Item 51 Selected\nInputSigType62=Digital\nInputCue63=Item 52 Selected\nInputSigType63=Digital\nInputCue64=Item 53 Selected\nInputSigType64=Digital\nInputCue65=Item 54 Selected\nInputSigType65=Digital\nInputCue66=Item 55 Selected\nInputSigType66=Digital\nInputCue67=Item 56 Selected\nInputSigType67=Digital\nInputCue68=Item 57 Selected\nInputSigType68=Digital\nInputCue69=Item 58 Selected\nInputSigType69=Digital\nInputCue70=Item 59 Selected\nInputSigType70=Digital\nInputCue71=Item 60 Selected\nInputSigType71=Digital\nInputCue72=Item 61 Selected\nInputSigType72=Digital\nInputCue73=Item 62 Selected\nInputSigType73=Digital\nInputCue74=Item 63 Selected\nInputSigType74=Digital\nInputCue75=Item 64 Selected\nInputSigType75=Digital\nInputCue76=Item 65 Selected\nInputSigType76=Digital\nInputCue77=Item 66 Selected\nInputSigType77=Digital\nInputCue78=Item 67 Selected\nInputSigType78=Digital\nInputCue79=Item 68 Selected\nInputSigType79=Digital\nInputCue80=Item 69 Selected\nInputSigType80=Digital\nInputCue81=Item 70 Selected\nInputSigType81=Digital\nInputCue82=Item 71 Selected\nInputSigType82=Digital\nInputCue83=Item 72 Selected\nInputSigType83=Digital\nInputCue84=Item 73 Selected\nInputSigType84=Digital\nInputCue85=Item 74 Selected\nInputSigType85=Digital\nInputCue86=Item 75 Selected\nInputSigType86=Digital\nInputCue87=Item 76 Selected\nInputSigType87=Digital\nInputCue88=Item 77 Selected\nInputSigType88=Digital\nInputCue89=Item 78 Selected\nInputSigType89=Digital\nInputCue90=Item 79 Selected\nInputSigType90=Digital\nInputCue91=Item 80 Selected\nInputSigType91=Digital\nInputCue92=Item 81 Selected\nInputSigType92=Digital\nInputCue93=Item 82 Selected\nInputSigType93=Digital\nInputCue94=Item 83 Selected\nInputSigType94=Digital\nInputCue95=Item 84 Selected\nInputSigType95=Digital\nInputCue96=Item 85 Selected\nInputSigType96=Digital\nInputCue97=Item 86 Selected\nInputSigType97=Digital\nInputCue98=Item 87 Selected\nInputSigType98=Digital\nInputCue99=Item 88 Selected\nInputSigType99=Digital\nInputCue100=Item 89 Selected\nInputSigType100=Digital\nInputCue101=Item 90 Selected\nInputSigType101=Digital\nInputCue102=Item 91 Selected\nInputSigType102=Digital\nInputCue103=Item 92 Selected\nInputSigType103=Digital\nInputCue104=Item 93 Selected\nInputSigType104=Digital\nInputCue105=Item 94 Selected\nInputSigType105=Digital\nInputCue106=Item 95 Selected\nInputSigType106=Digital\nInputCue107=Item 96 Selected\nInputSigType107=Digital\nInputCue108=Item 97 Selected\nInputSigType108=Digital\nInputCue109=Item 98 Selected\nInputSigType109=Digital\nInputCue110=Item 99 Selected\nInputSigType110=Digital\nInputCue111=Item 100 Selected\nInputSigType111=Digital\nInputCue112=Item 101 Selected\nInputSigType112=Digital\nInputCue113=Item 102 Selected\nInputSigType113=Digital\nInputCue114=Item 103 Selected\nInputSigType114=Digital\nInputCue115=Item 104 Selected\nInputSigType115=Digital\nInputCue116=Item 105 Selected\nInputSigType116=Digital\nInputCue117=Item 106 Selected\nInputSigType117=Digital\nInputCue118=Item 107 Selected\nInputSigType118=Digital\nInputCue119=Item 108 Selected\nInputSigType119=Digital\nInputCue120=Item 109 Selected\nInputSigType120=Digital\nInputCue121=Item 110 Selected\nInputSigType121=Digital\nInputCue122=Item 111 Selected\nInputSigType122=Digital\nInputCue123=Item 112 Selected\nInputSigType123=Digital\nInputCue124=Item 113 Selected\nInputSigType124=Digital\nInputCue125=Item 114 Selected\nInputSigType125=Digital\nInputCue126=Item 115 Selected\nInputSigType126=Digital\nInputCue127=Item 116 Selected\nInputSigType127=Digital\nInputCue128=Item 117 Selected\nInputSigType128=Digital\nInputCue129=Item 118 Selected\nInputSigType129=Digital\nInputCue130=Item 119 Selected\nInputSigType130=Digital\nInputCue131=Item 120 Selected\nInputSigType131=Digital\nInputCue132=Item 121 Selected\nInputSigType132=Digital\nInputCue133=Item 122 Selected\nInputSigType133=Digital\nInputCue134=Item 123 Selected\nInputSigType134=Digital\nInputCue135=Item 124 Selected\nInputSigType135=Digital\nInputCue136=Item 125 Selected\nInputSigType136=Digital\nInputCue137=Item 126 Selected\nInputSigType137=Digital\nInputCue138=Item 127 Selected\nInputSigType138=Digital\nInputCue139=Item 128 Selected\nInputSigType139=Digital\nInputCue140=Item 129 Selected\nInputSigType140=Digital\nInputCue141=Item 130 Selected\nInputSigType141=Digital\nInputCue142=Item 131 Selected\nInputSigType142=Digital\nInputCue143=Item 132 Selected\nInputSigType143=Digital\nInputCue144=Item 133 Selected\nInputSigType144=Digital\nInputCue145=Item 134 Selected\nInputSigType145=Digital\nInputCue146=Item 135 Selected\nInputSigType146=Digital\nInputCue147=Item 136 Selected\nInputSigType147=Digital\nInputCue148=Item 137 Selected\nInputSigType148=Digital\nInputCue149=Item 138 Selected\nInputSigType149=Digital\nInputCue150=Item 139 Selected\nInputSigType150=Digital\nInputCue151=Item 140 Selected\nInputSigType151=Digital\nInputCue152=Item 141 Selected\nInputSigType152=Digital\nInputCue153=Item 142 Selected\nInputSigType153=Digital\nInputCue154=Item 143 Selected\nInputSigType154=Digital\nInputCue155=Item 144 Selected\nInputSigType155=Digital\nInputCue156=Item 145 Selected\nInputSigType156=Digital\nInputCue157=Item 146 Selected\nInputSigType157=Digital\nInputCue158=Item 147 Selected\nInputSigType158=Digital\nInputCue159=Item 148 Selected\nInputSigType159=Digital\nInputCue160=Item 149 Selected\nInputSigType160=Digital\nInputCue161=Item 150 Selected\nInputSigType161=Digital\nInputCue162=Item 151 Selected\nInputSigType162=Digital\nInputCue163=Item 152 Selected\nInputSigType163=Digital\nInputCue164=Item 153 Selected\nInputSigType164=Digital\nInputCue165=Item 154 Selected\nInputSigType165=Digital\nInputCue166=Item 155 Selected\nInputSigType166=Digital\nInputCue167=Item 156 Selected\nInputSigType167=Digital\nInputCue168=Item 157 Selected\nInputSigType168=Digital\nInputCue169=Item 158 Selected\nInputSigType169=Digital\nInputCue170=Item 159 Selected\nInputSigType170=Digital\nInputCue171=Item 160 Selected\nInputSigType171=Digital\nInputCue172=Item 161 Selected\nInputSigType172=Digital\nInputCue173=Item 162 Selected\nInputSigType173=Digital\nInputCue174=Item 163 Selected\nInputSigType174=Digital\nInputCue175=Item 164 Selected\nInputSigType175=Digital\nInputCue176=Item 165 Selected\nInputSigType176=Digital\nInputCue177=Item 166 Selected\nInputSigType177=Digital\nInputCue178=Item 167 Selected\nInputSigType178=Digital\nInputCue179=Item 168 Selected\nInputSigType179=Digital\nInputCue180=Item 169 Selected\nInputSigType180=Digital\nInputCue181=Item 170 Selected\nInputSigType181=Digital\nInputCue182=Item 171 Selected\nInputSigType182=Digital\nInputCue183=Item 172 Selected\nInputSigType183=Digital\nInputCue184=Item 173 Selected\nInputSigType184=Digital\nInputCue185=Item 174 Selected\nInputSigType185=Digital\nInputCue186=Item 175 Selected\nInputSigType186=Digital\nInputCue187=Item 176 Selected\nInputSigType187=Digital\nInputCue188=Item 177 Selected\nInputSigType188=Digital\nInputCue189=Item 178 Selected\nInputSigType189=Digital\nInputCue190=Item 179 Selected\nInputSigType190=Digital\nInputCue191=Item 180 Selected\nInputSigType191=Digital\nInputCue192=Item 181 Selected\nInputSigType192=Digital\nInputCue193=Item 182 Selected\nInputSigType193=Digital\nInputCue194=Item 183 Selected\nInputSigType194=Digital\nInputCue195=Item 184 Selected\nInputSigType195=Digital\nInputCue196=Item 185 Selected\nInputSigType196=Digital\nInputCue197=Item 186 Selected\nInputSigType197=Digital\nInputCue198=Item 187 Selected\nInputSigType198=Digital\nInputCue199=Item 188 Selected\nInputSigType199=Digital\nInputCue200=Item 189 Selected\nInputSigType200=Digital\nInputCue201=Item 190 Selected\nInputSigType201=Digital\nInputCue202=Item 191 Selected\nInputSigType202=Digital\nInputCue203=Item 192 Selected\nInputSigType203=Digital\nInputCue204=Item 193 Selected\nInputSigType204=Digital\nInputCue205=Item 194 Selected\nInputSigType205=Digital\nInputCue206=Item 195 Selected\nInputSigType206=Digital\nInputCue207=Item 196 Selected\nInputSigType207=Digital\nInputCue208=Item 197 Selected\nInputSigType208=Digital\nInputCue209=Item 198 Selected\nInputSigType209=Digital\nInputCue210=Item 199 Selected\nInputSigType210=Digital\nInputCue211=Item 200 Selected\nInputSigType211=Digital\nInputCue212=Item 201 Selected\nInputSigType212=Digital\nInputCue213=Item 202 Selected\nInputSigType213=Digital\nInputCue214=Item 203 Selected\nInputSigType214=Digital\nInputCue215=Item 204 Selected\nInputSigType215=Digital\nInputCue216=Item 205 Selected\nInputSigType216=Digital\nInputCue217=Item 206 Selected\nInputSigType217=Digital\nInputCue218=Item 207 Selected\nInputSigType218=Digital\nInputCue219=Item 208 Selected\nInputSigType219=Digital\nInputCue220=Item 209 Selected\nInputSigType220=Digital\nInputCue221=Item 210 Selected\nInputSigType221=Digital\nInputCue222=Item 211 Selected\nInputSigType222=Digital\nInputCue223=Item 212 Selected\nInputSigType223=Digital\nInputCue224=Item 213 Selected\nInputSigType224=Digital\nInputCue225=Item 214 Selected\nInputSigType225=Digital\nInputCue226=Item 215 Selected\nInputSigType226=Digital\nInputCue227=Item 216 Selected\nInputSigType227=Digital\nInputCue228=Item 217 Selected\nInputSigType228=Digital\nInputCue229=Item 218 Selected\nInputSigType229=Digital\nInputCue230=Item 219 Selected\nInputSigType230=Digital\nInputCue231=Item 220 Selected\nInputSigType231=Digital\nInputCue232=Item 221 Selected\nInputSigType232=Digital\nInputCue233=Item 222 Selected\nInputSigType233=Digital\nInputCue234=Item 223 Selected\nInputSigType234=Digital\nInputCue235=Item 224 Selected\nInputSigType235=Digital\nInputCue236=Item 225 Selected\nInputSigType236=Digital\nInputCue237=Item 226 Selected\nInputSigType237=Digital\nInputCue238=Item 227 Selected\nInputSigType238=Digital\nInputCue239=Item 228 Selected\nInputSigType239=Digital\nInputCue240=Item 229 Selected\nInputSigType240=Digital\nInputCue241=Item 230 Selected\nInputSigType241=Digital\nInputCue242=Item 231 Selected\nInputSigType242=Digital\nInputCue243=Item 232 Selected\nInputSigType243=Digital\nInputCue244=Item 233 Selected\nInputSigType244=Digital\nInputCue245=Item 234 Selected\nInputSigType245=Digital\nInputCue246=Item 235 Selected\nInputSigType246=Digital\nInputCue247=Item 236 Selected\nInputSigType247=Digital\nInputCue248=Item 237 Selected\nInputSigType248=Digital\nInputCue249=Item 238 Selected\nInputSigType249=Digital\nInputCue250=Item 239 Selected\nInputSigType250=Digital\nInputCue251=Item 240 Selected\nInputSigType251=Digital\nInputCue252=Item 241 Selected\nInputSigType252=Digital\nInputCue253=Item 242 Selected\nInputSigType253=Digital\nInputCue254=Item 243 Selected\nInputSigType254=Digital\nInputCue255=Item 244 Selected\nInputSigType255=Digital\nInputCue256=Item 245 Selected\nInputSigType256=Digital\nInputCue257=Item 246 Selected\nInputSigType257=Digital\nInputCue258=Item 247 Selected\nInputSigType258=Digital\nInputCue259=Item 248 Selected\nInputSigType259=Digital\nInputCue260=Item 249 Selected\nInputSigType260=Digital\nInputCue261=Item 250 Selected\nInputSigType261=Digital\nInputCue262=Item 251 Selected\nInputSigType262=Digital\nInputCue263=Item 252 Selected\nInputSigType263=Digital\nInputCue264=Item 253 Selected\nInputSigType264=Digital\nInputCue265=Item 254 Selected\nInputSigType265=Digital\nInputCue266=Item 255 Selected\nInputSigType266=Digital\nInputCue267=[~UNUSED2~]\nInputSigType267=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=Item 11 Enabled\nInputSigType2024=Digital\nInputCue2025=Item 12 Enabled\nInputSigType2025=Digital\nInputCue2026=Item 13 Enabled\nInputSigType2026=Digital\nInputCue2027=Item 14 Enabled\nInputSigType2027=Digital\nInputCue2028=Item 15 Enabled\nInputSigType2028=Digital\nInputCue2029=Item 16 Enabled\nInputSigType2029=Digital\nInputCue2030=Item 17 Enabled\nInputSigType2030=Digital\nInputCue2031=Item 18 Enabled\nInputSigType2031=Digital\nInputCue2032=Item 19 Enabled\nInputSigType2032=Digital\nInputCue2033=Item 20 Enabled\nInputSigType2033=Digital\nInputCue2034=Item 21 Enabled\nInputSigType2034=Digital\nInputCue2035=Item 22 Enabled\nInputSigType2035=Digital\nInputCue2036=Item 23 Enabled\nInputSigType2036=Digital\nInputCue2037=Item 24 Enabled\nInputSigType2037=Digital\nInputCue2038=Item 25 Enabled\nInputSigType2038=Digital\nInputCue2039=Item 26 Enabled\nInputSigType2039=Digital\nInputCue2040=Item 27 Enabled\nInputSigType2040=Digital\nInputCue2041=Item 28 Enabled\nInputSigType2041=Digital\nInputCue2042=Item 29 Enabled\nInputSigType2042=Digital\nInputCue2043=Item 30 Enabled\nInputSigType2043=Digital\nInputCue2044=Item 31 Enabled\nInputSigType2044=Digital\nInputCue2045=Item 32 Enabled\nInputSigType2045=Digital\nInputCue2046=Item 33 Enabled\nInputSigType2046=Digital\nInputCue2047=Item 34 Enabled\nInputSigType2047=Digital\nInputCue2048=Item 35 Enabled\nInputSigType2048=Digital\nInputCue2049=Item 36 Enabled\nInputSigType2049=Digital\nInputCue2050=Item 37 Enabled\nInputSigType2050=Digital\nInputCue2051=Item 38 Enabled\nInputSigType2051=Digital\nInputCue2052=Item 39 Enabled\nInputSigType2052=Digital\nInputCue2053=Item 40 Enabled\nInputSigType2053=Digital\nInputCue2054=Item 41 Enabled\nInputSigType2054=Digital\nInputCue2055=Item 42 Enabled\nInputSigType2055=Digital\nInputCue2056=Item 43 Enabled\nInputSigType2056=Digital\nInputCue2057=Item 44 Enabled\nInputSigType2057=Digital\nInputCue2058=Item 45 Enabled\nInputSigType2058=Digital\nInputCue2059=Item 46 Enabled\nInputSigType2059=Digital\nInputCue2060=Item 47 Enabled\nInputSigType2060=Digital\nInputCue2061=Item 48 Enabled\nInputSigType2061=Digital\nInputCue2062=Item 49 Enabled\nInputSigType2062=Digital\nInputCue2063=Item 50 Enabled\nInputSigType2063=Digital\nInputCue2064=Item 51 Enabled\nInputSigType2064=Digital\nInputCue2065=Item 52 Enabled\nInputSigType2065=Digital\nInputCue2066=Item 53 Enabled\nInputSigType2066=Digital\nInputCue2067=Item 54 Enabled\nInputSigType2067=Digital\nInputCue2068=Item 55 Enabled\nInputSigType2068=Digital\nInputCue2069=Item 56 Enabled\nInputSigType2069=Digital\nInputCue2070=Item 57 Enabled\nInputSigType2070=Digital\nInputCue2071=Item 58 Enabled\nInputSigType2071=Digital\nInputCue2072=Item 59 Enabled\nInputSigType2072=Digital\nInputCue2073=Item 60 Enabled\nInputSigType2073=Digital\nInputCue2074=Item 61 Enabled\nInputSigType2074=Digital\nInputCue2075=Item 62 Enabled\nInputSigType2075=Digital\nInputCue2076=Item 63 Enabled\nInputSigType2076=Digital\nInputCue2077=Item 64 Enabled\nInputSigType2077=Digital\nInputCue2078=Item 65 Enabled\nInputSigType2078=Digital\nInputCue2079=Item 66 Enabled\nInputSigType2079=Digital\nInputCue2080=Item 67 Enabled\nInputSigType2080=Digital\nInputCue2081=Item 68 Enabled\nInputSigType2081=Digital\nInputCue2082=Item 69 Enabled\nInputSigType2082=Digital\nInputCue2083=Item 70 Enabled\nInputSigType2083=Digital\nInputCue2084=Item 71 Enabled\nInputSigType2084=Digital\nInputCue2085=Item 72 Enabled\nInputSigType2085=Digital\nInputCue2086=Item 73 Enabled\nInputSigType2086=Digital\nInputCue2087=Item 74 Enabled\nInputSigType2087=Digital\nInputCue2088=Item 75 Enabled\nInputSigType2088=Digital\nInputCue2089=Item 76 Enabled\nInputSigType2089=Digital\nInputCue2090=Item 77 Enabled\nInputSigType2090=Digital\nInputCue2091=Item 78 Enabled\nInputSigType2091=Digital\nInputCue2092=Item 79 Enabled\nInputSigType2092=Digital\nInputCue2093=Item 80 Enabled\nInputSigType2093=Digital\nInputCue2094=Item 81 Enabled\nInputSigType2094=Digital\nInputCue2095=Item 82 Enabled\nInputSigType2095=Digital\nInputCue2096=Item 83 Enabled\nInputSigType2096=Digital\nInputCue2097=Item 84 Enabled\nInputSigType2097=Digital\nInputCue2098=Item 85 Enabled\nInputSigType2098=Digital\nInputCue2099=Item 86 Enabled\nInputSigType2099=Digital\nInputCue2100=Item 87 Enabled\nInputSigType2100=Digital\nInputCue2101=Item 88 Enabled\nInputSigType2101=Digital\nInputCue2102=Item 89 Enabled\nInputSigType2102=Digital\nInputCue2103=Item 90 Enabled\nInputSigType2103=Digital\nInputCue2104=Item 91 Enabled\nInputSigType2104=Digital\nInputCue2105=Item 92 Enabled\nInputSigType2105=Digital\nInputCue2106=Item 93 Enabled\nInputSigType2106=Digital\nInputCue2107=Item 94 Enabled\nInputSigType2107=Digital\nInputCue2108=Item 95 Enabled\nInputSigType2108=Digital\nInputCue2109=Item 96 Enabled\nInputSigType2109=Digital\nInputCue2110=Item 97 Enabled\nInputSigType2110=Digital\nInputCue2111=Item 98 Enabled\nInputSigType2111=Digital\nInputCue2112=Item 99 Enabled\nInputSigType2112=Digital\nInputCue2113=Item 100 Enabled\nInputSigType2113=Digital\nInputCue2114=Item 101 Enabled\nInputSigType2114=Digital\nInputCue2115=Item 102 Enabled\nInputSigType2115=Digital\nInputCue2116=Item 103 Enabled\nInputSigType2116=Digital\nInputCue2117=Item 104 Enabled\nInputSigType2117=Digital\nInputCue2118=Item 105 Enabled\nInputSigType2118=Digital\nInputCue2119=Item 106 Enabled\nInputSigType2119=Digital\nInputCue2120=Item 107 Enabled\nInputSigType2120=Digital\nInputCue2121=Item 108 Enabled\nInputSigType2121=Digital\nInputCue2122=Item 109 Enabled\nInputSigType2122=Digital\nInputCue2123=Item 110 Enabled\nInputSigType2123=Digital\nInputCue2124=Item 111 Enabled\nInputSigType2124=Digital\nInputCue2125=Item 112 Enabled\nInputSigType2125=Digital\nInputCue2126=Item 113 Enabled\nInputSigType2126=Digital\nInputCue2127=Item 114 Enabled\nInputSigType2127=Digital\nInputCue2128=Item 115 Enabled\nInputSigType2128=Digital\nInputCue2129=Item 116 Enabled\nInputSigType2129=Digital\nInputCue2130=Item 117 Enabled\nInputSigType2130=Digital\nInputCue2131=Item 118 Enabled\nInputSigType2131=Digital\nInputCue2132=Item 119 Enabled\nInputSigType2132=Digital\nInputCue2133=Item 120 Enabled\nInputSigType2133=Digital\nInputCue2134=Item 121 Enabled\nInputSigType2134=Digital\nInputCue2135=Item 122 Enabled\nInputSigType2135=Digital\nInputCue2136=Item 123 Enabled\nInputSigType2136=Digital\nInputCue2137=Item 124 Enabled\nInputSigType2137=Digital\nInputCue2138=Item 125 Enabled\nInputSigType2138=Digital\nInputCue2139=Item 126 Enabled\nInputSigType2139=Digital\nInputCue2140=Item 127 Enabled\nInputSigType2140=Digital\nInputCue2141=Item 128 Enabled\nInputSigType2141=Digital\nInputCue2142=Item 129 Enabled\nInputSigType2142=Digital\nInputCue2143=Item 130 Enabled\nInputSigType2143=Digital\nInputCue2144=Item 131 Enabled\nInputSigType2144=Digital\nInputCue2145=Item 132 Enabled\nInputSigType2145=Digital\nInputCue2146=Item 133 Enabled\nInputSigType2146=Digital\nInputCue2147=Item 134 Enabled\nInputSigType2147=Digital\nInputCue2148=Item 135 Enabled\nInputSigType2148=Digital\nInputCue2149=Item 136 Enabled\nInputSigType2149=Digital\nInputCue2150=Item 137 Enabled\nInputSigType2150=Digital\nInputCue2151=Item 138 Enabled\nInputSigType2151=Digital\nInputCue2152=Item 139 Enabled\nInputSigType2152=Digital\nInputCue2153=Item 140 Enabled\nInputSigType2153=Digital\nInputCue2154=Item 141 Enabled\nInputSigType2154=Digital\nInputCue2155=Item 142 Enabled\nInputSigType2155=Digital\nInputCue2156=Item 143 Enabled\nInputSigType2156=Digital\nInputCue2157=Item 144 Enabled\nInputSigType2157=Digital\nInputCue2158=Item 145 Enabled\nInputSigType2158=Digital\nInputCue2159=Item 146 Enabled\nInputSigType2159=Digital\nInputCue2160=Item 147 Enabled\nInputSigType2160=Digital\nInputCue2161=Item 148 Enabled\nInputSigType2161=Digital\nInputCue2162=Item 149 Enabled\nInputSigType2162=Digital\nInputCue2163=Item 150 Enabled\nInputSigType2163=Digital\nInputCue2164=Item 151 Enabled\nInputSigType2164=Digital\nInputCue2165=Item 152 Enabled\nInputSigType2165=Digital\nInputCue2166=Item 153 Enabled\nInputSigType2166=Digital\nInputCue2167=Item 154 Enabled\nInputSigType2167=Digital\nInputCue2168=Item 155 Enabled\nInputSigType2168=Digital\nInputCue2169=Item 156 Enabled\nInputSigType2169=Digital\nInputCue2170=Item 157 Enabled\nInputSigType2170=Digital\nInputCue2171=Item 158 Enabled\nInputSigType2171=Digital\nInputCue2172=Item 159 Enabled\nInputSigType2172=Digital\nInputCue2173=Item 160 Enabled\nInputSigType2173=Digital\nInputCue2174=Item 161 Enabled\nInputSigType2174=Digital\nInputCue2175=Item 162 Enabled\nInputSigType2175=Digital\nInputCue2176=Item 163 Enabled\nInputSigType2176=Digital\nInputCue2177=Item 164 Enabled\nInputSigType2177=Digital\nInputCue2178=Item 165 Enabled\nInputSigType2178=Digital\nInputCue2179=Item 166 Enabled\nInputSigType2179=Digital\nInputCue2180=Item 167 Enabled\nInputSigType2180=Digital\nInputCue2181=Item 168 Enabled\nInputSigType2181=Digital\nInputCue2182=Item 169 Enabled\nInputSigType2182=Digital\nInputCue2183=Item 170 Enabled\nInputSigType2183=Digital\nInputCue2184=Item 171 Enabled\nInputSigType2184=Digital\nInputCue2185=Item 172 Enabled\nInputSigType2185=Digital\nInputCue2186=Item 173 Enabled\nInputSigType2186=Digital\nInputCue2187=Item 174 Enabled\nInputSigType2187=Digital\nInputCue2188=Item 175 Enabled\nInputSigType2188=Digital\nInputCue2189=Item 176 Enabled\nInputSigType2189=Digital\nInputCue2190=Item 177 Enabled\nInputSigType2190=Digital\nInputCue2191=Item 178 Enabled\nInputSigType2191=Digital\nInputCue2192=Item 179 Enabled\nInputSigType2192=Digital\nInputCue2193=Item 180 Enabled\nInputSigType2193=Digital\nInputCue2194=Item 181 Enabled\nInputSigType2194=Digital\nInputCue2195=Item 182 Enabled\nInputSigType2195=Digital\nInputCue2196=Item 183 Enabled\nInputSigType2196=Digital\nInputCue2197=Item 184 Enabled\nInputSigType2197=Digital\nInputCue2198=Item 185 Enabled\nInputSigType2198=Digital\nInputCue2199=Item 186 Enabled\nInputSigType2199=Digital\nInputCue2200=Item 187 Enabled\nInputSigType2200=Digital\nInputCue2201=Item 188 Enabled\nInputSigType2201=Digital\nInputCue2202=Item 189 Enabled\nInputSigType2202=Digital\nInputCue2203=Item 190 Enabled\nInputSigType2203=Digital\nInputCue2204=Item 191 Enabled\nInputSigType2204=Digital\nInputCue2205=Item 192 Enabled\nInputSigType2205=Digital\nInputCue2206=Item 193 Enabled\nInputSigType2206=Digital\nInputCue2207=Item 194 Enabled\nInputSigType2207=Digital\nInputCue2208=Item 195 Enabled\nInputSigType2208=Digital\nInputCue2209=Item 196 Enabled\nInputSigType2209=Digital\nInputCue2210=Item 197 Enabled\nInputSigType2210=Digital\nInputCue2211=Item 198 Enabled\nInputSigType2211=Digital\nInputCue2212=Item 199 Enabled\nInputSigType2212=Digital\nInputCue2213=Item 200 Enabled\nInputSigType2213=Digital\nInputCue2214=Item 201 Enabled\nInputSigType2214=Digital\nInputCue2215=Item 202 Enabled\nInputSigType2215=Digital\nInputCue2216=Item 203 Enabled\nInputSigType2216=Digital\nInputCue2217=Item 204 Enabled\nInputSigType2217=Digital\nInputCue2218=Item 205 Enabled\nInputSigType2218=Digital\nInputCue2219=Item 206 Enabled\nInputSigType2219=Digital\nInputCue2220=Item 207 Enabled\nInputSigType2220=Digital\nInputCue2221=Item 208 Enabled\nInputSigType2221=Digital\nInputCue2222=Item 209 Enabled\nInputSigType2222=Digital\nInputCue2223=Item 210 Enabled\nInputSigType2223=Digital\nInputCue2224=Item 211 Enabled\nInputSigType2224=Digital\nInputCue2225=Item 212 Enabled\nInputSigType2225=Digital\nInputCue2226=Item 213 Enabled\nInputSigType2226=Digital\nInputCue2227=Item 214 Enabled\nInputSigType2227=Digital\nInputCue2228=Item 215 Enabled\nInputSigType2228=Digital\nInputCue2229=Item 216 Enabled\nInputSigType2229=Digital\nInputCue2230=Item 217 Enabled\nInputSigType2230=Digital\nInputCue2231=Item 218 Enabled\nInputSigType2231=Digital\nInputCue2232=Item 219 Enabled\nInputSigType2232=Digital\nInputCue2233=Item 220 Enabled\nInputSigType2233=Digital\nInputCue2234=Item 221 Enabled\nInputSigType2234=Digital\nInputCue2235=Item 222 Enabled\nInputSigType2235=Digital\nInputCue2236=Item 223 Enabled\nInputSigType2236=Digital\nInputCue2237=Item 224 Enabled\nInputSigType2237=Digital\nInputCue2238=Item 225 Enabled\nInputSigType2238=Digital\nInputCue2239=Item 226 Enabled\nInputSigType2239=Digital\nInputCue2240=Item 227 Enabled\nInputSigType2240=Digital\nInputCue2241=Item 228 Enabled\nInputSigType2241=Digital\nInputCue2242=Item 229 Enabled\nInputSigType2242=Digital\nInputCue2243=Item 230 Enabled\nInputSigType2243=Digital\nInputCue2244=Item 231 Enabled\nInputSigType2244=Digital\nInputCue2245=Item 232 Enabled\nInputSigType2245=Digital\nInputCue2246=Item 233 Enabled\nInputSigType2246=Digital\nInputCue2247=Item 234 Enabled\nInputSigType2247=Digital\nInputCue2248=Item 235 Enabled\nInputSigType2248=Digital\nInputCue2249=Item 236 Enabled\nInputSigType2249=Digital\nInputCue2250=Item 237 Enabled\nInputSigType2250=Digital\nInputCue2251=Item 238 Enabled\nInputSigType2251=Digital\nInputCue2252=Item 239 Enabled\nInputSigType2252=Digital\nInputCue2253=Item 240 Enabled\nInputSigType2253=Digital\nInputCue2254=Item 241 Enabled\nInputSigType2254=Digital\nInputCue2255=Item 242 Enabled\nInputSigType2255=Digital\nInputCue2256=Item 243 Enabled\nInputSigType2256=Digital\nInputCue2257=Item 244 Enabled\nInputSigType2257=Digital\nInputCue2258=Item 245 Enabled\nInputSigType2258=Digital\nInputCue2259=Item 246 Enabled\nInputSigType2259=Digital\nInputCue2260=Item 247 Enabled\nInputSigType2260=Digital\nInputCue2261=Item 248 Enabled\nInputSigType2261=Digital\nInputCue2262=Item 249 Enabled\nInputSigType2262=Digital\nInputCue2263=Item 250 Enabled\nInputSigType2263=Digital\nInputCue2264=Item 251 Enabled\nInputSigType2264=Digital\nInputCue2265=Item 252 Enabled\nInputSigType2265=Digital\nInputCue2266=Item 253 Enabled\nInputSigType2266=Digital\nInputCue2267=Item 254 Enabled\nInputSigType2267=Digital\nInputCue2268=Item 255 Enabled\nInputSigType2268=Digital\nInputCue2269=[~UNUSED2~]\nInputSigType2269=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=Item 11 Visible\nInputSigType4026=Digital\nInputCue4027=Item 12 Visible\nInputSigType4027=Digital\nInputCue4028=Item 13 Visible\nInputSigType4028=Digital\nInputCue4029=Item 14 Visible\nInputSigType4029=Digital\nInputCue4030=Item 15 Visible\nInputSigType4030=Digital\nInputCue4031=Item 16 Visible\nInputSigType4031=Digital\nInputCue4032=Item 17 Visible\nInputSigType4032=Digital\nInputCue4033=Item 18 Visible\nInputSigType4033=Digital\nInputCue4034=Item 19 Visible\nInputSigType4034=Digital\nInputCue4035=Item 20 Visible\nInputSigType4035=Digital\nInputCue4036=Item 21 Visible\nInputSigType4036=Digital\nInputCue4037=Item 22 Visible\nInputSigType4037=Digital\nInputCue4038=Item 23 Visible\nInputSigType4038=Digital\nInputCue4039=Item 24 Visible\nInputSigType4039=Digital\nInputCue4040=Item 25 Visible\nInputSigType4040=Digital\nInputCue4041=Item 26 Visible\nInputSigType4041=Digital\nInputCue4042=Item 27 Visible\nInputSigType4042=Digital\nInputCue4043=Item 28 Visible\nInputSigType4043=Digital\nInputCue4044=Item 29 Visible\nInputSigType4044=Digital\nInputCue4045=Item 30 Visible\nInputSigType4045=Digital\nInputCue4046=Item 31 Visible\nInputSigType4046=Digital\nInputCue4047=Item 32 Visible\nInputSigType4047=Digital\nInputCue4048=Item 33 Visible\nInputSigType4048=Digital\nInputCue4049=Item 34 Visible\nInputSigType4049=Digital\nInputCue4050=Item 35 Visible\nInputSigType4050=Digital\nInputCue4051=Item 36 Visible\nInputSigType4051=Digital\nInputCue4052=Item 37 Visible\nInputSigType4052=Digital\nInputCue4053=Item 38 Visible\nInputSigType4053=Digital\nInputCue4054=Item 39 Visible\nInputSigType4054=Digital\nInputCue4055=Item 40 Visible\nInputSigType4055=Digital\nInputCue4056=Item 41 Visible\nInputSigType4056=Digital\nInputCue4057=Item 42 Visible\nInputSigType4057=Digital\nInputCue4058=Item 43 Visible\nInputSigType4058=Digital\nInputCue4059=Item 44 Visible\nInputSigType4059=Digital\nInputCue4060=Item 45 Visible\nInputSigType4060=Digital\nInputCue4061=Item 46 Visible\nInputSigType4061=Digital\nInputCue4062=Item 47 Visible\nInputSigType4062=Digital\nInputCue4063=Item 48 Visible\nInputSigType4063=Digital\nInputCue4064=Item 49 Visible\nInputSigType4064=Digital\nInputCue4065=Item 50 Visible\nInputSigType4065=Digital\nInputCue4066=Item 51 Visible\nInputSigType4066=Digital\nInputCue4067=Item 52 Visible\nInputSigType4067=Digital\nInputCue4068=Item 53 Visible\nInputSigType4068=Digital\nInputCue4069=Item 54 Visible\nInputSigType4069=Digital\nInputCue4070=Item 55 Visible\nInputSigType4070=Digital\nInputCue4071=Item 56 Visible\nInputSigType4071=Digital\nInputCue4072=Item 57 Visible\nInputSigType4072=Digital\nInputCue4073=Item 58 Visible\nInputSigType4073=Digital\nInputCue4074=Item 59 Visible\nInputSigType4074=Digital\nInputCue4075=Item 60 Visible\nInputSigType4075=Digital\nInputCue4076=Item 61 Visible\nInputSigType4076=Digital\nInputCue4077=Item 62 Visible\nInputSigType4077=Digital\nInputCue4078=Item 63 Visible\nInputSigType4078=Digital\nInputCue4079=Item 64 Visible\nInputSigType4079=Digital\nInputCue4080=Item 65 Visible\nInputSigType4080=Digital\nInputCue4081=Item 66 Visible\nInputSigType4081=Digital\nInputCue4082=Item 67 Visible\nInputSigType4082=Digital\nInputCue4083=Item 68 Visible\nInputSigType4083=Digital\nInputCue4084=Item 69 Visible\nInputSigType4084=Digital\nInputCue4085=Item 70 Visible\nInputSigType4085=Digital\nInputCue4086=Item 71 Visible\nInputSigType4086=Digital\nInputCue4087=Item 72 Visible\nInputSigType4087=Digital\nInputCue4088=Item 73 Visible\nInputSigType4088=Digital\nInputCue4089=Item 74 Visible\nInputSigType4089=Digital\nInputCue4090=Item 75 Visible\nInputSigType4090=Digital\nInputCue4091=Item 76 Visible\nInputSigType4091=Digital\nInputCue4092=Item 77 Visible\nInputSigType4092=Digital\nInputCue4093=Item 78 Visible\nInputSigType4093=Digital\nInputCue4094=Item 79 Visible\nInputSigType4094=Digital\nInputCue4095=Item 80 Visible\nInputSigType4095=Digital\nInputCue4096=Item 81 Visible\nInputSigType4096=Digital\nInputCue4097=Item 82 Visible\nInputSigType4097=Digital\nInputCue4098=Item 83 Visible\nInputSigType4098=Digital\nInputCue4099=Item 84 Visible\nInputSigType4099=Digital\nInputCue4100=Item 85 Visible\nInputSigType4100=Digital\nInputCue4101=Item 86 Visible\nInputSigType4101=Digital\nInputCue4102=Item 87 Visible\nInputSigType4102=Digital\nInputCue4103=Item 88 Visible\nInputSigType4103=Digital\nInputCue4104=Item 89 Visible\nInputSigType4104=Digital\nInputCue4105=Item 90 Visible\nInputSigType4105=Digital\nInputCue4106=Item 91 Visible\nInputSigType4106=Digital\nInputCue4107=Item 92 Visible\nInputSigType4107=Digital\nInputCue4108=Item 93 Visible\nInputSigType4108=Digital\nInputCue4109=Item 94 Visible\nInputSigType4109=Digital\nInputCue4110=Item 95 Visible\nInputSigType4110=Digital\nInputCue4111=Item 96 Visible\nInputSigType4111=Digital\nInputCue4112=Item 97 Visible\nInputSigType4112=Digital\nInputCue4113=Item 98 Visible\nInputSigType4113=Digital\nInputCue4114=Item 99 Visible\nInputSigType4114=Digital\nInputCue4115=Item 100 Visible\nInputSigType4115=Digital\nInputCue4116=Item 101 Visible\nInputSigType4116=Digital\nInputCue4117=Item 102 Visible\nInputSigType4117=Digital\nInputCue4118=Item 103 Visible\nInputSigType4118=Digital\nInputCue4119=Item 104 Visible\nInputSigType4119=Digital\nInputCue4120=Item 105 Visible\nInputSigType4120=Digital\nInputCue4121=Item 106 Visible\nInputSigType4121=Digital\nInputCue4122=Item 107 Visible\nInputSigType4122=Digital\nInputCue4123=Item 108 Visible\nInputSigType4123=Digital\nInputCue4124=Item 109 Visible\nInputSigType4124=Digital\nInputCue4125=Item 110 Visible\nInputSigType4125=Digital\nInputCue4126=Item 111 Visible\nInputSigType4126=Digital\nInputCue4127=Item 112 Visible\nInputSigType4127=Digital\nInputCue4128=Item 113 Visible\nInputSigType4128=Digital\nInputCue4129=Item 114 Visible\nInputSigType4129=Digital\nInputCue4130=Item 115 Visible\nInputSigType4130=Digital\nInputCue4131=Item 116 Visible\nInputSigType4131=Digital\nInputCue4132=Item 117 Visible\nInputSigType4132=Digital\nInputCue4133=Item 118 Visible\nInputSigType4133=Digital\nInputCue4134=Item 119 Visible\nInputSigType4134=Digital\nInputCue4135=Item 120 Visible\nInputSigType4135=Digital\nInputCue4136=Item 121 Visible\nInputSigType4136=Digital\nInputCue4137=Item 122 Visible\nInputSigType4137=Digital\nInputCue4138=Item 123 Visible\nInputSigType4138=Digital\nInputCue4139=Item 124 Visible\nInputSigType4139=Digital\nInputCue4140=Item 125 Visible\nInputSigType4140=Digital\nInputCue4141=Item 126 Visible\nInputSigType4141=Digital\nInputCue4142=Item 127 Visible\nInputSigType4142=Digital\nInputCue4143=Item 128 Visible\nInputSigType4143=Digital\nInputCue4144=Item 129 Visible\nInputSigType4144=Digital\nInputCue4145=Item 130 Visible\nInputSigType4145=Digital\nInputCue4146=Item 131 Visible\nInputSigType4146=Digital\nInputCue4147=Item 132 Visible\nInputSigType4147=Digital\nInputCue4148=Item 133 Visible\nInputSigType4148=Digital\nInputCue4149=Item 134 Visible\nInputSigType4149=Digital\nInputCue4150=Item 135 Visible\nInputSigType4150=Digital\nInputCue4151=Item 136 Visible\nInputSigType4151=Digital\nInputCue4152=Item 137 Visible\nInputSigType4152=Digital\nInputCue4153=Item 138 Visible\nInputSigType4153=Digital\nInputCue4154=Item 139 Visible\nInputSigType4154=Digital\nInputCue4155=Item 140 Visible\nInputSigType4155=Digital\nInputCue4156=Item 141 Visible\nInputSigType4156=Digital\nInputCue4157=Item 142 Visible\nInputSigType4157=Digital\nInputCue4158=Item 143 Visible\nInputSigType4158=Digital\nInputCue4159=Item 144 Visible\nInputSigType4159=Digital\nInputCue4160=Item 145 Visible\nInputSigType4160=Digital\nInputCue4161=Item 146 Visible\nInputSigType4161=Digital\nInputCue4162=Item 147 Visible\nInputSigType4162=Digital\nInputCue4163=Item 148 Visible\nInputSigType4163=Digital\nInputCue4164=Item 149 Visible\nInputSigType4164=Digital\nInputCue4165=Item 150 Visible\nInputSigType4165=Digital\nInputCue4166=Item 151 Visible\nInputSigType4166=Digital\nInputCue4167=Item 152 Visible\nInputSigType4167=Digital\nInputCue4168=Item 153 Visible\nInputSigType4168=Digital\nInputCue4169=Item 154 Visible\nInputSigType4169=Digital\nInputCue4170=Item 155 Visible\nInputSigType4170=Digital\nInputCue4171=Item 156 Visible\nInputSigType4171=Digital\nInputCue4172=Item 157 Visible\nInputSigType4172=Digital\nInputCue4173=Item 158 Visible\nInputSigType4173=Digital\nInputCue4174=Item 159 Visible\nInputSigType4174=Digital\nInputCue4175=Item 160 Visible\nInputSigType4175=Digital\nInputCue4176=Item 161 Visible\nInputSigType4176=Digital\nInputCue4177=Item 162 Visible\nInputSigType4177=Digital\nInputCue4178=Item 163 Visible\nInputSigType4178=Digital\nInputCue4179=Item 164 Visible\nInputSigType4179=Digital\nInputCue4180=Item 165 Visible\nInputSigType4180=Digital\nInputCue4181=Item 166 Visible\nInputSigType4181=Digital\nInputCue4182=Item 167 Visible\nInputSigType4182=Digital\nInputCue4183=Item 168 Visible\nInputSigType4183=Digital\nInputCue4184=Item 169 Visible\nInputSigType4184=Digital\nInputCue4185=Item 170 Visible\nInputSigType4185=Digital\nInputCue4186=Item 171 Visible\nInputSigType4186=Digital\nInputCue4187=Item 172 Visible\nInputSigType4187=Digital\nInputCue4188=Item 173 Visible\nInputSigType4188=Digital\nInputCue4189=Item 174 Visible\nInputSigType4189=Digital\nInputCue4190=Item 175 Visible\nInputSigType4190=Digital\nInputCue4191=Item 176 Visible\nInputSigType4191=Digital\nInputCue4192=Item 177 Visible\nInputSigType4192=Digital\nInputCue4193=Item 178 Visible\nInputSigType4193=Digital\nInputCue4194=Item 179 Visible\nInputSigType4194=Digital\nInputCue4195=Item 180 Visible\nInputSigType4195=Digital\nInputCue4196=Item 181 Visible\nInputSigType4196=Digital\nInputCue4197=Item 182 Visible\nInputSigType4197=Digital\nInputCue4198=Item 183 Visible\nInputSigType4198=Digital\nInputCue4199=Item 184 Visible\nInputSigType4199=Digital\nInputCue4200=Item 185 Visible\nInputSigType4200=Digital\nInputCue4201=Item 186 Visible\nInputSigType4201=Digital\nInputCue4202=Item 187 Visible\nInputSigType4202=Digital\nInputCue4203=Item 188 Visible\nInputSigType4203=Digital\nInputCue4204=Item 189 Visible\nInputSigType4204=Digital\nInputCue4205=Item 190 Visible\nInputSigType4205=Digital\nInputCue4206=Item 191 Visible\nInputSigType4206=Digital\nInputCue4207=Item 192 Visible\nInputSigType4207=Digital\nInputCue4208=Item 193 Visible\nInputSigType4208=Digital\nInputCue4209=Item 194 Visible\nInputSigType4209=Digital\nInputCue4210=Item 195 Visible\nInputSigType4210=Digital\nInputCue4211=Item 196 Visible\nInputSigType4211=Digital\nInputCue4212=Item 197 Visible\nInputSigType4212=Digital\nInputCue4213=Item 198 Visible\nInputSigType4213=Digital\nInputCue4214=Item 199 Visible\nInputSigType4214=Digital\nInputCue4215=Item 200 Visible\nInputSigType4215=Digital\nInputCue4216=Item 201 Visible\nInputSigType4216=Digital\nInputCue4217=Item 202 Visible\nInputSigType4217=Digital\nInputCue4218=Item 203 Visible\nInputSigType4218=Digital\nInputCue4219=Item 204 Visible\nInputSigType4219=Digital\nInputCue4220=Item 205 Visible\nInputSigType4220=Digital\nInputCue4221=Item 206 Visible\nInputSigType4221=Digital\nInputCue4222=Item 207 Visible\nInputSigType4222=Digital\nInputCue4223=Item 208 Visible\nInputSigType4223=Digital\nInputCue4224=Item 209 Visible\nInputSigType4224=Digital\nInputCue4225=Item 210 Visible\nInputSigType4225=Digital\nInputCue4226=Item 211 Visible\nInputSigType4226=Digital\nInputCue4227=Item 212 Visible\nInputSigType4227=Digital\nInputCue4228=Item 213 Visible\nInputSigType4228=Digital\nInputCue4229=Item 214 Visible\nInputSigType4229=Digital\nInputCue4230=Item 215 Visible\nInputSigType4230=Digital\nInputCue4231=Item 216 Visible\nInputSigType4231=Digital\nInputCue4232=Item 217 Visible\nInputSigType4232=Digital\nInputCue4233=Item 218 Visible\nInputSigType4233=Digital\nInputCue4234=Item 219 Visible\nInputSigType4234=Digital\nInputCue4235=Item 220 Visible\nInputSigType4235=Digital\nInputCue4236=Item 221 Visible\nInputSigType4236=Digital\nInputCue4237=Item 222 Visible\nInputSigType4237=Digital\nInputCue4238=Item 223 Visible\nInputSigType4238=Digital\nInputCue4239=Item 224 Visible\nInputSigType4239=Digital\nInputCue4240=Item 225 Visible\nInputSigType4240=Digital\nInputCue4241=Item 226 Visible\nInputSigType4241=Digital\nInputCue4242=Item 227 Visible\nInputSigType4242=Digital\nInputCue4243=Item 228 Visible\nInputSigType4243=Digital\nInputCue4244=Item 229 Visible\nInputSigType4244=Digital\nInputCue4245=Item 230 Visible\nInputSigType4245=Digital\nInputCue4246=Item 231 Visible\nInputSigType4246=Digital\nInputCue4247=Item 232 Visible\nInputSigType4247=Digital\nInputCue4248=Item 233 Visible\nInputSigType4248=Digital\nInputCue4249=Item 234 Visible\nInputSigType4249=Digital\nInputCue4250=Item 235 Visible\nInputSigType4250=Digital\nInputCue4251=Item 236 Visible\nInputSigType4251=Digital\nInputCue4252=Item 237 Visible\nInputSigType4252=Digital\nInputCue4253=Item 238 Visible\nInputSigType4253=Digital\nInputCue4254=Item 239 Visible\nInputSigType4254=Digital\nInputCue4255=Item 240 Visible\nInputSigType4255=Digital\nInputCue4256=Item 241 Visible\nInputSigType4256=Digital\nInputCue4257=Item 242 Visible\nInputSigType4257=Digital\nInputCue4258=Item 243 Visible\nInputSigType4258=Digital\nInputCue4259=Item 244 Visible\nInputSigType4259=Digital\nInputCue4260=Item 245 Visible\nInputSigType4260=Digital\nInputCue4261=Item 246 Visible\nInputSigType4261=Digital\nInputCue4262=Item 247 Visible\nInputSigType4262=Digital\nInputCue4263=Item 248 Visible\nInputSigType4263=Digital\nInputCue4264=Item 249 Visible\nInputSigType4264=Digital\nInputCue4265=Item 250 Visible\nInputSigType4265=Digital\nInputCue4266=Item 251 Visible\nInputSigType4266=Digital\nInputCue4267=Item 252 Visible\nInputSigType4267=Digital\nInputCue4268=Item 253 Visible\nInputSigType4268=Digital\nInputCue4269=Item 254 Visible\nInputSigType4269=Digital\nInputCue4270=Item 255 Visible\nInputSigType4270=Digital\nInputCue4271=[~UNUSED2~]\nInputSigType4271=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=Item 11 Pressed\nOutputSigType22=Digital\nOutputCue23=Item 12 Pressed\nOutputSigType23=Digital\nOutputCue24=Item 13 Pressed\nOutputSigType24=Digital\nOutputCue25=Item 14 Pressed\nOutputSigType25=Digital\nOutputCue26=Item 15 Pressed\nOutputSigType26=Digital\nOutputCue27=Item 16 Pressed\nOutputSigType27=Digital\nOutputCue28=Item 17 Pressed\nOutputSigType28=Digital\nOutputCue29=Item 18 Pressed\nOutputSigType29=Digital\nOutputCue30=Item 19 Pressed\nOutputSigType30=Digital\nOutputCue31=Item 20 Pressed\nOutputSigType31=Digital\nOutputCue32=Item 21 Pressed\nOutputSigType32=Digital\nOutputCue33=Item 22 Pressed\nOutputSigType33=Digital\nOutputCue34=Item 23 Pressed\nOutputSigType34=Digital\nOutputCue35=Item 24 Pressed\nOutputSigType35=Digital\nOutputCue36=Item 25 Pressed\nOutputSigType36=Digital\nOutputCue37=Item 26 Pressed\nOutputSigType37=Digital\nOutputCue38=Item 27 Pressed\nOutputSigType38=Digital\nOutputCue39=Item 28 Pressed\nOutputSigType39=Digital\nOutputCue40=Item 29 Pressed\nOutputSigType40=Digital\nOutputCue41=Item 30 Pressed\nOutputSigType41=Digital\nOutputCue42=Item 31 Pressed\nOutputSigType42=Digital\nOutputCue43=Item 32 Pressed\nOutputSigType43=Digital\nOutputCue44=Item 33 Pressed\nOutputSigType44=Digital\nOutputCue45=Item 34 Pressed\nOutputSigType45=Digital\nOutputCue46=Item 35 Pressed\nOutputSigType46=Digital\nOutputCue47=Item 36 Pressed\nOutputSigType47=Digital\nOutputCue48=Item 37 Pressed\nOutputSigType48=Digital\nOutputCue49=Item 38 Pressed\nOutputSigType49=Digital\nOutputCue50=Item 39 Pressed\nOutputSigType50=Digital\nOutputCue51=Item 40 Pressed\nOutputSigType51=Digital\nOutputCue52=Item 41 Pressed\nOutputSigType52=Digital\nOutputCue53=Item 42 Pressed\nOutputSigType53=Digital\nOutputCue54=Item 43 Pressed\nOutputSigType54=Digital\nOutputCue55=Item 44 Pressed\nOutputSigType55=Digital\nOutputCue56=Item 45 Pressed\nOutputSigType56=Digital\nOutputCue57=Item 46 Pressed\nOutputSigType57=Digital\nOutputCue58=Item 47 Pressed\nOutputSigType58=Digital\nOutputCue59=Item 48 Pressed\nOutputSigType59=Digital\nOutputCue60=Item 49 Pressed\nOutputSigType60=Digital\nOutputCue61=Item 50 Pressed\nOutputSigType61=Digital\nOutputCue62=Item 51 Pressed\nOutputSigType62=Digital\nOutputCue63=Item 52 Pressed\nOutputSigType63=Digital\nOutputCue64=Item 53 Pressed\nOutputSigType64=Digital\nOutputCue65=Item 54 Pressed\nOutputSigType65=Digital\nOutputCue66=Item 55 Pressed\nOutputSigType66=Digital\nOutputCue67=Item 56 Pressed\nOutputSigType67=Digital\nOutputCue68=Item 57 Pressed\nOutputSigType68=Digital\nOutputCue69=Item 58 Pressed\nOutputSigType69=Digital\nOutputCue70=Item 59 Pressed\nOutputSigType70=Digital\nOutputCue71=Item 60 Pressed\nOutputSigType71=Digital\nOutputCue72=Item 61 Pressed\nOutputSigType72=Digital\nOutputCue73=Item 62 Pressed\nOutputSigType73=Digital\nOutputCue74=Item 63 Pressed\nOutputSigType74=Digital\nOutputCue75=Item 64 Pressed\nOutputSigType75=Digital\nOutputCue76=Item 65 Pressed\nOutputSigType76=Digital\nOutputCue77=Item 66 Pressed\nOutputSigType77=Digital\nOutputCue78=Item 67 Pressed\nOutputSigType78=Digital\nOutputCue79=Item 68 Pressed\nOutputSigType79=Digital\nOutputCue80=Item 69 Pressed\nOutputSigType80=Digital\nOutputCue81=Item 70 Pressed\nOutputSigType81=Digital\nOutputCue82=Item 71 Pressed\nOutputSigType82=Digital\nOutputCue83=Item 72 Pressed\nOutputSigType83=Digital\nOutputCue84=Item 73 Pressed\nOutputSigType84=Digital\nOutputCue85=Item 74 Pressed\nOutputSigType85=Digital\nOutputCue86=Item 75 Pressed\nOutputSigType86=Digital\nOutputCue87=Item 76 Pressed\nOutputSigType87=Digital\nOutputCue88=Item 77 Pressed\nOutputSigType88=Digital\nOutputCue89=Item 78 Pressed\nOutputSigType89=Digital\nOutputCue90=Item 79 Pressed\nOutputSigType90=Digital\nOutputCue91=Item 80 Pressed\nOutputSigType91=Digital\nOutputCue92=Item 81 Pressed\nOutputSigType92=Digital\nOutputCue93=Item 82 Pressed\nOutputSigType93=Digital\nOutputCue94=Item 83 Pressed\nOutputSigType94=Digital\nOutputCue95=Item 84 Pressed\nOutputSigType95=Digital\nOutputCue96=Item 85 Pressed\nOutputSigType96=Digital\nOutputCue97=Item 86 Pressed\nOutputSigType97=Digital\nOutputCue98=Item 87 Pressed\nOutputSigType98=Digital\nOutputCue99=Item 88 Pressed\nOutputSigType99=Digital\nOutputCue100=Item 89 Pressed\nOutputSigType100=Digital\nOutputCue101=Item 90 Pressed\nOutputSigType101=Digital\nOutputCue102=Item 91 Pressed\nOutputSigType102=Digital\nOutputCue103=Item 92 Pressed\nOutputSigType103=Digital\nOutputCue104=Item 93 Pressed\nOutputSigType104=Digital\nOutputCue105=Item 94 Pressed\nOutputSigType105=Digital\nOutputCue106=Item 95 Pressed\nOutputSigType106=Digital\nOutputCue107=Item 96 Pressed\nOutputSigType107=Digital\nOutputCue108=Item 97 Pressed\nOutputSigType108=Digital\nOutputCue109=Item 98 Pressed\nOutputSigType109=Digital\nOutputCue110=Item 99 Pressed\nOutputSigType110=Digital\nOutputCue111=Item 100 Pressed\nOutputSigType111=Digital\nOutputCue112=Item 101 Pressed\nOutputSigType112=Digital\nOutputCue113=Item 102 Pressed\nOutputSigType113=Digital\nOutputCue114=Item 103 Pressed\nOutputSigType114=Digital\nOutputCue115=Item 104 Pressed\nOutputSigType115=Digital\nOutputCue116=Item 105 Pressed\nOutputSigType116=Digital\nOutputCue117=Item 106 Pressed\nOutputSigType117=Digital\nOutputCue118=Item 107 Pressed\nOutputSigType118=Digital\nOutputCue119=Item 108 Pressed\nOutputSigType119=Digital\nOutputCue120=Item 109 Pressed\nOutputSigType120=Digital\nOutputCue121=Item 110 Pressed\nOutputSigType121=Digital\nOutputCue122=Item 111 Pressed\nOutputSigType122=Digital\nOutputCue123=Item 112 Pressed\nOutputSigType123=Digital\nOutputCue124=Item 113 Pressed\nOutputSigType124=Digital\nOutputCue125=Item 114 Pressed\nOutputSigType125=Digital\nOutputCue126=Item 115 Pressed\nOutputSigType126=Digital\nOutputCue127=Item 116 Pressed\nOutputSigType127=Digital\nOutputCue128=Item 117 Pressed\nOutputSigType128=Digital\nOutputCue129=Item 118 Pressed\nOutputSigType129=Digital\nOutputCue130=Item 119 Pressed\nOutputSigType130=Digital\nOutputCue131=Item 120 Pressed\nOutputSigType131=Digital\nOutputCue132=Item 121 Pressed\nOutputSigType132=Digital\nOutputCue133=Item 122 Pressed\nOutputSigType133=Digital\nOutputCue134=Item 123 Pressed\nOutputSigType134=Digital\nOutputCue135=Item 124 Pressed\nOutputSigType135=Digital\nOutputCue136=Item 125 Pressed\nOutputSigType136=Digital\nOutputCue137=Item 126 Pressed\nOutputSigType137=Digital\nOutputCue138=Item 127 Pressed\nOutputSigType138=Digital\nOutputCue139=Item 128 Pressed\nOutputSigType139=Digital\nOutputCue140=Item 129 Pressed\nOutputSigType140=Digital\nOutputCue141=Item 130 Pressed\nOutputSigType141=Digital\nOutputCue142=Item 131 Pressed\nOutputSigType142=Digital\nOutputCue143=Item 132 Pressed\nOutputSigType143=Digital\nOutputCue144=Item 133 Pressed\nOutputSigType144=Digital\nOutputCue145=Item 134 Pressed\nOutputSigType145=Digital\nOutputCue146=Item 135 Pressed\nOutputSigType146=Digital\nOutputCue147=Item 136 Pressed\nOutputSigType147=Digital\nOutputCue148=Item 137 Pressed\nOutputSigType148=Digital\nOutputCue149=Item 138 Pressed\nOutputSigType149=Digital\nOutputCue150=Item 139 Pressed\nOutputSigType150=Digital\nOutputCue151=Item 140 Pressed\nOutputSigType151=Digital\nOutputCue152=Item 141 Pressed\nOutputSigType152=Digital\nOutputCue153=Item 142 Pressed\nOutputSigType153=Digital\nOutputCue154=Item 143 Pressed\nOutputSigType154=Digital\nOutputCue155=Item 144 Pressed\nOutputSigType155=Digital\nOutputCue156=Item 145 Pressed\nOutputSigType156=Digital\nOutputCue157=Item 146 Pressed\nOutputSigType157=Digital\nOutputCue158=Item 147 Pressed\nOutputSigType158=Digital\nOutputCue159=Item 148 Pressed\nOutputSigType159=Digital\nOutputCue160=Item 149 Pressed\nOutputSigType160=Digital\nOutputCue161=Item 150 Pressed\nOutputSigType161=Digital\nOutputCue162=Item 151 Pressed\nOutputSigType162=Digital\nOutputCue163=Item 152 Pressed\nOutputSigType163=Digital\nOutputCue164=Item 153 Pressed\nOutputSigType164=Digital\nOutputCue165=Item 154 Pressed\nOutputSigType165=Digital\nOutputCue166=Item 155 Pressed\nOutputSigType166=Digital\nOutputCue167=Item 156 Pressed\nOutputSigType167=Digital\nOutputCue168=Item 157 Pressed\nOutputSigType168=Digital\nOutputCue169=Item 158 Pressed\nOutputSigType169=Digital\nOutputCue170=Item 159 Pressed\nOutputSigType170=Digital\nOutputCue171=Item 160 Pressed\nOutputSigType171=Digital\nOutputCue172=Item 161 Pressed\nOutputSigType172=Digital\nOutputCue173=Item 162 Pressed\nOutputSigType173=Digital\nOutputCue174=Item 163 Pressed\nOutputSigType174=Digital\nOutputCue175=Item 164 Pressed\nOutputSigType175=Digital\nOutputCue176=Item 165 Pressed\nOutputSigType176=Digital\nOutputCue177=Item 166 Pressed\nOutputSigType177=Digital\nOutputCue178=Item 167 Pressed\nOutputSigType178=Digital\nOutputCue179=Item 168 Pressed\nOutputSigType179=Digital\nOutputCue180=Item 169 Pressed\nOutputSigType180=Digital\nOutputCue181=Item 170 Pressed\nOutputSigType181=Digital\nOutputCue182=Item 171 Pressed\nOutputSigType182=Digital\nOutputCue183=Item 172 Pressed\nOutputSigType183=Digital\nOutputCue184=Item 173 Pressed\nOutputSigType184=Digital\nOutputCue185=Item 174 Pressed\nOutputSigType185=Digital\nOutputCue186=Item 175 Pressed\nOutputSigType186=Digital\nOutputCue187=Item 176 Pressed\nOutputSigType187=Digital\nOutputCue188=Item 177 Pressed\nOutputSigType188=Digital\nOutputCue189=Item 178 Pressed\nOutputSigType189=Digital\nOutputCue190=Item 179 Pressed\nOutputSigType190=Digital\nOutputCue191=Item 180 Pressed\nOutputSigType191=Digital\nOutputCue192=Item 181 Pressed\nOutputSigType192=Digital\nOutputCue193=Item 182 Pressed\nOutputSigType193=Digital\nOutputCue194=Item 183 Pressed\nOutputSigType194=Digital\nOutputCue195=Item 184 Pressed\nOutputSigType195=Digital\nOutputCue196=Item 185 Pressed\nOutputSigType196=Digital\nOutputCue197=Item 186 Pressed\nOutputSigType197=Digital\nOutputCue198=Item 187 Pressed\nOutputSigType198=Digital\nOutputCue199=Item 188 Pressed\nOutputSigType199=Digital\nOutputCue200=Item 189 Pressed\nOutputSigType200=Digital\nOutputCue201=Item 190 Pressed\nOutputSigType201=Digital\nOutputCue202=Item 191 Pressed\nOutputSigType202=Digital\nOutputCue203=Item 192 Pressed\nOutputSigType203=Digital\nOutputCue204=Item 193 Pressed\nOutputSigType204=Digital\nOutputCue205=Item 194 Pressed\nOutputSigType205=Digital\nOutputCue206=Item 195 Pressed\nOutputSigType206=Digital\nOutputCue207=Item 196 Pressed\nOutputSigType207=Digital\nOutputCue208=Item 197 Pressed\nOutputSigType208=Digital\nOutputCue209=Item 198 Pressed\nOutputSigType209=Digital\nOutputCue210=Item 199 Pressed\nOutputSigType210=Digital\nOutputCue211=Item 200 Pressed\nOutputSigType211=Digital\nOutputCue212=Item 201 Pressed\nOutputSigType212=Digital\nOutputCue213=Item 202 Pressed\nOutputSigType213=Digital\nOutputCue214=Item 203 Pressed\nOutputSigType214=Digital\nOutputCue215=Item 204 Pressed\nOutputSigType215=Digital\nOutputCue216=Item 205 Pressed\nOutputSigType216=Digital\nOutputCue217=Item 206 Pressed\nOutputSigType217=Digital\nOutputCue218=Item 207 Pressed\nOutputSigType218=Digital\nOutputCue219=Item 208 Pressed\nOutputSigType219=Digital\nOutputCue220=Item 209 Pressed\nOutputSigType220=Digital\nOutputCue221=Item 210 Pressed\nOutputSigType221=Digital\nOutputCue222=Item 211 Pressed\nOutputSigType222=Digital\nOutputCue223=Item 212 Pressed\nOutputSigType223=Digital\nOutputCue224=Item 213 Pressed\nOutputSigType224=Digital\nOutputCue225=Item 214 Pressed\nOutputSigType225=Digital\nOutputCue226=Item 215 Pressed\nOutputSigType226=Digital\nOutputCue227=Item 216 Pressed\nOutputSigType227=Digital\nOutputCue228=Item 217 Pressed\nOutputSigType228=Digital\nOutputCue229=Item 218 Pressed\nOutputSigType229=Digital\nOutputCue230=Item 219 Pressed\nOutputSigType230=Digital\nOutputCue231=Item 220 Pressed\nOutputSigType231=Digital\nOutputCue232=Item 221 Pressed\nOutputSigType232=Digital\nOutputCue233=Item 222 Pressed\nOutputSigType233=Digital\nOutputCue234=Item 223 Pressed\nOutputSigType234=Digital\nOutputCue235=Item 224 Pressed\nOutputSigType235=Digital\nOutputCue236=Item 225 Pressed\nOutputSigType236=Digital\nOutputCue237=Item 226 Pressed\nOutputSigType237=Digital\nOutputCue238=Item 227 Pressed\nOutputSigType238=Digital\nOutputCue239=Item 228 Pressed\nOutputSigType239=Digital\nOutputCue240=Item 229 Pressed\nOutputSigType240=Digital\nOutputCue241=Item 230 Pressed\nOutputSigType241=Digital\nOutputCue242=Item 231 Pressed\nOutputSigType242=Digital\nOutputCue243=Item 232 Pressed\nOutputSigType243=Digital\nOutputCue244=Item 233 Pressed\nOutputSigType244=Digital\nOutputCue245=Item 234 Pressed\nOutputSigType245=Digital\nOutputCue246=Item 235 Pressed\nOutputSigType246=Digital\nOutputCue247=Item 236 Pressed\nOutputSigType247=Digital\nOutputCue248=Item 237 Pressed\nOutputSigType248=Digital\nOutputCue249=Item 238 Pressed\nOutputSigType249=Digital\nOutputCue250=Item 239 Pressed\nOutputSigType250=Digital\nOutputCue251=Item 240 Pressed\nOutputSigType251=Digital\nOutputCue252=Item 241 Pressed\nOutputSigType252=Digital\nOutputCue253=Item 242 Pressed\nOutputSigType253=Digital\nOutputCue254=Item 243 Pressed\nOutputSigType254=Digital\nOutputCue255=Item 244 Pressed\nOutputSigType255=Digital\nOutputCue256=Item 245 Pressed\nOutputSigType256=Digital\nOutputCue257=Item 246 Pressed\nOutputSigType257=Digital\nOutputCue258=Item 247 Pressed\nOutputSigType258=Digital\nOutputCue259=Item 248 Pressed\nOutputSigType259=Digital\nOutputCue260=Item 249 Pressed\nOutputSigType260=Digital\nOutputCue261=Item 250 Pressed\nOutputSigType261=Digital\nOutputCue262=Item 251 Pressed\nOutputSigType262=Digital\nOutputCue263=Item 252 Pressed\nOutputSigType263=Digital\nOutputCue264=Item 253 Pressed\nOutputSigType264=Digital\nOutputCue265=Item 254 Pressed\nOutputSigType265=Digital\nOutputCue266=Item 255 Pressed\nOutputSigType266=Digital\nOutputCue267=[~UNUSED2~]\nOutputSigType267=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED3~]\nOutputSigType2044=Digital\nOutputCue2045=[~UNUSED3~]\nOutputSigType2045=Digital\nOutputCue2046=[~UNUSED3~]\nOutputSigType2046=Digital\nOutputCue2047=[~UNUSED3~]\nOutputSigType2047=Digital\nOutputCue2048=[~UNUSED3~]\nOutputSigType2048=Digital\nOutputCue2049=[~UNUSED3~]\nOutputSigType2049=Digital\nOutputCue2050=[~UNUSED3~]\nOutputSigType2050=Digital\nOutputCue2051=[~UNUSED3~]\nOutputSigType2051=Digital\nOutputCue2052=[~UNUSED3~]\nOutputSigType2052=Digital\nOutputCue2053=[~UNUSED3~]\nOutputSigType2053=Digital\nOutputCue2054=[~UNUSED3~]\nOutputSigType2054=Digital\nOutputCue2055=[~UNUSED3~]\nOutputSigType2055=Digital\nOutputCue2056=[~UNUSED3~]\nOutputSigType2056=Digital\nOutputCue2057=[~UNUSED3~]\nOutputSigType2057=Digital\nOutputCue2058=[~UNUSED3~]\nOutputSigType2058=Digital\nOutputCue2059=[~UNUSED3~]\nOutputSigType2059=Digital\nOutputCue2060=[~UNUSED3~]\nOutputSigType2060=Digital\nOutputCue2061=[~UNUSED3~]\nOutputSigType2061=Digital\nOutputCue2062=[~UNUSED3~]\nOutputSigType2062=Digital\nOutputCue2063=[~UNUSED3~]\nOutputSigType2063=Digital\nOutputCue2064=[~UNUSED3~]\nOutputSigType2064=Digital\nOutputCue2065=[~UNUSED3~]\nOutputSigType2065=Digital\nOutputCue2066=[~UNUSED3~]\nOutputSigType2066=Digital\nOutputCue2067=[~UNUSED3~]\nOutputSigType2067=Digital\nOutputCue2068=[~UNUSED3~]\nOutputSigType2068=Digital\nOutputCue2069=[~UNUSED3~]\nOutputSigType2069=Digital\nOutputCue2070=[~UNUSED3~]\nOutputSigType2070=Digital\nOutputCue2071=[~UNUSED3~]\nOutputSigType2071=Digital\nOutputCue2072=[~UNUSED3~]\nOutputSigType2072=Digital\nOutputCue2073=[~UNUSED3~]\nOutputSigType2073=Digital\nOutputCue2074=[~UNUSED3~]\nOutputSigType2074=Digital\nOutputCue2075=[~UNUSED3~]\nOutputSigType2075=Digital\nOutputCue2076=[~UNUSED3~]\nOutputSigType2076=Digital\nOutputCue2077=[~UNUSED3~]\nOutputSigType2077=Digital\nOutputCue2078=[~UNUSED3~]\nOutputSigType2078=Digital\nOutputCue2079=[~UNUSED3~]\nOutputSigType2079=Digital\nOutputCue2080=[~UNUSED3~]\nOutputSigType2080=Digital\nOutputCue2081=[~UNUSED3~]\nOutputSigType2081=Digital\nOutputCue2082=[~UNUSED3~]\nOutputSigType2082=Digital\nOutputCue2083=[~UNUSED3~]\nOutputSigType2083=Digital\nOutputCue2084=[~UNUSED3~]\nOutputSigType2084=Digital\nOutputCue2085=[~UNUSED3~]\nOutputSigType2085=Digital\nOutputCue2086=[~UNUSED3~]\nOutputSigType2086=Digital\nOutputCue2087=[~UNUSED3~]\nOutputSigType2087=Digital\nOutputCue2088=[~UNUSED3~]\nOutputSigType2088=Digital\nOutputCue2089=[~UNUSED3~]\nOutputSigType2089=Digital\nOutputCue2090=[~UNUSED3~]\nOutputSigType2090=Digital\nOutputCue2091=[~UNUSED3~]\nOutputSigType2091=Digital\nOutputCue2092=[~UNUSED3~]\nOutputSigType2092=Digital\nOutputCue2093=[~UNUSED3~]\nOutputSigType2093=Digital\nOutputCue2094=[~UNUSED3~]\nOutputSigType2094=Digital\nOutputCue2095=[~UNUSED3~]\nOutputSigType2095=Digital\nOutputCue2096=[~UNUSED3~]\nOutputSigType2096=Digital\nOutputCue2097=[~UNUSED3~]\nOutputSigType2097=Digital\nOutputCue2098=[~UNUSED3~]\nOutputSigType2098=Digital\nOutputCue2099=[~UNUSED3~]\nOutputSigType2099=Digital\nOutputCue2100=[~UNUSED3~]\nOutputSigType2100=Digital\nOutputCue2101=[~UNUSED3~]\nOutputSigType2101=Digital\nOutputCue2102=[~UNUSED3~]\nOutputSigType2102=Digital\nOutputCue2103=[~UNUSED3~]\nOutputSigType2103=Digital\nOutputCue2104=[~UNUSED3~]\nOutputSigType2104=Digital\nOutputCue2105=[~UNUSED3~]\nOutputSigType2105=Digital\nOutputCue2106=[~UNUSED3~]\nOutputSigType2106=Digital\nOutputCue2107=[~UNUSED3~]\nOutputSigType2107=Digital\nOutputCue2108=[~UNUSED3~]\nOutputSigType2108=Digital\nOutputCue2109=[~UNUSED3~]\nOutputSigType2109=Digital\nOutputCue2110=[~UNUSED3~]\nOutputSigType2110=Digital\nOutputCue2111=[~UNUSED3~]\nOutputSigType2111=Digital\nOutputCue2112=[~UNUSED3~]\nOutputSigType2112=Digital\nOutputCue2113=[~UNUSED3~]\nOutputSigType2113=Digital\nOutputCue2114=[~UNUSED3~]\nOutputSigType2114=Digital\nOutputCue2115=[~UNUSED3~]\nOutputSigType2115=Digital\nOutputCue2116=[~UNUSED3~]\nOutputSigType2116=Digital\nOutputCue2117=[~UNUSED3~]\nOutputSigType2117=Digital\nOutputCue2118=[~UNUSED3~]\nOutputSigType2118=Digital\nOutputCue2119=[~UNUSED3~]\nOutputSigType2119=Digital\nOutputCue2120=[~UNUSED3~]\nOutputSigType2120=Digital\nOutputCue2121=[~UNUSED3~]\nOutputSigType2121=Digital\nOutputCue2122=[~UNUSED3~]\nOutputSigType2122=Digital\nOutputCue2123=[~UNUSED3~]\nOutputSigType2123=Digital\nOutputCue2124=[~UNUSED3~]\nOutputSigType2124=Digital\nOutputCue2125=[~UNUSED3~]\nOutputSigType2125=Digital\nOutputCue2126=[~UNUSED3~]\nOutputSigType2126=Digital\nOutputCue2127=[~UNUSED3~]\nOutputSigType2127=Digital\nOutputCue2128=[~UNUSED3~]\nOutputSigType2128=Digital\nOutputCue2129=[~UNUSED3~]\nOutputSigType2129=Digital\nOutputCue2130=[~UNUSED3~]\nOutputSigType2130=Digital\nOutputCue2131=[~UNUSED3~]\nOutputSigType2131=Digital\nOutputCue2132=[~UNUSED3~]\nOutputSigType2132=Digital\nOutputCue2133=[~UNUSED3~]\nOutputSigType2133=Digital\nOutputCue2134=[~UNUSED3~]\nOutputSigType2134=Digital\nOutputCue2135=[~UNUSED3~]\nOutputSigType2135=Digital\nOutputCue2136=[~UNUSED3~]\nOutputSigType2136=Digital\nOutputCue2137=[~UNUSED3~]\nOutputSigType2137=Digital\nOutputCue2138=[~UNUSED3~]\nOutputSigType2138=Digital\nOutputCue2139=[~UNUSED3~]\nOutputSigType2139=Digital\nOutputCue2140=[~UNUSED3~]\nOutputSigType2140=Digital\nOutputCue2141=[~UNUSED3~]\nOutputSigType2141=Digital\nOutputCue2142=[~UNUSED3~]\nOutputSigType2142=Digital\nOutputCue2143=[~UNUSED3~]\nOutputSigType2143=Digital\nOutputCue2144=[~UNUSED3~]\nOutputSigType2144=Digital\nOutputCue2145=[~UNUSED3~]\nOutputSigType2145=Digital\nOutputCue2146=[~UNUSED3~]\nOutputSigType2146=Digital\nOutputCue2147=[~UNUSED3~]\nOutputSigType2147=Digital\nOutputCue2148=[~UNUSED3~]\nOutputSigType2148=Digital\nOutputCue2149=[~UNUSED3~]\nOutputSigType2149=Digital\nOutputCue2150=[~UNUSED3~]\nOutputSigType2150=Digital\nOutputCue2151=[~UNUSED3~]\nOutputSigType2151=Digital\nOutputCue2152=[~UNUSED3~]\nOutputSigType2152=Digital\nOutputCue2153=[~UNUSED3~]\nOutputSigType2153=Digital\nOutputCue2154=[~UNUSED3~]\nOutputSigType2154=Digital\nOutputCue2155=[~UNUSED3~]\nOutputSigType2155=Digital\nOutputCue2156=[~UNUSED3~]\nOutputSigType2156=Digital\nOutputCue2157=[~UNUSED3~]\nOutputSigType2157=Digital\nOutputCue2158=[~UNUSED3~]\nOutputSigType2158=Digital\nOutputCue2159=[~UNUSED3~]\nOutputSigType2159=Digital\nOutputCue2160=[~UNUSED3~]\nOutputSigType2160=Digital\nOutputCue2161=[~UNUSED3~]\nOutputSigType2161=Digital\nOutputCue2162=[~UNUSED3~]\nOutputSigType2162=Digital\nOutputCue2163=[~UNUSED3~]\nOutputSigType2163=Digital\nOutputCue2164=[~UNUSED3~]\nOutputSigType2164=Digital\nOutputCue2165=[~UNUSED3~]\nOutputSigType2165=Digital\nOutputCue2166=[~UNUSED3~]\nOutputSigType2166=Digital\nOutputCue2167=[~UNUSED3~]\nOutputSigType2167=Digital\nOutputCue2168=[~UNUSED3~]\nOutputSigType2168=Digital\nOutputCue2169=[~UNUSED3~]\nOutputSigType2169=Digital\nOutputCue2170=[~UNUSED3~]\nOutputSigType2170=Digital\nOutputCue2171=[~UNUSED3~]\nOutputSigType2171=Digital\nOutputCue2172=[~UNUSED3~]\nOutputSigType2172=Digital\nOutputCue2173=[~UNUSED3~]\nOutputSigType2173=Digital\nOutputCue2174=[~UNUSED3~]\nOutputSigType2174=Digital\nOutputCue2175=[~UNUSED3~]\nOutputSigType2175=Digital\nOutputCue2176=[~UNUSED3~]\nOutputSigType2176=Digital\nOutputCue2177=[~UNUSED3~]\nOutputSigType2177=Digital\nOutputCue2178=[~UNUSED3~]\nOutputSigType2178=Digital\nOutputCue2179=[~UNUSED3~]\nOutputSigType2179=Digital\nOutputCue2180=[~UNUSED3~]\nOutputSigType2180=Digital\nOutputCue2181=[~UNUSED3~]\nOutputSigType2181=Digital\nOutputCue2182=[~UNUSED3~]\nOutputSigType2182=Digital\nOutputCue2183=[~UNUSED3~]\nOutputSigType2183=Digital\nOutputCue2184=[~UNUSED3~]\nOutputSigType2184=Digital\nOutputCue2185=[~UNUSED3~]\nOutputSigType2185=Digital\nOutputCue2186=[~UNUSED3~]\nOutputSigType2186=Digital\nOutputCue2187=[~UNUSED3~]\nOutputSigType2187=Digital\nOutputCue2188=[~UNUSED3~]\nOutputSigType2188=Digital\nOutputCue2189=[~UNUSED3~]\nOutputSigType2189=Digital\nOutputCue2190=[~UNUSED3~]\nOutputSigType2190=Digital\nOutputCue2191=[~UNUSED3~]\nOutputSigType2191=Digital\nOutputCue2192=[~UNUSED3~]\nOutputSigType2192=Digital\nOutputCue2193=[~UNUSED3~]\nOutputSigType2193=Digital\nOutputCue2194=[~UNUSED3~]\nOutputSigType2194=Digital\nOutputCue2195=[~UNUSED3~]\nOutputSigType2195=Digital\nOutputCue2196=[~UNUSED3~]\nOutputSigType2196=Digital\nOutputCue2197=[~UNUSED3~]\nOutputSigType2197=Digital\nOutputCue2198=[~UNUSED3~]\nOutputSigType2198=Digital\nOutputCue2199=[~UNUSED3~]\nOutputSigType2199=Digital\nOutputCue2200=[~UNUSED3~]\nOutputSigType2200=Digital\nOutputCue2201=[~UNUSED3~]\nOutputSigType2201=Digital\nOutputCue2202=[~UNUSED3~]\nOutputSigType2202=Digital\nOutputCue2203=[~UNUSED3~]\nOutputSigType2203=Digital\nOutputCue2204=[~UNUSED3~]\nOutputSigType2204=Digital\nOutputCue2205=[~UNUSED3~]\nOutputSigType2205=Digital\nOutputCue2206=[~UNUSED3~]\nOutputSigType2206=Digital\nOutputCue2207=[~UNUSED3~]\nOutputSigType2207=Digital\nOutputCue2208=[~UNUSED3~]\nOutputSigType2208=Digital\nOutputCue2209=[~UNUSED3~]\nOutputSigType2209=Digital\nOutputCue2210=[~UNUSED3~]\nOutputSigType2210=Digital\nOutputCue2211=[~UNUSED3~]\nOutputSigType2211=Digital\nOutputCue2212=[~UNUSED3~]\nOutputSigType2212=Digital\nOutputCue2213=[~UNUSED3~]\nOutputSigType2213=Digital\nOutputCue2214=[~UNUSED3~]\nOutputSigType2214=Digital\nOutputCue2215=[~UNUSED3~]\nOutputSigType2215=Digital\nOutputCue2216=[~UNUSED3~]\nOutputSigType2216=Digital\nOutputCue2217=[~UNUSED3~]\nOutputSigType2217=Digital\nOutputCue2218=[~UNUSED3~]\nOutputSigType2218=Digital\nOutputCue2219=[~UNUSED3~]\nOutputSigType2219=Digital\nOutputCue2220=[~UNUSED3~]\nOutputSigType2220=Digital\nOutputCue2221=[~UNUSED3~]\nOutputSigType2221=Digital\nOutputCue2222=[~UNUSED3~]\nOutputSigType2222=Digital\nOutputCue2223=[~UNUSED3~]\nOutputSigType2223=Digital\nOutputCue2224=[~UNUSED3~]\nOutputSigType2224=Digital\nOutputCue2225=[~UNUSED3~]\nOutputSigType2225=Digital\nOutputCue2226=[~UNUSED3~]\nOutputSigType2226=Digital\nOutputCue2227=[~UNUSED3~]\nOutputSigType2227=Digital\nOutputCue2228=[~UNUSED3~]\nOutputSigType2228=Digital\nOutputCue2229=[~UNUSED3~]\nOutputSigType2229=Digital\nOutputCue2230=[~UNUSED3~]\nOutputSigType2230=Digital\nOutputCue2231=[~UNUSED3~]\nOutputSigType2231=Digital\nOutputCue2232=[~UNUSED3~]\nOutputSigType2232=Digital\nOutputCue2233=[~UNUSED3~]\nOutputSigType2233=Digital\nOutputCue2234=[~UNUSED3~]\nOutputSigType2234=Digital\nOutputCue2235=[~UNUSED3~]\nOutputSigType2235=Digital\nOutputCue2236=[~UNUSED3~]\nOutputSigType2236=Digital\nOutputCue2237=[~UNUSED3~]\nOutputSigType2237=Digital\nOutputCue2238=[~UNUSED3~]\nOutputSigType2238=Digital\nOutputCue2239=[~UNUSED3~]\nOutputSigType2239=Digital\nOutputCue2240=[~UNUSED3~]\nOutputSigType2240=Digital\nOutputCue2241=[~UNUSED3~]\nOutputSigType2241=Digital\nOutputCue2242=[~UNUSED3~]\nOutputSigType2242=Digital\nOutputCue2243=[~UNUSED3~]\nOutputSigType2243=Digital\nOutputCue2244=[~UNUSED3~]\nOutputSigType2244=Digital\nOutputCue2245=[~UNUSED3~]\nOutputSigType2245=Digital\nOutputCue2246=[~UNUSED3~]\nOutputSigType2246=Digital\nOutputCue2247=[~UNUSED3~]\nOutputSigType2247=Digital\nOutputCue2248=[~UNUSED3~]\nOutputSigType2248=Digital\nOutputCue2249=[~UNUSED3~]\nOutputSigType2249=Digital\nOutputCue2250=[~UNUSED3~]\nOutputSigType2250=Digital\nOutputCue2251=[~UNUSED3~]\nOutputSigType2251=Digital\nOutputCue2252=[~UNUSED3~]\nOutputSigType2252=Digital\nOutputCue2253=[~UNUSED3~]\nOutputSigType2253=Digital\nOutputCue2254=[~UNUSED3~]\nOutputSigType2254=Digital\nOutputCue2255=[~UNUSED3~]\nOutputSigType2255=Digital\nOutputCue2256=[~UNUSED3~]\nOutputSigType2256=Digital\nOutputCue2257=[~UNUSED3~]\nOutputSigType2257=Digital\nOutputCue2258=[~UNUSED3~]\nOutputSigType2258=Digital\nOutputCue2259=[~UNUSED3~]\nOutputSigType2259=Digital\nOutputCue2260=[~UNUSED3~]\nOutputSigType2260=Digital\nOutputCue2261=[~UNUSED3~]\nOutputSigType2261=Digital\nOutputCue2262=[~UNUSED3~]\nOutputSigType2262=Digital\nOutputCue2263=[~UNUSED3~]\nOutputSigType2263=Digital\nOutputCue2264=[~UNUSED3~]\nOutputSigType2264=Digital\nOutputCue2265=[~UNUSED3~]\nOutputSigType2265=Digital\nOutputCue2266=[~UNUSED3~]\nOutputSigType2266=Digital\nOutputCue2267=[~UNUSED3~]\nOutputSigType2267=Digital\nOutputCue2268=[~UNUSED3~]\nOutputSigType2268=Digital\nOutputCue2269=[~UNUSED2~]\nOutputSigType2269=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED3~]\nOutputSigType4026=Digital\nOutputCue4027=[~UNUSED3~]\nOutputSigType4027=Digital\nOutputCue4028=[~UNUSED3~]\nOutputSigType4028=Digital\nOutputCue4029=[~UNUSED3~]\nOutputSigType4029=Digital\nOutputCue4030=[~UNUSED3~]\nOutputSigType4030=Digital\nOutputCue4031=[~UNUSED3~]\nOutputSigType4031=Digital\nOutputCue4032=[~UNUSED3~]\nOutputSigType4032=Digital\nOutputCue4033=[~UNUSED3~]\nOutputSigType4033=Digital\nOutputCue4034=[~UNUSED3~]\nOutputSigType4034=Digital\nOutputCue4035=[~UNUSED3~]\nOutputSigType4035=Digital\nOutputCue4036=[~UNUSED3~]\nOutputSigType4036=Digital\nOutputCue4037=[~UNUSED3~]\nOutputSigType4037=Digital\nOutputCue4038=[~UNUSED3~]\nOutputSigType4038=Digital\nOutputCue4039=[~UNUSED3~]\nOutputSigType4039=Digital\nOutputCue4040=[~UNUSED3~]\nOutputSigType4040=Digital\nOutputCue4041=[~UNUSED3~]\nOutputSigType4041=Digital\nOutputCue4042=[~UNUSED3~]\nOutputSigType4042=Digital\nOutputCue4043=[~UNUSED3~]\nOutputSigType4043=Digital\nOutputCue4044=[~UNUSED3~]\nOutputSigType4044=Digital\nOutputCue4045=[~UNUSED3~]\nOutputSigType4045=Digital\nOutputCue4046=[~UNUSED3~]\nOutputSigType4046=Digital\nOutputCue4047=[~UNUSED3~]\nOutputSigType4047=Digital\nOutputCue4048=[~UNUSED3~]\nOutputSigType4048=Digital\nOutputCue4049=[~UNUSED3~]\nOutputSigType4049=Digital\nOutputCue4050=[~UNUSED3~]\nOutputSigType4050=Digital\nOutputCue4051=[~UNUSED3~]\nOutputSigType4051=Digital\nOutputCue4052=[~UNUSED3~]\nOutputSigType4052=Digital\nOutputCue4053=[~UNUSED3~]\nOutputSigType4053=Digital\nOutputCue4054=[~UNUSED3~]\nOutputSigType4054=Digital\nOutputCue4055=[~UNUSED3~]\nOutputSigType4055=Digital\nOutputCue4056=[~UNUSED3~]\nOutputSigType4056=Digital\nOutputCue4057=[~UNUSED3~]\nOutputSigType4057=Digital\nOutputCue4058=[~UNUSED3~]\nOutputSigType4058=Digital\nOutputCue4059=[~UNUSED3~]\nOutputSigType4059=Digital\nOutputCue4060=[~UNUSED3~]\nOutputSigType4060=Digital\nOutputCue4061=[~UNUSED3~]\nOutputSigType4061=Digital\nOutputCue4062=[~UNUSED3~]\nOutputSigType4062=Digital\nOutputCue4063=[~UNUSED3~]\nOutputSigType4063=Digital\nOutputCue4064=[~UNUSED3~]\nOutputSigType4064=Digital\nOutputCue4065=[~UNUSED3~]\nOutputSigType4065=Digital\nOutputCue4066=[~UNUSED3~]\nOutputSigType4066=Digital\nOutputCue4067=[~UNUSED3~]\nOutputSigType4067=Digital\nOutputCue4068=[~UNUSED3~]\nOutputSigType4068=Digital\nOutputCue4069=[~UNUSED3~]\nOutputSigType4069=Digital\nOutputCue4070=[~UNUSED3~]\nOutputSigType4070=Digital\nOutputCue4071=[~UNUSED3~]\nOutputSigType4071=Digital\nOutputCue4072=[~UNUSED3~]\nOutputSigType4072=Digital\nOutputCue4073=[~UNUSED3~]\nOutputSigType4073=Digital\nOutputCue4074=[~UNUSED3~]\nOutputSigType4074=Digital\nOutputCue4075=[~UNUSED3~]\nOutputSigType4075=Digital\nOutputCue4076=[~UNUSED3~]\nOutputSigType4076=Digital\nOutputCue4077=[~UNUSED3~]\nOutputSigType4077=Digital\nOutputCue4078=[~UNUSED3~]\nOutputSigType4078=Digital\nOutputCue4079=[~UNUSED3~]\nOutputSigType4079=Digital\nOutputCue4080=[~UNUSED3~]\nOutputSigType4080=Digital\nOutputCue4081=[~UNUSED3~]\nOutputSigType4081=Digital\nOutputCue4082=[~UNUSED3~]\nOutputSigType4082=Digital\nOutputCue4083=[~UNUSED3~]\nOutputSigType4083=Digital\nOutputCue4084=[~UNUSED3~]\nOutputSigType4084=Digital\nOutputCue4085=[~UNUSED3~]\nOutputSigType4085=Digital\nOutputCue4086=[~UNUSED3~]\nOutputSigType4086=Digital\nOutputCue4087=[~UNUSED3~]\nOutputSigType4087=Digital\nOutputCue4088=[~UNUSED3~]\nOutputSigType4088=Digital\nOutputCue4089=[~UNUSED3~]\nOutputSigType4089=Digital\nOutputCue4090=[~UNUSED3~]\nOutputSigType4090=Digital\nOutputCue4091=[~UNUSED3~]\nOutputSigType4091=Digital\nOutputCue4092=[~UNUSED3~]\nOutputSigType4092=Digital\nOutputCue4093=[~UNUSED3~]\nOutputSigType4093=Digital\nOutputCue4094=[~UNUSED3~]\nOutputSigType4094=Digital\nOutputCue4095=[~UNUSED3~]\nOutputSigType4095=Digital\nOutputCue4096=[~UNUSED3~]\nOutputSigType4096=Digital\nOutputCue4097=[~UNUSED3~]\nOutputSigType4097=Digital\nOutputCue4098=[~UNUSED3~]\nOutputSigType4098=Digital\nOutputCue4099=[~UNUSED3~]\nOutputSigType4099=Digital\nOutputCue4100=[~UNUSED3~]\nOutputSigType4100=Digital\nOutputCue4101=[~UNUSED3~]\nOutputSigType4101=Digital\nOutputCue4102=[~UNUSED3~]\nOutputSigType4102=Digital\nOutputCue4103=[~UNUSED3~]\nOutputSigType4103=Digital\nOutputCue4104=[~UNUSED3~]\nOutputSigType4104=Digital\nOutputCue4105=[~UNUSED3~]\nOutputSigType4105=Digital\nOutputCue4106=[~UNUSED3~]\nOutputSigType4106=Digital\nOutputCue4107=[~UNUSED3~]\nOutputSigType4107=Digital\nOutputCue4108=[~UNUSED3~]\nOutputSigType4108=Digital\nOutputCue4109=[~UNUSED3~]\nOutputSigType4109=Digital\nOutputCue4110=[~UNUSED3~]\nOutputSigType4110=Digital\nOutputCue4111=[~UNUSED3~]\nOutputSigType4111=Digital\nOutputCue4112=[~UNUSED3~]\nOutputSigType4112=Digital\nOutputCue4113=[~UNUSED3~]\nOutputSigType4113=Digital\nOutputCue4114=[~UNUSED3~]\nOutputSigType4114=Digital\nOutputCue4115=[~UNUSED3~]\nOutputSigType4115=Digital\nOutputCue4116=[~UNUSED3~]\nOutputSigType4116=Digital\nOutputCue4117=[~UNUSED3~]\nOutputSigType4117=Digital\nOutputCue4118=[~UNUSED3~]\nOutputSigType4118=Digital\nOutputCue4119=[~UNUSED3~]\nOutputSigType4119=Digital\nOutputCue4120=[~UNUSED3~]\nOutputSigType4120=Digital\nOutputCue4121=[~UNUSED3~]\nOutputSigType4121=Digital\nOutputCue4122=[~UNUSED3~]\nOutputSigType4122=Digital\nOutputCue4123=[~UNUSED3~]\nOutputSigType4123=Digital\nOutputCue4124=[~UNUSED3~]\nOutputSigType4124=Digital\nOutputCue4125=[~UNUSED3~]\nOutputSigType4125=Digital\nOutputCue4126=[~UNUSED3~]\nOutputSigType4126=Digital\nOutputCue4127=[~UNUSED3~]\nOutputSigType4127=Digital\nOutputCue4128=[~UNUSED3~]\nOutputSigType4128=Digital\nOutputCue4129=[~UNUSED3~]\nOutputSigType4129=Digital\nOutputCue4130=[~UNUSED3~]\nOutputSigType4130=Digital\nOutputCue4131=[~UNUSED3~]\nOutputSigType4131=Digital\nOutputCue4132=[~UNUSED3~]\nOutputSigType4132=Digital\nOutputCue4133=[~UNUSED3~]\nOutputSigType4133=Digital\nOutputCue4134=[~UNUSED3~]\nOutputSigType4134=Digital\nOutputCue4135=[~UNUSED3~]\nOutputSigType4135=Digital\nOutputCue4136=[~UNUSED3~]\nOutputSigType4136=Digital\nOutputCue4137=[~UNUSED3~]\nOutputSigType4137=Digital\nOutputCue4138=[~UNUSED3~]\nOutputSigType4138=Digital\nOutputCue4139=[~UNUSED3~]\nOutputSigType4139=Digital\nOutputCue4140=[~UNUSED3~]\nOutputSigType4140=Digital\nOutputCue4141=[~UNUSED3~]\nOutputSigType4141=Digital\nOutputCue4142=[~UNUSED3~]\nOutputSigType4142=Digital\nOutputCue4143=[~UNUSED3~]\nOutputSigType4143=Digital\nOutputCue4144=[~UNUSED3~]\nOutputSigType4144=Digital\nOutputCue4145=[~UNUSED3~]\nOutputSigType4145=Digital\nOutputCue4146=[~UNUSED3~]\nOutputSigType4146=Digital\nOutputCue4147=[~UNUSED3~]\nOutputSigType4147=Digital\nOutputCue4148=[~UNUSED3~]\nOutputSigType4148=Digital\nOutputCue4149=[~UNUSED3~]\nOutputSigType4149=Digital\nOutputCue4150=[~UNUSED3~]\nOutputSigType4150=Digital\nOutputCue4151=[~UNUSED3~]\nOutputSigType4151=Digital\nOutputCue4152=[~UNUSED3~]\nOutputSigType4152=Digital\nOutputCue4153=[~UNUSED3~]\nOutputSigType4153=Digital\nOutputCue4154=[~UNUSED3~]\nOutputSigType4154=Digital\nOutputCue4155=[~UNUSED3~]\nOutputSigType4155=Digital\nOutputCue4156=[~UNUSED3~]\nOutputSigType4156=Digital\nOutputCue4157=[~UNUSED3~]\nOutputSigType4157=Digital\nOutputCue4158=[~UNUSED3~]\nOutputSigType4158=Digital\nOutputCue4159=[~UNUSED3~]\nOutputSigType4159=Digital\nOutputCue4160=[~UNUSED3~]\nOutputSigType4160=Digital\nOutputCue4161=[~UNUSED3~]\nOutputSigType4161=Digital\nOutputCue4162=[~UNUSED3~]\nOutputSigType4162=Digital\nOutputCue4163=[~UNUSED3~]\nOutputSigType4163=Digital\nOutputCue4164=[~UNUSED3~]\nOutputSigType4164=Digital\nOutputCue4165=[~UNUSED3~]\nOutputSigType4165=Digital\nOutputCue4166=[~UNUSED3~]\nOutputSigType4166=Digital\nOutputCue4167=[~UNUSED3~]\nOutputSigType4167=Digital\nOutputCue4168=[~UNUSED3~]\nOutputSigType4168=Digital\nOutputCue4169=[~UNUSED3~]\nOutputSigType4169=Digital\nOutputCue4170=[~UNUSED3~]\nOutputSigType4170=Digital\nOutputCue4171=[~UNUSED3~]\nOutputSigType4171=Digital\nOutputCue4172=[~UNUSED3~]\nOutputSigType4172=Digital\nOutputCue4173=[~UNUSED3~]\nOutputSigType4173=Digital\nOutputCue4174=[~UNUSED3~]\nOutputSigType4174=Digital\nOutputCue4175=[~UNUSED3~]\nOutputSigType4175=Digital\nOutputCue4176=[~UNUSED3~]\nOutputSigType4176=Digital\nOutputCue4177=[~UNUSED3~]\nOutputSigType4177=Digital\nOutputCue4178=[~UNUSED3~]\nOutputSigType4178=Digital\nOutputCue4179=[~UNUSED3~]\nOutputSigType4179=Digital\nOutputCue4180=[~UNUSED3~]\nOutputSigType4180=Digital\nOutputCue4181=[~UNUSED3~]\nOutputSigType4181=Digital\nOutputCue4182=[~UNUSED3~]\nOutputSigType4182=Digital\nOutputCue4183=[~UNUSED3~]\nOutputSigType4183=Digital\nOutputCue4184=[~UNUSED3~]\nOutputSigType4184=Digital\nOutputCue4185=[~UNUSED3~]\nOutputSigType4185=Digital\nOutputCue4186=[~UNUSED3~]\nOutputSigType4186=Digital\nOutputCue4187=[~UNUSED3~]\nOutputSigType4187=Digital\nOutputCue4188=[~UNUSED3~]\nOutputSigType4188=Digital\nOutputCue4189=[~UNUSED3~]\nOutputSigType4189=Digital\nOutputCue4190=[~UNUSED3~]\nOutputSigType4190=Digital\nOutputCue4191=[~UNUSED3~]\nOutputSigType4191=Digital\nOutputCue4192=[~UNUSED3~]\nOutputSigType4192=Digital\nOutputCue4193=[~UNUSED3~]\nOutputSigType4193=Digital\nOutputCue4194=[~UNUSED3~]\nOutputSigType4194=Digital\nOutputCue4195=[~UNUSED3~]\nOutputSigType4195=Digital\nOutputCue4196=[~UNUSED3~]\nOutputSigType4196=Digital\nOutputCue4197=[~UNUSED3~]\nOutputSigType4197=Digital\nOutputCue4198=[~UNUSED3~]\nOutputSigType4198=Digital\nOutputCue4199=[~UNUSED3~]\nOutputSigType4199=Digital\nOutputCue4200=[~UNUSED3~]\nOutputSigType4200=Digital\nOutputCue4201=[~UNUSED3~]\nOutputSigType4201=Digital\nOutputCue4202=[~UNUSED3~]\nOutputSigType4202=Digital\nOutputCue4203=[~UNUSED3~]\nOutputSigType4203=Digital\nOutputCue4204=[~UNUSED3~]\nOutputSigType4204=Digital\nOutputCue4205=[~UNUSED3~]\nOutputSigType4205=Digital\nOutputCue4206=[~UNUSED3~]\nOutputSigType4206=Digital\nOutputCue4207=[~UNUSED3~]\nOutputSigType4207=Digital\nOutputCue4208=[~UNUSED3~]\nOutputSigType4208=Digital\nOutputCue4209=[~UNUSED3~]\nOutputSigType4209=Digital\nOutputCue4210=[~UNUSED3~]\nOutputSigType4210=Digital\nOutputCue4211=[~UNUSED3~]\nOutputSigType4211=Digital\nOutputCue4212=[~UNUSED3~]\nOutputSigType4212=Digital\nOutputCue4213=[~UNUSED3~]\nOutputSigType4213=Digital\nOutputCue4214=[~UNUSED3~]\nOutputSigType4214=Digital\nOutputCue4215=[~UNUSED3~]\nOutputSigType4215=Digital\nOutputCue4216=[~UNUSED3~]\nOutputSigType4216=Digital\nOutputCue4217=[~UNUSED3~]\nOutputSigType4217=Digital\nOutputCue4218=[~UNUSED3~]\nOutputSigType4218=Digital\nOutputCue4219=[~UNUSED3~]\nOutputSigType4219=Digital\nOutputCue4220=[~UNUSED3~]\nOutputSigType4220=Digital\nOutputCue4221=[~UNUSED3~]\nOutputSigType4221=Digital\nOutputCue4222=[~UNUSED3~]\nOutputSigType4222=Digital\nOutputCue4223=[~UNUSED3~]\nOutputSigType4223=Digital\nOutputCue4224=[~UNUSED3~]\nOutputSigType4224=Digital\nOutputCue4225=[~UNUSED3~]\nOutputSigType4225=Digital\nOutputCue4226=[~UNUSED3~]\nOutputSigType4226=Digital\nOutputCue4227=[~UNUSED3~]\nOutputSigType4227=Digital\nOutputCue4228=[~UNUSED3~]\nOutputSigType4228=Digital\nOutputCue4229=[~UNUSED3~]\nOutputSigType4229=Digital\nOutputCue4230=[~UNUSED3~]\nOutputSigType4230=Digital\nOutputCue4231=[~UNUSED3~]\nOutputSigType4231=Digital\nOutputCue4232=[~UNUSED3~]\nOutputSigType4232=Digital\nOutputCue4233=[~UNUSED3~]\nOutputSigType4233=Digital\nOutputCue4234=[~UNUSED3~]\nOutputSigType4234=Digital\nOutputCue4235=[~UNUSED3~]\nOutputSigType4235=Digital\nOutputCue4236=[~UNUSED3~]\nOutputSigType4236=Digital\nOutputCue4237=[~UNUSED3~]\nOutputSigType4237=Digital\nOutputCue4238=[~UNUSED3~]\nOutputSigType4238=Digital\nOutputCue4239=[~UNUSED3~]\nOutputSigType4239=Digital\nOutputCue4240=[~UNUSED3~]\nOutputSigType4240=Digital\nOutputCue4241=[~UNUSED3~]\nOutputSigType4241=Digital\nOutputCue4242=[~UNUSED3~]\nOutputSigType4242=Digital\nOutputCue4243=[~UNUSED3~]\nOutputSigType4243=Digital\nOutputCue4244=[~UNUSED3~]\nOutputSigType4244=Digital\nOutputCue4245=[~UNUSED3~]\nOutputSigType4245=Digital\nOutputCue4246=[~UNUSED3~]\nOutputSigType4246=Digital\nOutputCue4247=[~UNUSED3~]\nOutputSigType4247=Digital\nOutputCue4248=[~UNUSED3~]\nOutputSigType4248=Digital\nOutputCue4249=[~UNUSED3~]\nOutputSigType4249=Digital\nOutputCue4250=[~UNUSED3~]\nOutputSigType4250=Digital\nOutputCue4251=[~UNUSED3~]\nOutputSigType4251=Digital\nOutputCue4252=[~UNUSED3~]\nOutputSigType4252=Digital\nOutputCue4253=[~UNUSED3~]\nOutputSigType4253=Digital\nOutputCue4254=[~UNUSED3~]\nOutputSigType4254=Digital\nOutputCue4255=[~UNUSED3~]\nOutputSigType4255=Digital\nOutputCue4256=[~UNUSED3~]\nOutputSigType4256=Digital\nOutputCue4257=[~UNUSED3~]\nOutputSigType4257=Digital\nOutputCue4258=[~UNUSED3~]\nOutputSigType4258=Digital\nOutputCue4259=[~UNUSED3~]\nOutputSigType4259=Digital\nOutputCue4260=[~UNUSED3~]\nOutputSigType4260=Digital\nOutputCue4261=[~UNUSED3~]\nOutputSigType4261=Digital\nOutputCue4262=[~UNUSED3~]\nOutputSigType4262=Digital\nOutputCue4263=[~UNUSED3~]\nOutputSigType4263=Digital\nOutputCue4264=[~UNUSED3~]\nOutputSigType4264=Digital\nOutputCue4265=[~UNUSED3~]\nOutputSigType4265=Digital\nOutputCue4266=[~UNUSED3~]\nOutputSigType4266=Digital\nOutputCue4267=[~UNUSED3~]\nOutputSigType4267=Digital\nOutputCue4268=[~UNUSED3~]\nOutputSigType4268=Digital\nOutputCue4269=[~UNUSED3~]\nOutputSigType4269=Digital\nOutputCue4270=[~UNUSED3~]\nOutputSigType4270=Digital\nOutputCue4271=[~UNUSED2~]\nOutputSigType4271=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=Set Item 11 Icon Analog\nInputList2SigType22=Analog\nInputList2Cue23=Set Item 12 Icon Analog\nInputList2SigType23=Analog\nInputList2Cue24=Set Item 13 Icon Analog\nInputList2SigType24=Analog\nInputList2Cue25=Set Item 14 Icon Analog\nInputList2SigType25=Analog\nInputList2Cue26=Set Item 15 Icon Analog\nInputList2SigType26=Analog\nInputList2Cue27=Set Item 16 Icon Analog\nInputList2SigType27=Analog\nInputList2Cue28=Set Item 17 Icon Analog\nInputList2SigType28=Analog\nInputList2Cue29=Set Item 18 Icon Analog\nInputList2SigType29=Analog\nInputList2Cue30=Set Item 19 Icon Analog\nInputList2SigType30=Analog\nInputList2Cue31=Set Item 20 Icon Analog\nInputList2SigType31=Analog\nInputList2Cue32=Set Item 21 Icon Analog\nInputList2SigType32=Analog\nInputList2Cue33=Set Item 22 Icon Analog\nInputList2SigType33=Analog\nInputList2Cue34=Set Item 23 Icon Analog\nInputList2SigType34=Analog\nInputList2Cue35=Set Item 24 Icon Analog\nInputList2SigType35=Analog\nInputList2Cue36=Set Item 25 Icon Analog\nInputList2SigType36=Analog\nInputList2Cue37=Set Item 26 Icon Analog\nInputList2SigType37=Analog\nInputList2Cue38=Set Item 27 Icon Analog\nInputList2SigType38=Analog\nInputList2Cue39=Set Item 28 Icon Analog\nInputList2SigType39=Analog\nInputList2Cue40=Set Item 29 Icon Analog\nInputList2SigType40=Analog\nInputList2Cue41=Set Item 30 Icon Analog\nInputList2SigType41=Analog\nInputList2Cue42=Set Item 31 Icon Analog\nInputList2SigType42=Analog\nInputList2Cue43=Set Item 32 Icon Analog\nInputList2SigType43=Analog\nInputList2Cue44=Set Item 33 Icon Analog\nInputList2SigType44=Analog\nInputList2Cue45=Set Item 34 Icon Analog\nInputList2SigType45=Analog\nInputList2Cue46=Set Item 35 Icon Analog\nInputList2SigType46=Analog\nInputList2Cue47=Set Item 36 Icon Analog\nInputList2SigType47=Analog\nInputList2Cue48=Set Item 37 Icon Analog\nInputList2SigType48=Analog\nInputList2Cue49=Set Item 38 Icon Analog\nInputList2SigType49=Analog\nInputList2Cue50=Set Item 39 Icon Analog\nInputList2SigType50=Analog\nInputList2Cue51=Set Item 40 Icon Analog\nInputList2SigType51=Analog\nInputList2Cue52=Set Item 41 Icon Analog\nInputList2SigType52=Analog\nInputList2Cue53=Set Item 42 Icon Analog\nInputList2SigType53=Analog\nInputList2Cue54=Set Item 43 Icon Analog\nInputList2SigType54=Analog\nInputList2Cue55=Set Item 44 Icon Analog\nInputList2SigType55=Analog\nInputList2Cue56=Set Item 45 Icon Analog\nInputList2SigType56=Analog\nInputList2Cue57=Set Item 46 Icon Analog\nInputList2SigType57=Analog\nInputList2Cue58=Set Item 47 Icon Analog\nInputList2SigType58=Analog\nInputList2Cue59=Set Item 48 Icon Analog\nInputList2SigType59=Analog\nInputList2Cue60=Set Item 49 Icon Analog\nInputList2SigType60=Analog\nInputList2Cue61=Set Item 50 Icon Analog\nInputList2SigType61=Analog\nInputList2Cue62=Set Item 51 Icon Analog\nInputList2SigType62=Analog\nInputList2Cue63=Set Item 52 Icon Analog\nInputList2SigType63=Analog\nInputList2Cue64=Set Item 53 Icon Analog\nInputList2SigType64=Analog\nInputList2Cue65=Set Item 54 Icon Analog\nInputList2SigType65=Analog\nInputList2Cue66=Set Item 55 Icon Analog\nInputList2SigType66=Analog\nInputList2Cue67=Set Item 56 Icon Analog\nInputList2SigType67=Analog\nInputList2Cue68=Set Item 57 Icon Analog\nInputList2SigType68=Analog\nInputList2Cue69=Set Item 58 Icon Analog\nInputList2SigType69=Analog\nInputList2Cue70=Set Item 59 Icon Analog\nInputList2SigType70=Analog\nInputList2Cue71=Set Item 60 Icon Analog\nInputList2SigType71=Analog\nInputList2Cue72=Set Item 61 Icon Analog\nInputList2SigType72=Analog\nInputList2Cue73=Set Item 62 Icon Analog\nInputList2SigType73=Analog\nInputList2Cue74=Set Item 63 Icon Analog\nInputList2SigType74=Analog\nInputList2Cue75=Set Item 64 Icon Analog\nInputList2SigType75=Analog\nInputList2Cue76=Set Item 65 Icon Analog\nInputList2SigType76=Analog\nInputList2Cue77=Set Item 66 Icon Analog\nInputList2SigType77=Analog\nInputList2Cue78=Set Item 67 Icon Analog\nInputList2SigType78=Analog\nInputList2Cue79=Set Item 68 Icon Analog\nInputList2SigType79=Analog\nInputList2Cue80=Set Item 69 Icon Analog\nInputList2SigType80=Analog\nInputList2Cue81=Set Item 70 Icon Analog\nInputList2SigType81=Analog\nInputList2Cue82=Set Item 71 Icon Analog\nInputList2SigType82=Analog\nInputList2Cue83=Set Item 72 Icon Analog\nInputList2SigType83=Analog\nInputList2Cue84=Set Item 73 Icon Analog\nInputList2SigType84=Analog\nInputList2Cue85=Set Item 74 Icon Analog\nInputList2SigType85=Analog\nInputList2Cue86=Set Item 75 Icon Analog\nInputList2SigType86=Analog\nInputList2Cue87=Set Item 76 Icon Analog\nInputList2SigType87=Analog\nInputList2Cue88=Set Item 77 Icon Analog\nInputList2SigType88=Analog\nInputList2Cue89=Set Item 78 Icon Analog\nInputList2SigType89=Analog\nInputList2Cue90=Set Item 79 Icon Analog\nInputList2SigType90=Analog\nInputList2Cue91=Set Item 80 Icon Analog\nInputList2SigType91=Analog\nInputList2Cue92=Set Item 81 Icon Analog\nInputList2SigType92=Analog\nInputList2Cue93=Set Item 82 Icon Analog\nInputList2SigType93=Analog\nInputList2Cue94=Set Item 83 Icon Analog\nInputList2SigType94=Analog\nInputList2Cue95=Set Item 84 Icon Analog\nInputList2SigType95=Analog\nInputList2Cue96=Set Item 85 Icon Analog\nInputList2SigType96=Analog\nInputList2Cue97=Set Item 86 Icon Analog\nInputList2SigType97=Analog\nInputList2Cue98=Set Item 87 Icon Analog\nInputList2SigType98=Analog\nInputList2Cue99=Set Item 88 Icon Analog\nInputList2SigType99=Analog\nInputList2Cue100=Set Item 89 Icon Analog\nInputList2SigType100=Analog\nInputList2Cue101=Set Item 90 Icon Analog\nInputList2SigType101=Analog\nInputList2Cue102=Set Item 91 Icon Analog\nInputList2SigType102=Analog\nInputList2Cue103=Set Item 92 Icon Analog\nInputList2SigType103=Analog\nInputList2Cue104=Set Item 93 Icon Analog\nInputList2SigType104=Analog\nInputList2Cue105=Set Item 94 Icon Analog\nInputList2SigType105=Analog\nInputList2Cue106=Set Item 95 Icon Analog\nInputList2SigType106=Analog\nInputList2Cue107=Set Item 96 Icon Analog\nInputList2SigType107=Analog\nInputList2Cue108=Set Item 97 Icon Analog\nInputList2SigType108=Analog\nInputList2Cue109=Set Item 98 Icon Analog\nInputList2SigType109=Analog\nInputList2Cue110=Set Item 99 Icon Analog\nInputList2SigType110=Analog\nInputList2Cue111=Set Item 100 Icon Analog\nInputList2SigType111=Analog\nInputList2Cue112=Set Item 101 Icon Analog\nInputList2SigType112=Analog\nInputList2Cue113=Set Item 102 Icon Analog\nInputList2SigType113=Analog\nInputList2Cue114=Set Item 103 Icon Analog\nInputList2SigType114=Analog\nInputList2Cue115=Set Item 104 Icon Analog\nInputList2SigType115=Analog\nInputList2Cue116=Set Item 105 Icon Analog\nInputList2SigType116=Analog\nInputList2Cue117=Set Item 106 Icon Analog\nInputList2SigType117=Analog\nInputList2Cue118=Set Item 107 Icon Analog\nInputList2SigType118=Analog\nInputList2Cue119=Set Item 108 Icon Analog\nInputList2SigType119=Analog\nInputList2Cue120=Set Item 109 Icon Analog\nInputList2SigType120=Analog\nInputList2Cue121=Set Item 110 Icon Analog\nInputList2SigType121=Analog\nInputList2Cue122=Set Item 111 Icon Analog\nInputList2SigType122=Analog\nInputList2Cue123=Set Item 112 Icon Analog\nInputList2SigType123=Analog\nInputList2Cue124=Set Item 113 Icon Analog\nInputList2SigType124=Analog\nInputList2Cue125=Set Item 114 Icon Analog\nInputList2SigType125=Analog\nInputList2Cue126=Set Item 115 Icon Analog\nInputList2SigType126=Analog\nInputList2Cue127=Set Item 116 Icon Analog\nInputList2SigType127=Analog\nInputList2Cue128=Set Item 117 Icon Analog\nInputList2SigType128=Analog\nInputList2Cue129=Set Item 118 Icon Analog\nInputList2SigType129=Analog\nInputList2Cue130=Set Item 119 Icon Analog\nInputList2SigType130=Analog\nInputList2Cue131=Set Item 120 Icon Analog\nInputList2SigType131=Analog\nInputList2Cue132=Set Item 121 Icon Analog\nInputList2SigType132=Analog\nInputList2Cue133=Set Item 122 Icon Analog\nInputList2SigType133=Analog\nInputList2Cue134=Set Item 123 Icon Analog\nInputList2SigType134=Analog\nInputList2Cue135=Set Item 124 Icon Analog\nInputList2SigType135=Analog\nInputList2Cue136=Set Item 125 Icon Analog\nInputList2SigType136=Analog\nInputList2Cue137=Set Item 126 Icon Analog\nInputList2SigType137=Analog\nInputList2Cue138=Set Item 127 Icon Analog\nInputList2SigType138=Analog\nInputList2Cue139=Set Item 128 Icon Analog\nInputList2SigType139=Analog\nInputList2Cue140=Set Item 129 Icon Analog\nInputList2SigType140=Analog\nInputList2Cue141=Set Item 130 Icon Analog\nInputList2SigType141=Analog\nInputList2Cue142=Set Item 131 Icon Analog\nInputList2SigType142=Analog\nInputList2Cue143=Set Item 132 Icon Analog\nInputList2SigType143=Analog\nInputList2Cue144=Set Item 133 Icon Analog\nInputList2SigType144=Analog\nInputList2Cue145=Set Item 134 Icon Analog\nInputList2SigType145=Analog\nInputList2Cue146=Set Item 135 Icon Analog\nInputList2SigType146=Analog\nInputList2Cue147=Set Item 136 Icon Analog\nInputList2SigType147=Analog\nInputList2Cue148=Set Item 137 Icon Analog\nInputList2SigType148=Analog\nInputList2Cue149=Set Item 138 Icon Analog\nInputList2SigType149=Analog\nInputList2Cue150=Set Item 139 Icon Analog\nInputList2SigType150=Analog\nInputList2Cue151=Set Item 140 Icon Analog\nInputList2SigType151=Analog\nInputList2Cue152=Set Item 141 Icon Analog\nInputList2SigType152=Analog\nInputList2Cue153=Set Item 142 Icon Analog\nInputList2SigType153=Analog\nInputList2Cue154=Set Item 143 Icon Analog\nInputList2SigType154=Analog\nInputList2Cue155=Set Item 144 Icon Analog\nInputList2SigType155=Analog\nInputList2Cue156=Set Item 145 Icon Analog\nInputList2SigType156=Analog\nInputList2Cue157=Set Item 146 Icon Analog\nInputList2SigType157=Analog\nInputList2Cue158=Set Item 147 Icon Analog\nInputList2SigType158=Analog\nInputList2Cue159=Set Item 148 Icon Analog\nInputList2SigType159=Analog\nInputList2Cue160=Set Item 149 Icon Analog\nInputList2SigType160=Analog\nInputList2Cue161=Set Item 150 Icon Analog\nInputList2SigType161=Analog\nInputList2Cue162=Set Item 151 Icon Analog\nInputList2SigType162=Analog\nInputList2Cue163=Set Item 152 Icon Analog\nInputList2SigType163=Analog\nInputList2Cue164=Set Item 153 Icon Analog\nInputList2SigType164=Analog\nInputList2Cue165=Set Item 154 Icon Analog\nInputList2SigType165=Analog\nInputList2Cue166=Set Item 155 Icon Analog\nInputList2SigType166=Analog\nInputList2Cue167=Set Item 156 Icon Analog\nInputList2SigType167=Analog\nInputList2Cue168=Set Item 157 Icon Analog\nInputList2SigType168=Analog\nInputList2Cue169=Set Item 158 Icon Analog\nInputList2SigType169=Analog\nInputList2Cue170=Set Item 159 Icon Analog\nInputList2SigType170=Analog\nInputList2Cue171=Set Item 160 Icon Analog\nInputList2SigType171=Analog\nInputList2Cue172=Set Item 161 Icon Analog\nInputList2SigType172=Analog\nInputList2Cue173=Set Item 162 Icon Analog\nInputList2SigType173=Analog\nInputList2Cue174=Set Item 163 Icon Analog\nInputList2SigType174=Analog\nInputList2Cue175=Set Item 164 Icon Analog\nInputList2SigType175=Analog\nInputList2Cue176=Set Item 165 Icon Analog\nInputList2SigType176=Analog\nInputList2Cue177=Set Item 166 Icon Analog\nInputList2SigType177=Analog\nInputList2Cue178=Set Item 167 Icon Analog\nInputList2SigType178=Analog\nInputList2Cue179=Set Item 168 Icon Analog\nInputList2SigType179=Analog\nInputList2Cue180=Set Item 169 Icon Analog\nInputList2SigType180=Analog\nInputList2Cue181=Set Item 170 Icon Analog\nInputList2SigType181=Analog\nInputList2Cue182=Set Item 171 Icon Analog\nInputList2SigType182=Analog\nInputList2Cue183=Set Item 172 Icon Analog\nInputList2SigType183=Analog\nInputList2Cue184=Set Item 173 Icon Analog\nInputList2SigType184=Analog\nInputList2Cue185=Set Item 174 Icon Analog\nInputList2SigType185=Analog\nInputList2Cue186=Set Item 175 Icon Analog\nInputList2SigType186=Analog\nInputList2Cue187=Set Item 176 Icon Analog\nInputList2SigType187=Analog\nInputList2Cue188=Set Item 177 Icon Analog\nInputList2SigType188=Analog\nInputList2Cue189=Set Item 178 Icon Analog\nInputList2SigType189=Analog\nInputList2Cue190=Set Item 179 Icon Analog\nInputList2SigType190=Analog\nInputList2Cue191=Set Item 180 Icon Analog\nInputList2SigType191=Analog\nInputList2Cue192=Set Item 181 Icon Analog\nInputList2SigType192=Analog\nInputList2Cue193=Set Item 182 Icon Analog\nInputList2SigType193=Analog\nInputList2Cue194=Set Item 183 Icon Analog\nInputList2SigType194=Analog\nInputList2Cue195=Set Item 184 Icon Analog\nInputList2SigType195=Analog\nInputList2Cue196=Set Item 185 Icon Analog\nInputList2SigType196=Analog\nInputList2Cue197=Set Item 186 Icon Analog\nInputList2SigType197=Analog\nInputList2Cue198=Set Item 187 Icon Analog\nInputList2SigType198=Analog\nInputList2Cue199=Set Item 188 Icon Analog\nInputList2SigType199=Analog\nInputList2Cue200=Set Item 189 Icon Analog\nInputList2SigType200=Analog\nInputList2Cue201=Set Item 190 Icon Analog\nInputList2SigType201=Analog\nInputList2Cue202=Set Item 191 Icon Analog\nInputList2SigType202=Analog\nInputList2Cue203=Set Item 192 Icon Analog\nInputList2SigType203=Analog\nInputList2Cue204=Set Item 193 Icon Analog\nInputList2SigType204=Analog\nInputList2Cue205=Set Item 194 Icon Analog\nInputList2SigType205=Analog\nInputList2Cue206=Set Item 195 Icon Analog\nInputList2SigType206=Analog\nInputList2Cue207=Set Item 196 Icon Analog\nInputList2SigType207=Analog\nInputList2Cue208=Set Item 197 Icon Analog\nInputList2SigType208=Analog\nInputList2Cue209=Set Item 198 Icon Analog\nInputList2SigType209=Analog\nInputList2Cue210=Set Item 199 Icon Analog\nInputList2SigType210=Analog\nInputList2Cue211=Set Item 200 Icon Analog\nInputList2SigType211=Analog\nInputList2Cue212=Set Item 201 Icon Analog\nInputList2SigType212=Analog\nInputList2Cue213=Set Item 202 Icon Analog\nInputList2SigType213=Analog\nInputList2Cue214=Set Item 203 Icon Analog\nInputList2SigType214=Analog\nInputList2Cue215=Set Item 204 Icon Analog\nInputList2SigType215=Analog\nInputList2Cue216=Set Item 205 Icon Analog\nInputList2SigType216=Analog\nInputList2Cue217=Set Item 206 Icon Analog\nInputList2SigType217=Analog\nInputList2Cue218=Set Item 207 Icon Analog\nInputList2SigType218=Analog\nInputList2Cue219=Set Item 208 Icon Analog\nInputList2SigType219=Analog\nInputList2Cue220=Set Item 209 Icon Analog\nInputList2SigType220=Analog\nInputList2Cue221=Set Item 210 Icon Analog\nInputList2SigType221=Analog\nInputList2Cue222=Set Item 211 Icon Analog\nInputList2SigType222=Analog\nInputList2Cue223=Set Item 212 Icon Analog\nInputList2SigType223=Analog\nInputList2Cue224=Set Item 213 Icon Analog\nInputList2SigType224=Analog\nInputList2Cue225=Set Item 214 Icon Analog\nInputList2SigType225=Analog\nInputList2Cue226=Set Item 215 Icon Analog\nInputList2SigType226=Analog\nInputList2Cue227=Set Item 216 Icon Analog\nInputList2SigType227=Analog\nInputList2Cue228=Set Item 217 Icon Analog\nInputList2SigType228=Analog\nInputList2Cue229=Set Item 218 Icon Analog\nInputList2SigType229=Analog\nInputList2Cue230=Set Item 219 Icon Analog\nInputList2SigType230=Analog\nInputList2Cue231=Set Item 220 Icon Analog\nInputList2SigType231=Analog\nInputList2Cue232=Set Item 221 Icon Analog\nInputList2SigType232=Analog\nInputList2Cue233=Set Item 222 Icon Analog\nInputList2SigType233=Analog\nInputList2Cue234=Set Item 223 Icon Analog\nInputList2SigType234=Analog\nInputList2Cue235=Set Item 224 Icon Analog\nInputList2SigType235=Analog\nInputList2Cue236=Set Item 225 Icon Analog\nInputList2SigType236=Analog\nInputList2Cue237=Set Item 226 Icon Analog\nInputList2SigType237=Analog\nInputList2Cue238=Set Item 227 Icon Analog\nInputList2SigType238=Analog\nInputList2Cue239=Set Item 228 Icon Analog\nInputList2SigType239=Analog\nInputList2Cue240=Set Item 229 Icon Analog\nInputList2SigType240=Analog\nInputList2Cue241=Set Item 230 Icon Analog\nInputList2SigType241=Analog\nInputList2Cue242=Set Item 231 Icon Analog\nInputList2SigType242=Analog\nInputList2Cue243=Set Item 232 Icon Analog\nInputList2SigType243=Analog\nInputList2Cue244=Set Item 233 Icon Analog\nInputList2SigType244=Analog\nInputList2Cue245=Set Item 234 Icon Analog\nInputList2SigType245=Analog\nInputList2Cue246=Set Item 235 Icon Analog\nInputList2SigType246=Analog\nInputList2Cue247=Set Item 236 Icon Analog\nInputList2SigType247=Analog\nInputList2Cue248=Set Item 237 Icon Analog\nInputList2SigType248=Analog\nInputList2Cue249=Set Item 238 Icon Analog\nInputList2SigType249=Analog\nInputList2Cue250=Set Item 239 Icon Analog\nInputList2SigType250=Analog\nInputList2Cue251=Set Item 240 Icon Analog\nInputList2SigType251=Analog\nInputList2Cue252=Set Item 241 Icon Analog\nInputList2SigType252=Analog\nInputList2Cue253=Set Item 242 Icon Analog\nInputList2SigType253=Analog\nInputList2Cue254=Set Item 243 Icon Analog\nInputList2SigType254=Analog\nInputList2Cue255=Set Item 244 Icon Analog\nInputList2SigType255=Analog\nInputList2Cue256=Set Item 245 Icon Analog\nInputList2SigType256=Analog\nInputList2Cue257=Set Item 246 Icon Analog\nInputList2SigType257=Analog\nInputList2Cue258=Set Item 247 Icon Analog\nInputList2SigType258=Analog\nInputList2Cue259=Set Item 248 Icon Analog\nInputList2SigType259=Analog\nInputList2Cue260=Set Item 249 Icon Analog\nInputList2SigType260=Analog\nInputList2Cue261=Set Item 250 Icon Analog\nInputList2SigType261=Analog\nInputList2Cue262=Set Item 251 Icon Analog\nInputList2SigType262=Analog\nInputList2Cue263=Set Item 252 Icon Analog\nInputList2SigType263=Analog\nInputList2Cue264=Set Item 253 Icon Analog\nInputList2SigType264=Analog\nInputList2Cue265=Set Item 254 Icon Analog\nInputList2SigType265=Analog\nInputList2Cue266=Set Item 255 Icon Analog\nInputList2SigType266=Analog\nInputList2Cue267=[~UNUSED2~]\nInputList2SigType267=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED3~]\nOutputList2SigType22=Analog\nOutputList2Cue23=[~UNUSED3~]\nOutputList2SigType23=Analog\nOutputList2Cue24=[~UNUSED3~]\nOutputList2SigType24=Analog\nOutputList2Cue25=[~UNUSED3~]\nOutputList2SigType25=Analog\nOutputList2Cue26=[~UNUSED3~]\nOutputList2SigType26=Analog\nOutputList2Cue27=[~UNUSED3~]\nOutputList2SigType27=Analog\nOutputList2Cue28=[~UNUSED3~]\nOutputList2SigType28=Analog\nOutputList2Cue29=[~UNUSED3~]\nOutputList2SigType29=Analog\nOutputList2Cue30=[~UNUSED3~]\nOutputList2SigType30=Analog\nOutputList2Cue31=[~UNUSED3~]\nOutputList2SigType31=Analog\nOutputList2Cue32=[~UNUSED3~]\nOutputList2SigType32=Analog\nOutputList2Cue33=[~UNUSED3~]\nOutputList2SigType33=Analog\nOutputList2Cue34=[~UNUSED3~]\nOutputList2SigType34=Analog\nOutputList2Cue35=[~UNUSED3~]\nOutputList2SigType35=Analog\nOutputList2Cue36=[~UNUSED3~]\nOutputList2SigType36=Analog\nOutputList2Cue37=[~UNUSED3~]\nOutputList2SigType37=Analog\nOutputList2Cue38=[~UNUSED3~]\nOutputList2SigType38=Analog\nOutputList2Cue39=[~UNUSED3~]\nOutputList2SigType39=Analog\nOutputList2Cue40=[~UNUSED3~]\nOutputList2SigType40=Analog\nOutputList2Cue41=[~UNUSED3~]\nOutputList2SigType41=Analog\nOutputList2Cue42=[~UNUSED3~]\nOutputList2SigType42=Analog\nOutputList2Cue43=[~UNUSED3~]\nOutputList2SigType43=Analog\nOutputList2Cue44=[~UNUSED3~]\nOutputList2SigType44=Analog\nOutputList2Cue45=[~UNUSED3~]\nOutputList2SigType45=Analog\nOutputList2Cue46=[~UNUSED3~]\nOutputList2SigType46=Analog\nOutputList2Cue47=[~UNUSED3~]\nOutputList2SigType47=Analog\nOutputList2Cue48=[~UNUSED3~]\nOutputList2SigType48=Analog\nOutputList2Cue49=[~UNUSED3~]\nOutputList2SigType49=Analog\nOutputList2Cue50=[~UNUSED3~]\nOutputList2SigType50=Analog\nOutputList2Cue51=[~UNUSED3~]\nOutputList2SigType51=Analog\nOutputList2Cue52=[~UNUSED3~]\nOutputList2SigType52=Analog\nOutputList2Cue53=[~UNUSED3~]\nOutputList2SigType53=Analog\nOutputList2Cue54=[~UNUSED3~]\nOutputList2SigType54=Analog\nOutputList2Cue55=[~UNUSED3~]\nOutputList2SigType55=Analog\nOutputList2Cue56=[~UNUSED3~]\nOutputList2SigType56=Analog\nOutputList2Cue57=[~UNUSED3~]\nOutputList2SigType57=Analog\nOutputList2Cue58=[~UNUSED3~]\nOutputList2SigType58=Analog\nOutputList2Cue59=[~UNUSED3~]\nOutputList2SigType59=Analog\nOutputList2Cue60=[~UNUSED3~]\nOutputList2SigType60=Analog\nOutputList2Cue61=[~UNUSED3~]\nOutputList2SigType61=Analog\nOutputList2Cue62=[~UNUSED3~]\nOutputList2SigType62=Analog\nOutputList2Cue63=[~UNUSED3~]\nOutputList2SigType63=Analog\nOutputList2Cue64=[~UNUSED3~]\nOutputList2SigType64=Analog\nOutputList2Cue65=[~UNUSED3~]\nOutputList2SigType65=Analog\nOutputList2Cue66=[~UNUSED3~]\nOutputList2SigType66=Analog\nOutputList2Cue67=[~UNUSED3~]\nOutputList2SigType67=Analog\nOutputList2Cue68=[~UNUSED3~]\nOutputList2SigType68=Analog\nOutputList2Cue69=[~UNUSED3~]\nOutputList2SigType69=Analog\nOutputList2Cue70=[~UNUSED3~]\nOutputList2SigType70=Analog\nOutputList2Cue71=[~UNUSED3~]\nOutputList2SigType71=Analog\nOutputList2Cue72=[~UNUSED3~]\nOutputList2SigType72=Analog\nOutputList2Cue73=[~UNUSED3~]\nOutputList2SigType73=Analog\nOutputList2Cue74=[~UNUSED3~]\nOutputList2SigType74=Analog\nOutputList2Cue75=[~UNUSED3~]\nOutputList2SigType75=Analog\nOutputList2Cue76=[~UNUSED3~]\nOutputList2SigType76=Analog\nOutputList2Cue77=[~UNUSED3~]\nOutputList2SigType77=Analog\nOutputList2Cue78=[~UNUSED3~]\nOutputList2SigType78=Analog\nOutputList2Cue79=[~UNUSED3~]\nOutputList2SigType79=Analog\nOutputList2Cue80=[~UNUSED3~]\nOutputList2SigType80=Analog\nOutputList2Cue81=[~UNUSED3~]\nOutputList2SigType81=Analog\nOutputList2Cue82=[~UNUSED3~]\nOutputList2SigType82=Analog\nOutputList2Cue83=[~UNUSED3~]\nOutputList2SigType83=Analog\nOutputList2Cue84=[~UNUSED3~]\nOutputList2SigType84=Analog\nOutputList2Cue85=[~UNUSED3~]\nOutputList2SigType85=Analog\nOutputList2Cue86=[~UNUSED3~]\nOutputList2SigType86=Analog\nOutputList2Cue87=[~UNUSED3~]\nOutputList2SigType87=Analog\nOutputList2Cue88=[~UNUSED3~]\nOutputList2SigType88=Analog\nOutputList2Cue89=[~UNUSED3~]\nOutputList2SigType89=Analog\nOutputList2Cue90=[~UNUSED3~]\nOutputList2SigType90=Analog\nOutputList2Cue91=[~UNUSED3~]\nOutputList2SigType91=Analog\nOutputList2Cue92=[~UNUSED3~]\nOutputList2SigType92=Analog\nOutputList2Cue93=[~UNUSED3~]\nOutputList2SigType93=Analog\nOutputList2Cue94=[~UNUSED3~]\nOutputList2SigType94=Analog\nOutputList2Cue95=[~UNUSED3~]\nOutputList2SigType95=Analog\nOutputList2Cue96=[~UNUSED3~]\nOutputList2SigType96=Analog\nOutputList2Cue97=[~UNUSED3~]\nOutputList2SigType97=Analog\nOutputList2Cue98=[~UNUSED3~]\nOutputList2SigType98=Analog\nOutputList2Cue99=[~UNUSED3~]\nOutputList2SigType99=Analog\nOutputList2Cue100=[~UNUSED3~]\nOutputList2SigType100=Analog\nOutputList2Cue101=[~UNUSED3~]\nOutputList2SigType101=Analog\nOutputList2Cue102=[~UNUSED3~]\nOutputList2SigType102=Analog\nOutputList2Cue103=[~UNUSED3~]\nOutputList2SigType103=Analog\nOutputList2Cue104=[~UNUSED3~]\nOutputList2SigType104=Analog\nOutputList2Cue105=[~UNUSED3~]\nOutputList2SigType105=Analog\nOutputList2Cue106=[~UNUSED3~]\nOutputList2SigType106=Analog\nOutputList2Cue107=[~UNUSED3~]\nOutputList2SigType107=Analog\nOutputList2Cue108=[~UNUSED3~]\nOutputList2SigType108=Analog\nOutputList2Cue109=[~UNUSED3~]\nOutputList2SigType109=Analog\nOutputList2Cue110=[~UNUSED3~]\nOutputList2SigType110=Analog\nOutputList2Cue111=[~UNUSED3~]\nOutputList2SigType111=Analog\nOutputList2Cue112=[~UNUSED3~]\nOutputList2SigType112=Analog\nOutputList2Cue113=[~UNUSED3~]\nOutputList2SigType113=Analog\nOutputList2Cue114=[~UNUSED3~]\nOutputList2SigType114=Analog\nOutputList2Cue115=[~UNUSED3~]\nOutputList2SigType115=Analog\nOutputList2Cue116=[~UNUSED3~]\nOutputList2SigType116=Analog\nOutputList2Cue117=[~UNUSED3~]\nOutputList2SigType117=Analog\nOutputList2Cue118=[~UNUSED3~]\nOutputList2SigType118=Analog\nOutputList2Cue119=[~UNUSED3~]\nOutputList2SigType119=Analog\nOutputList2Cue120=[~UNUSED3~]\nOutputList2SigType120=Analog\nOutputList2Cue121=[~UNUSED3~]\nOutputList2SigType121=Analog\nOutputList2Cue122=[~UNUSED3~]\nOutputList2SigType122=Analog\nOutputList2Cue123=[~UNUSED3~]\nOutputList2SigType123=Analog\nOutputList2Cue124=[~UNUSED3~]\nOutputList2SigType124=Analog\nOutputList2Cue125=[~UNUSED3~]\nOutputList2SigType125=Analog\nOutputList2Cue126=[~UNUSED3~]\nOutputList2SigType126=Analog\nOutputList2Cue127=[~UNUSED3~]\nOutputList2SigType127=Analog\nOutputList2Cue128=[~UNUSED3~]\nOutputList2SigType128=Analog\nOutputList2Cue129=[~UNUSED3~]\nOutputList2SigType129=Analog\nOutputList2Cue130=[~UNUSED3~]\nOutputList2SigType130=Analog\nOutputList2Cue131=[~UNUSED3~]\nOutputList2SigType131=Analog\nOutputList2Cue132=[~UNUSED3~]\nOutputList2SigType132=Analog\nOutputList2Cue133=[~UNUSED3~]\nOutputList2SigType133=Analog\nOutputList2Cue134=[~UNUSED3~]\nOutputList2SigType134=Analog\nOutputList2Cue135=[~UNUSED3~]\nOutputList2SigType135=Analog\nOutputList2Cue136=[~UNUSED3~]\nOutputList2SigType136=Analog\nOutputList2Cue137=[~UNUSED3~]\nOutputList2SigType137=Analog\nOutputList2Cue138=[~UNUSED3~]\nOutputList2SigType138=Analog\nOutputList2Cue139=[~UNUSED3~]\nOutputList2SigType139=Analog\nOutputList2Cue140=[~UNUSED3~]\nOutputList2SigType140=Analog\nOutputList2Cue141=[~UNUSED3~]\nOutputList2SigType141=Analog\nOutputList2Cue142=[~UNUSED3~]\nOutputList2SigType142=Analog\nOutputList2Cue143=[~UNUSED3~]\nOutputList2SigType143=Analog\nOutputList2Cue144=[~UNUSED3~]\nOutputList2SigType144=Analog\nOutputList2Cue145=[~UNUSED3~]\nOutputList2SigType145=Analog\nOutputList2Cue146=[~UNUSED3~]\nOutputList2SigType146=Analog\nOutputList2Cue147=[~UNUSED3~]\nOutputList2SigType147=Analog\nOutputList2Cue148=[~UNUSED3~]\nOutputList2SigType148=Analog\nOutputList2Cue149=[~UNUSED3~]\nOutputList2SigType149=Analog\nOutputList2Cue150=[~UNUSED3~]\nOutputList2SigType150=Analog\nOutputList2Cue151=[~UNUSED3~]\nOutputList2SigType151=Analog\nOutputList2Cue152=[~UNUSED3~]\nOutputList2SigType152=Analog\nOutputList2Cue153=[~UNUSED3~]\nOutputList2SigType153=Analog\nOutputList2Cue154=[~UNUSED3~]\nOutputList2SigType154=Analog\nOutputList2Cue155=[~UNUSED3~]\nOutputList2SigType155=Analog\nOutputList2Cue156=[~UNUSED3~]\nOutputList2SigType156=Analog\nOutputList2Cue157=[~UNUSED3~]\nOutputList2SigType157=Analog\nOutputList2Cue158=[~UNUSED3~]\nOutputList2SigType158=Analog\nOutputList2Cue159=[~UNUSED3~]\nOutputList2SigType159=Analog\nOutputList2Cue160=[~UNUSED3~]\nOutputList2SigType160=Analog\nOutputList2Cue161=[~UNUSED3~]\nOutputList2SigType161=Analog\nOutputList2Cue162=[~UNUSED3~]\nOutputList2SigType162=Analog\nOutputList2Cue163=[~UNUSED3~]\nOutputList2SigType163=Analog\nOutputList2Cue164=[~UNUSED3~]\nOutputList2SigType164=Analog\nOutputList2Cue165=[~UNUSED3~]\nOutputList2SigType165=Analog\nOutputList2Cue166=[~UNUSED3~]\nOutputList2SigType166=Analog\nOutputList2Cue167=[~UNUSED3~]\nOutputList2SigType167=Analog\nOutputList2Cue168=[~UNUSED3~]\nOutputList2SigType168=Analog\nOutputList2Cue169=[~UNUSED3~]\nOutputList2SigType169=Analog\nOutputList2Cue170=[~UNUSED3~]\nOutputList2SigType170=Analog\nOutputList2Cue171=[~UNUSED3~]\nOutputList2SigType171=Analog\nOutputList2Cue172=[~UNUSED3~]\nOutputList2SigType172=Analog\nOutputList2Cue173=[~UNUSED3~]\nOutputList2SigType173=Analog\nOutputList2Cue174=[~UNUSED3~]\nOutputList2SigType174=Analog\nOutputList2Cue175=[~UNUSED3~]\nOutputList2SigType175=Analog\nOutputList2Cue176=[~UNUSED3~]\nOutputList2SigType176=Analog\nOutputList2Cue177=[~UNUSED3~]\nOutputList2SigType177=Analog\nOutputList2Cue178=[~UNUSED3~]\nOutputList2SigType178=Analog\nOutputList2Cue179=[~UNUSED3~]\nOutputList2SigType179=Analog\nOutputList2Cue180=[~UNUSED3~]\nOutputList2SigType180=Analog\nOutputList2Cue181=[~UNUSED3~]\nOutputList2SigType181=Analog\nOutputList2Cue182=[~UNUSED3~]\nOutputList2SigType182=Analog\nOutputList2Cue183=[~UNUSED3~]\nOutputList2SigType183=Analog\nOutputList2Cue184=[~UNUSED3~]\nOutputList2SigType184=Analog\nOutputList2Cue185=[~UNUSED3~]\nOutputList2SigType185=Analog\nOutputList2Cue186=[~UNUSED3~]\nOutputList2SigType186=Analog\nOutputList2Cue187=[~UNUSED3~]\nOutputList2SigType187=Analog\nOutputList2Cue188=[~UNUSED3~]\nOutputList2SigType188=Analog\nOutputList2Cue189=[~UNUSED3~]\nOutputList2SigType189=Analog\nOutputList2Cue190=[~UNUSED3~]\nOutputList2SigType190=Analog\nOutputList2Cue191=[~UNUSED3~]\nOutputList2SigType191=Analog\nOutputList2Cue192=[~UNUSED3~]\nOutputList2SigType192=Analog\nOutputList2Cue193=[~UNUSED3~]\nOutputList2SigType193=Analog\nOutputList2Cue194=[~UNUSED3~]\nOutputList2SigType194=Analog\nOutputList2Cue195=[~UNUSED3~]\nOutputList2SigType195=Analog\nOutputList2Cue196=[~UNUSED3~]\nOutputList2SigType196=Analog\nOutputList2Cue197=[~UNUSED3~]\nOutputList2SigType197=Analog\nOutputList2Cue198=[~UNUSED3~]\nOutputList2SigType198=Analog\nOutputList2Cue199=[~UNUSED3~]\nOutputList2SigType199=Analog\nOutputList2Cue200=[~UNUSED3~]\nOutputList2SigType200=Analog\nOutputList2Cue201=[~UNUSED3~]\nOutputList2SigType201=Analog\nOutputList2Cue202=[~UNUSED3~]\nOutputList2SigType202=Analog\nOutputList2Cue203=[~UNUSED3~]\nOutputList2SigType203=Analog\nOutputList2Cue204=[~UNUSED3~]\nOutputList2SigType204=Analog\nOutputList2Cue205=[~UNUSED3~]\nOutputList2SigType205=Analog\nOutputList2Cue206=[~UNUSED3~]\nOutputList2SigType206=Analog\nOutputList2Cue207=[~UNUSED3~]\nOutputList2SigType207=Analog\nOutputList2Cue208=[~UNUSED3~]\nOutputList2SigType208=Analog\nOutputList2Cue209=[~UNUSED3~]\nOutputList2SigType209=Analog\nOutputList2Cue210=[~UNUSED3~]\nOutputList2SigType210=Analog\nOutputList2Cue211=[~UNUSED3~]\nOutputList2SigType211=Analog\nOutputList2Cue212=[~UNUSED3~]\nOutputList2SigType212=Analog\nOutputList2Cue213=[~UNUSED3~]\nOutputList2SigType213=Analog\nOutputList2Cue214=[~UNUSED3~]\nOutputList2SigType214=Analog\nOutputList2Cue215=[~UNUSED3~]\nOutputList2SigType215=Analog\nOutputList2Cue216=[~UNUSED3~]\nOutputList2SigType216=Analog\nOutputList2Cue217=[~UNUSED3~]\nOutputList2SigType217=Analog\nOutputList2Cue218=[~UNUSED3~]\nOutputList2SigType218=Analog\nOutputList2Cue219=[~UNUSED3~]\nOutputList2SigType219=Analog\nOutputList2Cue220=[~UNUSED3~]\nOutputList2SigType220=Analog\nOutputList2Cue221=[~UNUSED3~]\nOutputList2SigType221=Analog\nOutputList2Cue222=[~UNUSED3~]\nOutputList2SigType222=Analog\nOutputList2Cue223=[~UNUSED3~]\nOutputList2SigType223=Analog\nOutputList2Cue224=[~UNUSED3~]\nOutputList2SigType224=Analog\nOutputList2Cue225=[~UNUSED3~]\nOutputList2SigType225=Analog\nOutputList2Cue226=[~UNUSED3~]\nOutputList2SigType226=Analog\nOutputList2Cue227=[~UNUSED3~]\nOutputList2SigType227=Analog\nOutputList2Cue228=[~UNUSED3~]\nOutputList2SigType228=Analog\nOutputList2Cue229=[~UNUSED3~]\nOutputList2SigType229=Analog\nOutputList2Cue230=[~UNUSED3~]\nOutputList2SigType230=Analog\nOutputList2Cue231=[~UNUSED3~]\nOutputList2SigType231=Analog\nOutputList2Cue232=[~UNUSED3~]\nOutputList2SigType232=Analog\nOutputList2Cue233=[~UNUSED3~]\nOutputList2SigType233=Analog\nOutputList2Cue234=[~UNUSED3~]\nOutputList2SigType234=Analog\nOutputList2Cue235=[~UNUSED3~]\nOutputList2SigType235=Analog\nOutputList2Cue236=[~UNUSED3~]\nOutputList2SigType236=Analog\nOutputList2Cue237=[~UNUSED3~]\nOutputList2SigType237=Analog\nOutputList2Cue238=[~UNUSED3~]\nOutputList2SigType238=Analog\nOutputList2Cue239=[~UNUSED3~]\nOutputList2SigType239=Analog\nOutputList2Cue240=[~UNUSED3~]\nOutputList2SigType240=Analog\nOutputList2Cue241=[~UNUSED3~]\nOutputList2SigType241=Analog\nOutputList2Cue242=[~UNUSED3~]\nOutputList2SigType242=Analog\nOutputList2Cue243=[~UNUSED3~]\nOutputList2SigType243=Analog\nOutputList2Cue244=[~UNUSED3~]\nOutputList2SigType244=Analog\nOutputList2Cue245=[~UNUSED3~]\nOutputList2SigType245=Analog\nOutputList2Cue246=[~UNUSED3~]\nOutputList2SigType246=Analog\nOutputList2Cue247=[~UNUSED3~]\nOutputList2SigType247=Analog\nOutputList2Cue248=[~UNUSED3~]\nOutputList2SigType248=Analog\nOutputList2Cue249=[~UNUSED3~]\nOutputList2SigType249=Analog\nOutputList2Cue250=[~UNUSED3~]\nOutputList2SigType250=Analog\nOutputList2Cue251=[~UNUSED3~]\nOutputList2SigType251=Analog\nOutputList2Cue252=[~UNUSED3~]\nOutputList2SigType252=Analog\nOutputList2Cue253=[~UNUSED3~]\nOutputList2SigType253=Analog\nOutputList2Cue254=[~UNUSED3~]\nOutputList2SigType254=Analog\nOutputList2Cue255=[~UNUSED3~]\nOutputList2SigType255=Analog\nOutputList2Cue256=[~UNUSED3~]\nOutputList2SigType256=Analog\nOutputList2Cue257=[~UNUSED3~]\nOutputList2SigType257=Analog\nOutputList2Cue258=[~UNUSED3~]\nOutputList2SigType258=Analog\nOutputList2Cue259=[~UNUSED3~]\nOutputList2SigType259=Analog\nOutputList2Cue260=[~UNUSED3~]\nOutputList2SigType260=Analog\nOutputList2Cue261=[~UNUSED3~]\nOutputList2SigType261=Analog\nOutputList2Cue262=[~UNUSED3~]\nOutputList2SigType262=Analog\nOutputList2Cue263=[~UNUSED3~]\nOutputList2SigType263=Analog\nOutputList2Cue264=[~UNUSED3~]\nOutputList2SigType264=Analog\nOutputList2Cue265=[~UNUSED3~]\nOutputList2SigType265=Analog\nOutputList2Cue266=[~UNUSED3~]\nOutputList2SigType266=Analog\nOutputList2Cue267=[~UNUSED2~]\nOutputList2SigType267=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=Set Item 11 Text\nInputList3SigType22=Serial\nInputList3Cue23=Set Item 12 Text\nInputList3SigType23=Serial\nInputList3Cue24=Set Item 13 Text\nInputList3SigType24=Serial\nInputList3Cue25=Set Item 14 Text\nInputList3SigType25=Serial\nInputList3Cue26=Set Item 15 Text\nInputList3SigType26=Serial\nInputList3Cue27=Set Item 16 Text\nInputList3SigType27=Serial\nInputList3Cue28=Set Item 17 Text\nInputList3SigType28=Serial\nInputList3Cue29=Set Item 18 Text\nInputList3SigType29=Serial\nInputList3Cue30=Set Item 19 Text\nInputList3SigType30=Serial\nInputList3Cue31=Set Item 20 Text\nInputList3SigType31=Serial\nInputList3Cue32=Set Item 21 Text\nInputList3SigType32=Serial\nInputList3Cue33=Set Item 22 Text\nInputList3SigType33=Serial\nInputList3Cue34=Set Item 23 Text\nInputList3SigType34=Serial\nInputList3Cue35=Set Item 24 Text\nInputList3SigType35=Serial\nInputList3Cue36=Set Item 25 Text\nInputList3SigType36=Serial\nInputList3Cue37=Set Item 26 Text\nInputList3SigType37=Serial\nInputList3Cue38=Set Item 27 Text\nInputList3SigType38=Serial\nInputList3Cue39=Set Item 28 Text\nInputList3SigType39=Serial\nInputList3Cue40=Set Item 29 Text\nInputList3SigType40=Serial\nInputList3Cue41=Set Item 30 Text\nInputList3SigType41=Serial\nInputList3Cue42=Set Item 31 Text\nInputList3SigType42=Serial\nInputList3Cue43=Set Item 32 Text\nInputList3SigType43=Serial\nInputList3Cue44=Set Item 33 Text\nInputList3SigType44=Serial\nInputList3Cue45=Set Item 34 Text\nInputList3SigType45=Serial\nInputList3Cue46=Set Item 35 Text\nInputList3SigType46=Serial\nInputList3Cue47=Set Item 36 Text\nInputList3SigType47=Serial\nInputList3Cue48=Set Item 37 Text\nInputList3SigType48=Serial\nInputList3Cue49=Set Item 38 Text\nInputList3SigType49=Serial\nInputList3Cue50=Set Item 39 Text\nInputList3SigType50=Serial\nInputList3Cue51=Set Item 40 Text\nInputList3SigType51=Serial\nInputList3Cue52=Set Item 41 Text\nInputList3SigType52=Serial\nInputList3Cue53=Set Item 42 Text\nInputList3SigType53=Serial\nInputList3Cue54=Set Item 43 Text\nInputList3SigType54=Serial\nInputList3Cue55=Set Item 44 Text\nInputList3SigType55=Serial\nInputList3Cue56=Set Item 45 Text\nInputList3SigType56=Serial\nInputList3Cue57=Set Item 46 Text\nInputList3SigType57=Serial\nInputList3Cue58=Set Item 47 Text\nInputList3SigType58=Serial\nInputList3Cue59=Set Item 48 Text\nInputList3SigType59=Serial\nInputList3Cue60=Set Item 49 Text\nInputList3SigType60=Serial\nInputList3Cue61=Set Item 50 Text\nInputList3SigType61=Serial\nInputList3Cue62=Set Item 51 Text\nInputList3SigType62=Serial\nInputList3Cue63=Set Item 52 Text\nInputList3SigType63=Serial\nInputList3Cue64=Set Item 53 Text\nInputList3SigType64=Serial\nInputList3Cue65=Set Item 54 Text\nInputList3SigType65=Serial\nInputList3Cue66=Set Item 55 Text\nInputList3SigType66=Serial\nInputList3Cue67=Set Item 56 Text\nInputList3SigType67=Serial\nInputList3Cue68=Set Item 57 Text\nInputList3SigType68=Serial\nInputList3Cue69=Set Item 58 Text\nInputList3SigType69=Serial\nInputList3Cue70=Set Item 59 Text\nInputList3SigType70=Serial\nInputList3Cue71=Set Item 60 Text\nInputList3SigType71=Serial\nInputList3Cue72=Set Item 61 Text\nInputList3SigType72=Serial\nInputList3Cue73=Set Item 62 Text\nInputList3SigType73=Serial\nInputList3Cue74=Set Item 63 Text\nInputList3SigType74=Serial\nInputList3Cue75=Set Item 64 Text\nInputList3SigType75=Serial\nInputList3Cue76=Set Item 65 Text\nInputList3SigType76=Serial\nInputList3Cue77=Set Item 66 Text\nInputList3SigType77=Serial\nInputList3Cue78=Set Item 67 Text\nInputList3SigType78=Serial\nInputList3Cue79=Set Item 68 Text\nInputList3SigType79=Serial\nInputList3Cue80=Set Item 69 Text\nInputList3SigType80=Serial\nInputList3Cue81=Set Item 70 Text\nInputList3SigType81=Serial\nInputList3Cue82=Set Item 71 Text\nInputList3SigType82=Serial\nInputList3Cue83=Set Item 72 Text\nInputList3SigType83=Serial\nInputList3Cue84=Set Item 73 Text\nInputList3SigType84=Serial\nInputList3Cue85=Set Item 74 Text\nInputList3SigType85=Serial\nInputList3Cue86=Set Item 75 Text\nInputList3SigType86=Serial\nInputList3Cue87=Set Item 76 Text\nInputList3SigType87=Serial\nInputList3Cue88=Set Item 77 Text\nInputList3SigType88=Serial\nInputList3Cue89=Set Item 78 Text\nInputList3SigType89=Serial\nInputList3Cue90=Set Item 79 Text\nInputList3SigType90=Serial\nInputList3Cue91=Set Item 80 Text\nInputList3SigType91=Serial\nInputList3Cue92=Set Item 81 Text\nInputList3SigType92=Serial\nInputList3Cue93=Set Item 82 Text\nInputList3SigType93=Serial\nInputList3Cue94=Set Item 83 Text\nInputList3SigType94=Serial\nInputList3Cue95=Set Item 84 Text\nInputList3SigType95=Serial\nInputList3Cue96=Set Item 85 Text\nInputList3SigType96=Serial\nInputList3Cue97=Set Item 86 Text\nInputList3SigType97=Serial\nInputList3Cue98=Set Item 87 Text\nInputList3SigType98=Serial\nInputList3Cue99=Set Item 88 Text\nInputList3SigType99=Serial\nInputList3Cue100=Set Item 89 Text\nInputList3SigType100=Serial\nInputList3Cue101=Set Item 90 Text\nInputList3SigType101=Serial\nInputList3Cue102=Set Item 91 Text\nInputList3SigType102=Serial\nInputList3Cue103=Set Item 92 Text\nInputList3SigType103=Serial\nInputList3Cue104=Set Item 93 Text\nInputList3SigType104=Serial\nInputList3Cue105=Set Item 94 Text\nInputList3SigType105=Serial\nInputList3Cue106=Set Item 95 Text\nInputList3SigType106=Serial\nInputList3Cue107=Set Item 96 Text\nInputList3SigType107=Serial\nInputList3Cue108=Set Item 97 Text\nInputList3SigType108=Serial\nInputList3Cue109=Set Item 98 Text\nInputList3SigType109=Serial\nInputList3Cue110=Set Item 99 Text\nInputList3SigType110=Serial\nInputList3Cue111=Set Item 100 Text\nInputList3SigType111=Serial\nInputList3Cue112=Set Item 101 Text\nInputList3SigType112=Serial\nInputList3Cue113=Set Item 102 Text\nInputList3SigType113=Serial\nInputList3Cue114=Set Item 103 Text\nInputList3SigType114=Serial\nInputList3Cue115=Set Item 104 Text\nInputList3SigType115=Serial\nInputList3Cue116=Set Item 105 Text\nInputList3SigType116=Serial\nInputList3Cue117=Set Item 106 Text\nInputList3SigType117=Serial\nInputList3Cue118=Set Item 107 Text\nInputList3SigType118=Serial\nInputList3Cue119=Set Item 108 Text\nInputList3SigType119=Serial\nInputList3Cue120=Set Item 109 Text\nInputList3SigType120=Serial\nInputList3Cue121=Set Item 110 Text\nInputList3SigType121=Serial\nInputList3Cue122=Set Item 111 Text\nInputList3SigType122=Serial\nInputList3Cue123=Set Item 112 Text\nInputList3SigType123=Serial\nInputList3Cue124=Set Item 113 Text\nInputList3SigType124=Serial\nInputList3Cue125=Set Item 114 Text\nInputList3SigType125=Serial\nInputList3Cue126=Set Item 115 Text\nInputList3SigType126=Serial\nInputList3Cue127=Set Item 116 Text\nInputList3SigType127=Serial\nInputList3Cue128=Set Item 117 Text\nInputList3SigType128=Serial\nInputList3Cue129=Set Item 118 Text\nInputList3SigType129=Serial\nInputList3Cue130=Set Item 119 Text\nInputList3SigType130=Serial\nInputList3Cue131=Set Item 120 Text\nInputList3SigType131=Serial\nInputList3Cue132=Set Item 121 Text\nInputList3SigType132=Serial\nInputList3Cue133=Set Item 122 Text\nInputList3SigType133=Serial\nInputList3Cue134=Set Item 123 Text\nInputList3SigType134=Serial\nInputList3Cue135=Set Item 124 Text\nInputList3SigType135=Serial\nInputList3Cue136=Set Item 125 Text\nInputList3SigType136=Serial\nInputList3Cue137=Set Item 126 Text\nInputList3SigType137=Serial\nInputList3Cue138=Set Item 127 Text\nInputList3SigType138=Serial\nInputList3Cue139=Set Item 128 Text\nInputList3SigType139=Serial\nInputList3Cue140=Set Item 129 Text\nInputList3SigType140=Serial\nInputList3Cue141=Set Item 130 Text\nInputList3SigType141=Serial\nInputList3Cue142=Set Item 131 Text\nInputList3SigType142=Serial\nInputList3Cue143=Set Item 132 Text\nInputList3SigType143=Serial\nInputList3Cue144=Set Item 133 Text\nInputList3SigType144=Serial\nInputList3Cue145=Set Item 134 Text\nInputList3SigType145=Serial\nInputList3Cue146=Set Item 135 Text\nInputList3SigType146=Serial\nInputList3Cue147=Set Item 136 Text\nInputList3SigType147=Serial\nInputList3Cue148=Set Item 137 Text\nInputList3SigType148=Serial\nInputList3Cue149=Set Item 138 Text\nInputList3SigType149=Serial\nInputList3Cue150=Set Item 139 Text\nInputList3SigType150=Serial\nInputList3Cue151=Set Item 140 Text\nInputList3SigType151=Serial\nInputList3Cue152=Set Item 141 Text\nInputList3SigType152=Serial\nInputList3Cue153=Set Item 142 Text\nInputList3SigType153=Serial\nInputList3Cue154=Set Item 143 Text\nInputList3SigType154=Serial\nInputList3Cue155=Set Item 144 Text\nInputList3SigType155=Serial\nInputList3Cue156=Set Item 145 Text\nInputList3SigType156=Serial\nInputList3Cue157=Set Item 146 Text\nInputList3SigType157=Serial\nInputList3Cue158=Set Item 147 Text\nInputList3SigType158=Serial\nInputList3Cue159=Set Item 148 Text\nInputList3SigType159=Serial\nInputList3Cue160=Set Item 149 Text\nInputList3SigType160=Serial\nInputList3Cue161=Set Item 150 Text\nInputList3SigType161=Serial\nInputList3Cue162=Set Item 151 Text\nInputList3SigType162=Serial\nInputList3Cue163=Set Item 152 Text\nInputList3SigType163=Serial\nInputList3Cue164=Set Item 153 Text\nInputList3SigType164=Serial\nInputList3Cue165=Set Item 154 Text\nInputList3SigType165=Serial\nInputList3Cue166=Set Item 155 Text\nInputList3SigType166=Serial\nInputList3Cue167=Set Item 156 Text\nInputList3SigType167=Serial\nInputList3Cue168=Set Item 157 Text\nInputList3SigType168=Serial\nInputList3Cue169=Set Item 158 Text\nInputList3SigType169=Serial\nInputList3Cue170=Set Item 159 Text\nInputList3SigType170=Serial\nInputList3Cue171=Set Item 160 Text\nInputList3SigType171=Serial\nInputList3Cue172=Set Item 161 Text\nInputList3SigType172=Serial\nInputList3Cue173=Set Item 162 Text\nInputList3SigType173=Serial\nInputList3Cue174=Set Item 163 Text\nInputList3SigType174=Serial\nInputList3Cue175=Set Item 164 Text\nInputList3SigType175=Serial\nInputList3Cue176=Set Item 165 Text\nInputList3SigType176=Serial\nInputList3Cue177=Set Item 166 Text\nInputList3SigType177=Serial\nInputList3Cue178=Set Item 167 Text\nInputList3SigType178=Serial\nInputList3Cue179=Set Item 168 Text\nInputList3SigType179=Serial\nInputList3Cue180=Set Item 169 Text\nInputList3SigType180=Serial\nInputList3Cue181=Set Item 170 Text\nInputList3SigType181=Serial\nInputList3Cue182=Set Item 171 Text\nInputList3SigType182=Serial\nInputList3Cue183=Set Item 172 Text\nInputList3SigType183=Serial\nInputList3Cue184=Set Item 173 Text\nInputList3SigType184=Serial\nInputList3Cue185=Set Item 174 Text\nInputList3SigType185=Serial\nInputList3Cue186=Set Item 175 Text\nInputList3SigType186=Serial\nInputList3Cue187=Set Item 176 Text\nInputList3SigType187=Serial\nInputList3Cue188=Set Item 177 Text\nInputList3SigType188=Serial\nInputList3Cue189=Set Item 178 Text\nInputList3SigType189=Serial\nInputList3Cue190=Set Item 179 Text\nInputList3SigType190=Serial\nInputList3Cue191=Set Item 180 Text\nInputList3SigType191=Serial\nInputList3Cue192=Set Item 181 Text\nInputList3SigType192=Serial\nInputList3Cue193=Set Item 182 Text\nInputList3SigType193=Serial\nInputList3Cue194=Set Item 183 Text\nInputList3SigType194=Serial\nInputList3Cue195=Set Item 184 Text\nInputList3SigType195=Serial\nInputList3Cue196=Set Item 185 Text\nInputList3SigType196=Serial\nInputList3Cue197=Set Item 186 Text\nInputList3SigType197=Serial\nInputList3Cue198=Set Item 187 Text\nInputList3SigType198=Serial\nInputList3Cue199=Set Item 188 Text\nInputList3SigType199=Serial\nInputList3Cue200=Set Item 189 Text\nInputList3SigType200=Serial\nInputList3Cue201=Set Item 190 Text\nInputList3SigType201=Serial\nInputList3Cue202=Set Item 191 Text\nInputList3SigType202=Serial\nInputList3Cue203=Set Item 192 Text\nInputList3SigType203=Serial\nInputList3Cue204=Set Item 193 Text\nInputList3SigType204=Serial\nInputList3Cue205=Set Item 194 Text\nInputList3SigType205=Serial\nInputList3Cue206=Set Item 195 Text\nInputList3SigType206=Serial\nInputList3Cue207=Set Item 196 Text\nInputList3SigType207=Serial\nInputList3Cue208=Set Item 197 Text\nInputList3SigType208=Serial\nInputList3Cue209=Set Item 198 Text\nInputList3SigType209=Serial\nInputList3Cue210=Set Item 199 Text\nInputList3SigType210=Serial\nInputList3Cue211=Set Item 200 Text\nInputList3SigType211=Serial\nInputList3Cue212=Set Item 201 Text\nInputList3SigType212=Serial\nInputList3Cue213=Set Item 202 Text\nInputList3SigType213=Serial\nInputList3Cue214=Set Item 203 Text\nInputList3SigType214=Serial\nInputList3Cue215=Set Item 204 Text\nInputList3SigType215=Serial\nInputList3Cue216=Set Item 205 Text\nInputList3SigType216=Serial\nInputList3Cue217=Set Item 206 Text\nInputList3SigType217=Serial\nInputList3Cue218=Set Item 207 Text\nInputList3SigType218=Serial\nInputList3Cue219=Set Item 208 Text\nInputList3SigType219=Serial\nInputList3Cue220=Set Item 209 Text\nInputList3SigType220=Serial\nInputList3Cue221=Set Item 210 Text\nInputList3SigType221=Serial\nInputList3Cue222=Set Item 211 Text\nInputList3SigType222=Serial\nInputList3Cue223=Set Item 212 Text\nInputList3SigType223=Serial\nInputList3Cue224=Set Item 213 Text\nInputList3SigType224=Serial\nInputList3Cue225=Set Item 214 Text\nInputList3SigType225=Serial\nInputList3Cue226=Set Item 215 Text\nInputList3SigType226=Serial\nInputList3Cue227=Set Item 216 Text\nInputList3SigType227=Serial\nInputList3Cue228=Set Item 217 Text\nInputList3SigType228=Serial\nInputList3Cue229=Set Item 218 Text\nInputList3SigType229=Serial\nInputList3Cue230=Set Item 219 Text\nInputList3SigType230=Serial\nInputList3Cue231=Set Item 220 Text\nInputList3SigType231=Serial\nInputList3Cue232=Set Item 221 Text\nInputList3SigType232=Serial\nInputList3Cue233=Set Item 222 Text\nInputList3SigType233=Serial\nInputList3Cue234=Set Item 223 Text\nInputList3SigType234=Serial\nInputList3Cue235=Set Item 224 Text\nInputList3SigType235=Serial\nInputList3Cue236=Set Item 225 Text\nInputList3SigType236=Serial\nInputList3Cue237=Set Item 226 Text\nInputList3SigType237=Serial\nInputList3Cue238=Set Item 227 Text\nInputList3SigType238=Serial\nInputList3Cue239=Set Item 228 Text\nInputList3SigType239=Serial\nInputList3Cue240=Set Item 229 Text\nInputList3SigType240=Serial\nInputList3Cue241=Set Item 230 Text\nInputList3SigType241=Serial\nInputList3Cue242=Set Item 231 Text\nInputList3SigType242=Serial\nInputList3Cue243=Set Item 232 Text\nInputList3SigType243=Serial\nInputList3Cue244=Set Item 233 Text\nInputList3SigType244=Serial\nInputList3Cue245=Set Item 234 Text\nInputList3SigType245=Serial\nInputList3Cue246=Set Item 235 Text\nInputList3SigType246=Serial\nInputList3Cue247=Set Item 236 Text\nInputList3SigType247=Serial\nInputList3Cue248=Set Item 237 Text\nInputList3SigType248=Serial\nInputList3Cue249=Set Item 238 Text\nInputList3SigType249=Serial\nInputList3Cue250=Set Item 239 Text\nInputList3SigType250=Serial\nInputList3Cue251=Set Item 240 Text\nInputList3SigType251=Serial\nInputList3Cue252=Set Item 241 Text\nInputList3SigType252=Serial\nInputList3Cue253=Set Item 242 Text\nInputList3SigType253=Serial\nInputList3Cue254=Set Item 243 Text\nInputList3SigType254=Serial\nInputList3Cue255=Set Item 244 Text\nInputList3SigType255=Serial\nInputList3Cue256=Set Item 245 Text\nInputList3SigType256=Serial\nInputList3Cue257=Set Item 246 Text\nInputList3SigType257=Serial\nInputList3Cue258=Set Item 247 Text\nInputList3SigType258=Serial\nInputList3Cue259=Set Item 248 Text\nInputList3SigType259=Serial\nInputList3Cue260=Set Item 249 Text\nInputList3SigType260=Serial\nInputList3Cue261=Set Item 250 Text\nInputList3SigType261=Serial\nInputList3Cue262=Set Item 251 Text\nInputList3SigType262=Serial\nInputList3Cue263=Set Item 252 Text\nInputList3SigType263=Serial\nInputList3Cue264=Set Item 253 Text\nInputList3SigType264=Serial\nInputList3Cue265=Set Item 254 Text\nInputList3SigType265=Serial\nInputList3Cue266=Set Item 255 Text\nInputList3SigType266=Serial\nInputList3Cue267=[~UNUSED2~]\nInputList3SigType267=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=Set Item 11 Icon Serial\nInputList3SigType2024=Serial\nInputList3Cue2025=Set Item 12 Icon Serial\nInputList3SigType2025=Serial\nInputList3Cue2026=Set Item 13 Icon Serial\nInputList3SigType2026=Serial\nInputList3Cue2027=Set Item 14 Icon Serial\nInputList3SigType2027=Serial\nInputList3Cue2028=Set Item 15 Icon Serial\nInputList3SigType2028=Serial\nInputList3Cue2029=Set Item 16 Icon Serial\nInputList3SigType2029=Serial\nInputList3Cue2030=Set Item 17 Icon Serial\nInputList3SigType2030=Serial\nInputList3Cue2031=Set Item 18 Icon Serial\nInputList3SigType2031=Serial\nInputList3Cue2032=Set Item 19 Icon Serial\nInputList3SigType2032=Serial\nInputList3Cue2033=Set Item 20 Icon Serial\nInputList3SigType2033=Serial\nInputList3Cue2034=Set Item 21 Icon Serial\nInputList3SigType2034=Serial\nInputList3Cue2035=Set Item 22 Icon Serial\nInputList3SigType2035=Serial\nInputList3Cue2036=Set Item 23 Icon Serial\nInputList3SigType2036=Serial\nInputList3Cue2037=Set Item 24 Icon Serial\nInputList3SigType2037=Serial\nInputList3Cue2038=Set Item 25 Icon Serial\nInputList3SigType2038=Serial\nInputList3Cue2039=Set Item 26 Icon Serial\nInputList3SigType2039=Serial\nInputList3Cue2040=Set Item 27 Icon Serial\nInputList3SigType2040=Serial\nInputList3Cue2041=Set Item 28 Icon Serial\nInputList3SigType2041=Serial\nInputList3Cue2042=Set Item 29 Icon Serial\nInputList3SigType2042=Serial\nInputList3Cue2043=Set Item 30 Icon Serial\nInputList3SigType2043=Serial\nInputList3Cue2044=Set Item 31 Icon Serial\nInputList3SigType2044=Serial\nInputList3Cue2045=Set Item 32 Icon Serial\nInputList3SigType2045=Serial\nInputList3Cue2046=Set Item 33 Icon Serial\nInputList3SigType2046=Serial\nInputList3Cue2047=Set Item 34 Icon Serial\nInputList3SigType2047=Serial\nInputList3Cue2048=Set Item 35 Icon Serial\nInputList3SigType2048=Serial\nInputList3Cue2049=Set Item 36 Icon Serial\nInputList3SigType2049=Serial\nInputList3Cue2050=Set Item 37 Icon Serial\nInputList3SigType2050=Serial\nInputList3Cue2051=Set Item 38 Icon Serial\nInputList3SigType2051=Serial\nInputList3Cue2052=Set Item 39 Icon Serial\nInputList3SigType2052=Serial\nInputList3Cue2053=Set Item 40 Icon Serial\nInputList3SigType2053=Serial\nInputList3Cue2054=Set Item 41 Icon Serial\nInputList3SigType2054=Serial\nInputList3Cue2055=Set Item 42 Icon Serial\nInputList3SigType2055=Serial\nInputList3Cue2056=Set Item 43 Icon Serial\nInputList3SigType2056=Serial\nInputList3Cue2057=Set Item 44 Icon Serial\nInputList3SigType2057=Serial\nInputList3Cue2058=Set Item 45 Icon Serial\nInputList3SigType2058=Serial\nInputList3Cue2059=Set Item 46 Icon Serial\nInputList3SigType2059=Serial\nInputList3Cue2060=Set Item 47 Icon Serial\nInputList3SigType2060=Serial\nInputList3Cue2061=Set Item 48 Icon Serial\nInputList3SigType2061=Serial\nInputList3Cue2062=Set Item 49 Icon Serial\nInputList3SigType2062=Serial\nInputList3Cue2063=Set Item 50 Icon Serial\nInputList3SigType2063=Serial\nInputList3Cue2064=Set Item 51 Icon Serial\nInputList3SigType2064=Serial\nInputList3Cue2065=Set Item 52 Icon Serial\nInputList3SigType2065=Serial\nInputList3Cue2066=Set Item 53 Icon Serial\nInputList3SigType2066=Serial\nInputList3Cue2067=Set Item 54 Icon Serial\nInputList3SigType2067=Serial\nInputList3Cue2068=Set Item 55 Icon Serial\nInputList3SigType2068=Serial\nInputList3Cue2069=Set Item 56 Icon Serial\nInputList3SigType2069=Serial\nInputList3Cue2070=Set Item 57 Icon Serial\nInputList3SigType2070=Serial\nInputList3Cue2071=Set Item 58 Icon Serial\nInputList3SigType2071=Serial\nInputList3Cue2072=Set Item 59 Icon Serial\nInputList3SigType2072=Serial\nInputList3Cue2073=Set Item 60 Icon Serial\nInputList3SigType2073=Serial\nInputList3Cue2074=Set Item 61 Icon Serial\nInputList3SigType2074=Serial\nInputList3Cue2075=Set Item 62 Icon Serial\nInputList3SigType2075=Serial\nInputList3Cue2076=Set Item 63 Icon Serial\nInputList3SigType2076=Serial\nInputList3Cue2077=Set Item 64 Icon Serial\nInputList3SigType2077=Serial\nInputList3Cue2078=Set Item 65 Icon Serial\nInputList3SigType2078=Serial\nInputList3Cue2079=Set Item 66 Icon Serial\nInputList3SigType2079=Serial\nInputList3Cue2080=Set Item 67 Icon Serial\nInputList3SigType2080=Serial\nInputList3Cue2081=Set Item 68 Icon Serial\nInputList3SigType2081=Serial\nInputList3Cue2082=Set Item 69 Icon Serial\nInputList3SigType2082=Serial\nInputList3Cue2083=Set Item 70 Icon Serial\nInputList3SigType2083=Serial\nInputList3Cue2084=Set Item 71 Icon Serial\nInputList3SigType2084=Serial\nInputList3Cue2085=Set Item 72 Icon Serial\nInputList3SigType2085=Serial\nInputList3Cue2086=Set Item 73 Icon Serial\nInputList3SigType2086=Serial\nInputList3Cue2087=Set Item 74 Icon Serial\nInputList3SigType2087=Serial\nInputList3Cue2088=Set Item 75 Icon Serial\nInputList3SigType2088=Serial\nInputList3Cue2089=Set Item 76 Icon Serial\nInputList3SigType2089=Serial\nInputList3Cue2090=Set Item 77 Icon Serial\nInputList3SigType2090=Serial\nInputList3Cue2091=Set Item 78 Icon Serial\nInputList3SigType2091=Serial\nInputList3Cue2092=Set Item 79 Icon Serial\nInputList3SigType2092=Serial\nInputList3Cue2093=Set Item 80 Icon Serial\nInputList3SigType2093=Serial\nInputList3Cue2094=Set Item 81 Icon Serial\nInputList3SigType2094=Serial\nInputList3Cue2095=Set Item 82 Icon Serial\nInputList3SigType2095=Serial\nInputList3Cue2096=Set Item 83 Icon Serial\nInputList3SigType2096=Serial\nInputList3Cue2097=Set Item 84 Icon Serial\nInputList3SigType2097=Serial\nInputList3Cue2098=Set Item 85 Icon Serial\nInputList3SigType2098=Serial\nInputList3Cue2099=Set Item 86 Icon Serial\nInputList3SigType2099=Serial\nInputList3Cue2100=Set Item 87 Icon Serial\nInputList3SigType2100=Serial\nInputList3Cue2101=Set Item 88 Icon Serial\nInputList3SigType2101=Serial\nInputList3Cue2102=Set Item 89 Icon Serial\nInputList3SigType2102=Serial\nInputList3Cue2103=Set Item 90 Icon Serial\nInputList3SigType2103=Serial\nInputList3Cue2104=Set Item 91 Icon Serial\nInputList3SigType2104=Serial\nInputList3Cue2105=Set Item 92 Icon Serial\nInputList3SigType2105=Serial\nInputList3Cue2106=Set Item 93 Icon Serial\nInputList3SigType2106=Serial\nInputList3Cue2107=Set Item 94 Icon Serial\nInputList3SigType2107=Serial\nInputList3Cue2108=Set Item 95 Icon Serial\nInputList3SigType2108=Serial\nInputList3Cue2109=Set Item 96 Icon Serial\nInputList3SigType2109=Serial\nInputList3Cue2110=Set Item 97 Icon Serial\nInputList3SigType2110=Serial\nInputList3Cue2111=Set Item 98 Icon Serial\nInputList3SigType2111=Serial\nInputList3Cue2112=Set Item 99 Icon Serial\nInputList3SigType2112=Serial\nInputList3Cue2113=Set Item 100 Icon Serial\nInputList3SigType2113=Serial\nInputList3Cue2114=Set Item 101 Icon Serial\nInputList3SigType2114=Serial\nInputList3Cue2115=Set Item 102 Icon Serial\nInputList3SigType2115=Serial\nInputList3Cue2116=Set Item 103 Icon Serial\nInputList3SigType2116=Serial\nInputList3Cue2117=Set Item 104 Icon Serial\nInputList3SigType2117=Serial\nInputList3Cue2118=Set Item 105 Icon Serial\nInputList3SigType2118=Serial\nInputList3Cue2119=Set Item 106 Icon Serial\nInputList3SigType2119=Serial\nInputList3Cue2120=Set Item 107 Icon Serial\nInputList3SigType2120=Serial\nInputList3Cue2121=Set Item 108 Icon Serial\nInputList3SigType2121=Serial\nInputList3Cue2122=Set Item 109 Icon Serial\nInputList3SigType2122=Serial\nInputList3Cue2123=Set Item 110 Icon Serial\nInputList3SigType2123=Serial\nInputList3Cue2124=Set Item 111 Icon Serial\nInputList3SigType2124=Serial\nInputList3Cue2125=Set Item 112 Icon Serial\nInputList3SigType2125=Serial\nInputList3Cue2126=Set Item 113 Icon Serial\nInputList3SigType2126=Serial\nInputList3Cue2127=Set Item 114 Icon Serial\nInputList3SigType2127=Serial\nInputList3Cue2128=Set Item 115 Icon Serial\nInputList3SigType2128=Serial\nInputList3Cue2129=Set Item 116 Icon Serial\nInputList3SigType2129=Serial\nInputList3Cue2130=Set Item 117 Icon Serial\nInputList3SigType2130=Serial\nInputList3Cue2131=Set Item 118 Icon Serial\nInputList3SigType2131=Serial\nInputList3Cue2132=Set Item 119 Icon Serial\nInputList3SigType2132=Serial\nInputList3Cue2133=Set Item 120 Icon Serial\nInputList3SigType2133=Serial\nInputList3Cue2134=Set Item 121 Icon Serial\nInputList3SigType2134=Serial\nInputList3Cue2135=Set Item 122 Icon Serial\nInputList3SigType2135=Serial\nInputList3Cue2136=Set Item 123 Icon Serial\nInputList3SigType2136=Serial\nInputList3Cue2137=Set Item 124 Icon Serial\nInputList3SigType2137=Serial\nInputList3Cue2138=Set Item 125 Icon Serial\nInputList3SigType2138=Serial\nInputList3Cue2139=Set Item 126 Icon Serial\nInputList3SigType2139=Serial\nInputList3Cue2140=Set Item 127 Icon Serial\nInputList3SigType2140=Serial\nInputList3Cue2141=Set Item 128 Icon Serial\nInputList3SigType2141=Serial\nInputList3Cue2142=Set Item 129 Icon Serial\nInputList3SigType2142=Serial\nInputList3Cue2143=Set Item 130 Icon Serial\nInputList3SigType2143=Serial\nInputList3Cue2144=Set Item 131 Icon Serial\nInputList3SigType2144=Serial\nInputList3Cue2145=Set Item 132 Icon Serial\nInputList3SigType2145=Serial\nInputList3Cue2146=Set Item 133 Icon Serial\nInputList3SigType2146=Serial\nInputList3Cue2147=Set Item 134 Icon Serial\nInputList3SigType2147=Serial\nInputList3Cue2148=Set Item 135 Icon Serial\nInputList3SigType2148=Serial\nInputList3Cue2149=Set Item 136 Icon Serial\nInputList3SigType2149=Serial\nInputList3Cue2150=Set Item 137 Icon Serial\nInputList3SigType2150=Serial\nInputList3Cue2151=Set Item 138 Icon Serial\nInputList3SigType2151=Serial\nInputList3Cue2152=Set Item 139 Icon Serial\nInputList3SigType2152=Serial\nInputList3Cue2153=Set Item 140 Icon Serial\nInputList3SigType2153=Serial\nInputList3Cue2154=Set Item 141 Icon Serial\nInputList3SigType2154=Serial\nInputList3Cue2155=Set Item 142 Icon Serial\nInputList3SigType2155=Serial\nInputList3Cue2156=Set Item 143 Icon Serial\nInputList3SigType2156=Serial\nInputList3Cue2157=Set Item 144 Icon Serial\nInputList3SigType2157=Serial\nInputList3Cue2158=Set Item 145 Icon Serial\nInputList3SigType2158=Serial\nInputList3Cue2159=Set Item 146 Icon Serial\nInputList3SigType2159=Serial\nInputList3Cue2160=Set Item 147 Icon Serial\nInputList3SigType2160=Serial\nInputList3Cue2161=Set Item 148 Icon Serial\nInputList3SigType2161=Serial\nInputList3Cue2162=Set Item 149 Icon Serial\nInputList3SigType2162=Serial\nInputList3Cue2163=Set Item 150 Icon Serial\nInputList3SigType2163=Serial\nInputList3Cue2164=Set Item 151 Icon Serial\nInputList3SigType2164=Serial\nInputList3Cue2165=Set Item 152 Icon Serial\nInputList3SigType2165=Serial\nInputList3Cue2166=Set Item 153 Icon Serial\nInputList3SigType2166=Serial\nInputList3Cue2167=Set Item 154 Icon Serial\nInputList3SigType2167=Serial\nInputList3Cue2168=Set Item 155 Icon Serial\nInputList3SigType2168=Serial\nInputList3Cue2169=Set Item 156 Icon Serial\nInputList3SigType2169=Serial\nInputList3Cue2170=Set Item 157 Icon Serial\nInputList3SigType2170=Serial\nInputList3Cue2171=Set Item 158 Icon Serial\nInputList3SigType2171=Serial\nInputList3Cue2172=Set Item 159 Icon Serial\nInputList3SigType2172=Serial\nInputList3Cue2173=Set Item 160 Icon Serial\nInputList3SigType2173=Serial\nInputList3Cue2174=Set Item 161 Icon Serial\nInputList3SigType2174=Serial\nInputList3Cue2175=Set Item 162 Icon Serial\nInputList3SigType2175=Serial\nInputList3Cue2176=Set Item 163 Icon Serial\nInputList3SigType2176=Serial\nInputList3Cue2177=Set Item 164 Icon Serial\nInputList3SigType2177=Serial\nInputList3Cue2178=Set Item 165 Icon Serial\nInputList3SigType2178=Serial\nInputList3Cue2179=Set Item 166 Icon Serial\nInputList3SigType2179=Serial\nInputList3Cue2180=Set Item 167 Icon Serial\nInputList3SigType2180=Serial\nInputList3Cue2181=Set Item 168 Icon Serial\nInputList3SigType2181=Serial\nInputList3Cue2182=Set Item 169 Icon Serial\nInputList3SigType2182=Serial\nInputList3Cue2183=Set Item 170 Icon Serial\nInputList3SigType2183=Serial\nInputList3Cue2184=Set Item 171 Icon Serial\nInputList3SigType2184=Serial\nInputList3Cue2185=Set Item 172 Icon Serial\nInputList3SigType2185=Serial\nInputList3Cue2186=Set Item 173 Icon Serial\nInputList3SigType2186=Serial\nInputList3Cue2187=Set Item 174 Icon Serial\nInputList3SigType2187=Serial\nInputList3Cue2188=Set Item 175 Icon Serial\nInputList3SigType2188=Serial\nInputList3Cue2189=Set Item 176 Icon Serial\nInputList3SigType2189=Serial\nInputList3Cue2190=Set Item 177 Icon Serial\nInputList3SigType2190=Serial\nInputList3Cue2191=Set Item 178 Icon Serial\nInputList3SigType2191=Serial\nInputList3Cue2192=Set Item 179 Icon Serial\nInputList3SigType2192=Serial\nInputList3Cue2193=Set Item 180 Icon Serial\nInputList3SigType2193=Serial\nInputList3Cue2194=Set Item 181 Icon Serial\nInputList3SigType2194=Serial\nInputList3Cue2195=Set Item 182 Icon Serial\nInputList3SigType2195=Serial\nInputList3Cue2196=Set Item 183 Icon Serial\nInputList3SigType2196=Serial\nInputList3Cue2197=Set Item 184 Icon Serial\nInputList3SigType2197=Serial\nInputList3Cue2198=Set Item 185 Icon Serial\nInputList3SigType2198=Serial\nInputList3Cue2199=Set Item 186 Icon Serial\nInputList3SigType2199=Serial\nInputList3Cue2200=Set Item 187 Icon Serial\nInputList3SigType2200=Serial\nInputList3Cue2201=Set Item 188 Icon Serial\nInputList3SigType2201=Serial\nInputList3Cue2202=Set Item 189 Icon Serial\nInputList3SigType2202=Serial\nInputList3Cue2203=Set Item 190 Icon Serial\nInputList3SigType2203=Serial\nInputList3Cue2204=Set Item 191 Icon Serial\nInputList3SigType2204=Serial\nInputList3Cue2205=Set Item 192 Icon Serial\nInputList3SigType2205=Serial\nInputList3Cue2206=Set Item 193 Icon Serial\nInputList3SigType2206=Serial\nInputList3Cue2207=Set Item 194 Icon Serial\nInputList3SigType2207=Serial\nInputList3Cue2208=Set Item 195 Icon Serial\nInputList3SigType2208=Serial\nInputList3Cue2209=Set Item 196 Icon Serial\nInputList3SigType2209=Serial\nInputList3Cue2210=Set Item 197 Icon Serial\nInputList3SigType2210=Serial\nInputList3Cue2211=Set Item 198 Icon Serial\nInputList3SigType2211=Serial\nInputList3Cue2212=Set Item 199 Icon Serial\nInputList3SigType2212=Serial\nInputList3Cue2213=Set Item 200 Icon Serial\nInputList3SigType2213=Serial\nInputList3Cue2214=Set Item 201 Icon Serial\nInputList3SigType2214=Serial\nInputList3Cue2215=Set Item 202 Icon Serial\nInputList3SigType2215=Serial\nInputList3Cue2216=Set Item 203 Icon Serial\nInputList3SigType2216=Serial\nInputList3Cue2217=Set Item 204 Icon Serial\nInputList3SigType2217=Serial\nInputList3Cue2218=Set Item 205 Icon Serial\nInputList3SigType2218=Serial\nInputList3Cue2219=Set Item 206 Icon Serial\nInputList3SigType2219=Serial\nInputList3Cue2220=Set Item 207 Icon Serial\nInputList3SigType2220=Serial\nInputList3Cue2221=Set Item 208 Icon Serial\nInputList3SigType2221=Serial\nInputList3Cue2222=Set Item 209 Icon Serial\nInputList3SigType2222=Serial\nInputList3Cue2223=Set Item 210 Icon Serial\nInputList3SigType2223=Serial\nInputList3Cue2224=Set Item 211 Icon Serial\nInputList3SigType2224=Serial\nInputList3Cue2225=Set Item 212 Icon Serial\nInputList3SigType2225=Serial\nInputList3Cue2226=Set Item 213 Icon Serial\nInputList3SigType2226=Serial\nInputList3Cue2227=Set Item 214 Icon Serial\nInputList3SigType2227=Serial\nInputList3Cue2228=Set Item 215 Icon Serial\nInputList3SigType2228=Serial\nInputList3Cue2229=Set Item 216 Icon Serial\nInputList3SigType2229=Serial\nInputList3Cue2230=Set Item 217 Icon Serial\nInputList3SigType2230=Serial\nInputList3Cue2231=Set Item 218 Icon Serial\nInputList3SigType2231=Serial\nInputList3Cue2232=Set Item 219 Icon Serial\nInputList3SigType2232=Serial\nInputList3Cue2233=Set Item 220 Icon Serial\nInputList3SigType2233=Serial\nInputList3Cue2234=Set Item 221 Icon Serial\nInputList3SigType2234=Serial\nInputList3Cue2235=Set Item 222 Icon Serial\nInputList3SigType2235=Serial\nInputList3Cue2236=Set Item 223 Icon Serial\nInputList3SigType2236=Serial\nInputList3Cue2237=Set Item 224 Icon Serial\nInputList3SigType2237=Serial\nInputList3Cue2238=Set Item 225 Icon Serial\nInputList3SigType2238=Serial\nInputList3Cue2239=Set Item 226 Icon Serial\nInputList3SigType2239=Serial\nInputList3Cue2240=Set Item 227 Icon Serial\nInputList3SigType2240=Serial\nInputList3Cue2241=Set Item 228 Icon Serial\nInputList3SigType2241=Serial\nInputList3Cue2242=Set Item 229 Icon Serial\nInputList3SigType2242=Serial\nInputList3Cue2243=Set Item 230 Icon Serial\nInputList3SigType2243=Serial\nInputList3Cue2244=Set Item 231 Icon Serial\nInputList3SigType2244=Serial\nInputList3Cue2245=Set Item 232 Icon Serial\nInputList3SigType2245=Serial\nInputList3Cue2246=Set Item 233 Icon Serial\nInputList3SigType2246=Serial\nInputList3Cue2247=Set Item 234 Icon Serial\nInputList3SigType2247=Serial\nInputList3Cue2248=Set Item 235 Icon Serial\nInputList3SigType2248=Serial\nInputList3Cue2249=Set Item 236 Icon Serial\nInputList3SigType2249=Serial\nInputList3Cue2250=Set Item 237 Icon Serial\nInputList3SigType2250=Serial\nInputList3Cue2251=Set Item 238 Icon Serial\nInputList3SigType2251=Serial\nInputList3Cue2252=Set Item 239 Icon Serial\nInputList3SigType2252=Serial\nInputList3Cue2253=Set Item 240 Icon Serial\nInputList3SigType2253=Serial\nInputList3Cue2254=Set Item 241 Icon Serial\nInputList3SigType2254=Serial\nInputList3Cue2255=Set Item 242 Icon Serial\nInputList3SigType2255=Serial\nInputList3Cue2256=Set Item 243 Icon Serial\nInputList3SigType2256=Serial\nInputList3Cue2257=Set Item 244 Icon Serial\nInputList3SigType2257=Serial\nInputList3Cue2258=Set Item 245 Icon Serial\nInputList3SigType2258=Serial\nInputList3Cue2259=Set Item 246 Icon Serial\nInputList3SigType2259=Serial\nInputList3Cue2260=Set Item 247 Icon Serial\nInputList3SigType2260=Serial\nInputList3Cue2261=Set Item 248 Icon Serial\nInputList3SigType2261=Serial\nInputList3Cue2262=Set Item 249 Icon Serial\nInputList3SigType2262=Serial\nInputList3Cue2263=Set Item 250 Icon Serial\nInputList3SigType2263=Serial\nInputList3Cue2264=Set Item 251 Icon Serial\nInputList3SigType2264=Serial\nInputList3Cue2265=Set Item 252 Icon Serial\nInputList3SigType2265=Serial\nInputList3Cue2266=Set Item 253 Icon Serial\nInputList3SigType2266=Serial\nInputList3Cue2267=Set Item 254 Icon Serial\nInputList3SigType2267=Serial\nInputList3Cue2268=Set Item 255 Icon Serial\nInputList3SigType2268=Serial\nInputList3Cue2269=[~UNUSED2~]\nInputList3SigType2269=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED3~]\nOutputList3SigType22=Serial\nOutputList3Cue23=[~UNUSED3~]\nOutputList3SigType23=Serial\nOutputList3Cue24=[~UNUSED3~]\nOutputList3SigType24=Serial\nOutputList3Cue25=[~UNUSED3~]\nOutputList3SigType25=Serial\nOutputList3Cue26=[~UNUSED3~]\nOutputList3SigType26=Serial\nOutputList3Cue27=[~UNUSED3~]\nOutputList3SigType27=Serial\nOutputList3Cue28=[~UNUSED3~]\nOutputList3SigType28=Serial\nOutputList3Cue29=[~UNUSED3~]\nOutputList3SigType29=Serial\nOutputList3Cue30=[~UNUSED3~]\nOutputList3SigType30=Serial\nOutputList3Cue31=[~UNUSED3~]\nOutputList3SigType31=Serial\nOutputList3Cue32=[~UNUSED3~]\nOutputList3SigType32=Serial\nOutputList3Cue33=[~UNUSED3~]\nOutputList3SigType33=Serial\nOutputList3Cue34=[~UNUSED3~]\nOutputList3SigType34=Serial\nOutputList3Cue35=[~UNUSED3~]\nOutputList3SigType35=Serial\nOutputList3Cue36=[~UNUSED3~]\nOutputList3SigType36=Serial\nOutputList3Cue37=[~UNUSED3~]\nOutputList3SigType37=Serial\nOutputList3Cue38=[~UNUSED3~]\nOutputList3SigType38=Serial\nOutputList3Cue39=[~UNUSED3~]\nOutputList3SigType39=Serial\nOutputList3Cue40=[~UNUSED3~]\nOutputList3SigType40=Serial\nOutputList3Cue41=[~UNUSED3~]\nOutputList3SigType41=Serial\nOutputList3Cue42=[~UNUSED3~]\nOutputList3SigType42=Serial\nOutputList3Cue43=[~UNUSED3~]\nOutputList3SigType43=Serial\nOutputList3Cue44=[~UNUSED3~]\nOutputList3SigType44=Serial\nOutputList3Cue45=[~UNUSED3~]\nOutputList3SigType45=Serial\nOutputList3Cue46=[~UNUSED3~]\nOutputList3SigType46=Serial\nOutputList3Cue47=[~UNUSED3~]\nOutputList3SigType47=Serial\nOutputList3Cue48=[~UNUSED3~]\nOutputList3SigType48=Serial\nOutputList3Cue49=[~UNUSED3~]\nOutputList3SigType49=Serial\nOutputList3Cue50=[~UNUSED3~]\nOutputList3SigType50=Serial\nOutputList3Cue51=[~UNUSED3~]\nOutputList3SigType51=Serial\nOutputList3Cue52=[~UNUSED3~]\nOutputList3SigType52=Serial\nOutputList3Cue53=[~UNUSED3~]\nOutputList3SigType53=Serial\nOutputList3Cue54=[~UNUSED3~]\nOutputList3SigType54=Serial\nOutputList3Cue55=[~UNUSED3~]\nOutputList3SigType55=Serial\nOutputList3Cue56=[~UNUSED3~]\nOutputList3SigType56=Serial\nOutputList3Cue57=[~UNUSED3~]\nOutputList3SigType57=Serial\nOutputList3Cue58=[~UNUSED3~]\nOutputList3SigType58=Serial\nOutputList3Cue59=[~UNUSED3~]\nOutputList3SigType59=Serial\nOutputList3Cue60=[~UNUSED3~]\nOutputList3SigType60=Serial\nOutputList3Cue61=[~UNUSED3~]\nOutputList3SigType61=Serial\nOutputList3Cue62=[~UNUSED3~]\nOutputList3SigType62=Serial\nOutputList3Cue63=[~UNUSED3~]\nOutputList3SigType63=Serial\nOutputList3Cue64=[~UNUSED3~]\nOutputList3SigType64=Serial\nOutputList3Cue65=[~UNUSED3~]\nOutputList3SigType65=Serial\nOutputList3Cue66=[~UNUSED3~]\nOutputList3SigType66=Serial\nOutputList3Cue67=[~UNUSED3~]\nOutputList3SigType67=Serial\nOutputList3Cue68=[~UNUSED3~]\nOutputList3SigType68=Serial\nOutputList3Cue69=[~UNUSED3~]\nOutputList3SigType69=Serial\nOutputList3Cue70=[~UNUSED3~]\nOutputList3SigType70=Serial\nOutputList3Cue71=[~UNUSED3~]\nOutputList3SigType71=Serial\nOutputList3Cue72=[~UNUSED3~]\nOutputList3SigType72=Serial\nOutputList3Cue73=[~UNUSED3~]\nOutputList3SigType73=Serial\nOutputList3Cue74=[~UNUSED3~]\nOutputList3SigType74=Serial\nOutputList3Cue75=[~UNUSED3~]\nOutputList3SigType75=Serial\nOutputList3Cue76=[~UNUSED3~]\nOutputList3SigType76=Serial\nOutputList3Cue77=[~UNUSED3~]\nOutputList3SigType77=Serial\nOutputList3Cue78=[~UNUSED3~]\nOutputList3SigType78=Serial\nOutputList3Cue79=[~UNUSED3~]\nOutputList3SigType79=Serial\nOutputList3Cue80=[~UNUSED3~]\nOutputList3SigType80=Serial\nOutputList3Cue81=[~UNUSED3~]\nOutputList3SigType81=Serial\nOutputList3Cue82=[~UNUSED3~]\nOutputList3SigType82=Serial\nOutputList3Cue83=[~UNUSED3~]\nOutputList3SigType83=Serial\nOutputList3Cue84=[~UNUSED3~]\nOutputList3SigType84=Serial\nOutputList3Cue85=[~UNUSED3~]\nOutputList3SigType85=Serial\nOutputList3Cue86=[~UNUSED3~]\nOutputList3SigType86=Serial\nOutputList3Cue87=[~UNUSED3~]\nOutputList3SigType87=Serial\nOutputList3Cue88=[~UNUSED3~]\nOutputList3SigType88=Serial\nOutputList3Cue89=[~UNUSED3~]\nOutputList3SigType89=Serial\nOutputList3Cue90=[~UNUSED3~]\nOutputList3SigType90=Serial\nOutputList3Cue91=[~UNUSED3~]\nOutputList3SigType91=Serial\nOutputList3Cue92=[~UNUSED3~]\nOutputList3SigType92=Serial\nOutputList3Cue93=[~UNUSED3~]\nOutputList3SigType93=Serial\nOutputList3Cue94=[~UNUSED3~]\nOutputList3SigType94=Serial\nOutputList3Cue95=[~UNUSED3~]\nOutputList3SigType95=Serial\nOutputList3Cue96=[~UNUSED3~]\nOutputList3SigType96=Serial\nOutputList3Cue97=[~UNUSED3~]\nOutputList3SigType97=Serial\nOutputList3Cue98=[~UNUSED3~]\nOutputList3SigType98=Serial\nOutputList3Cue99=[~UNUSED3~]\nOutputList3SigType99=Serial\nOutputList3Cue100=[~UNUSED3~]\nOutputList3SigType100=Serial\nOutputList3Cue101=[~UNUSED3~]\nOutputList3SigType101=Serial\nOutputList3Cue102=[~UNUSED3~]\nOutputList3SigType102=Serial\nOutputList3Cue103=[~UNUSED3~]\nOutputList3SigType103=Serial\nOutputList3Cue104=[~UNUSED3~]\nOutputList3SigType104=Serial\nOutputList3Cue105=[~UNUSED3~]\nOutputList3SigType105=Serial\nOutputList3Cue106=[~UNUSED3~]\nOutputList3SigType106=Serial\nOutputList3Cue107=[~UNUSED3~]\nOutputList3SigType107=Serial\nOutputList3Cue108=[~UNUSED3~]\nOutputList3SigType108=Serial\nOutputList3Cue109=[~UNUSED3~]\nOutputList3SigType109=Serial\nOutputList3Cue110=[~UNUSED3~]\nOutputList3SigType110=Serial\nOutputList3Cue111=[~UNUSED3~]\nOutputList3SigType111=Serial\nOutputList3Cue112=[~UNUSED3~]\nOutputList3SigType112=Serial\nOutputList3Cue113=[~UNUSED3~]\nOutputList3SigType113=Serial\nOutputList3Cue114=[~UNUSED3~]\nOutputList3SigType114=Serial\nOutputList3Cue115=[~UNUSED3~]\nOutputList3SigType115=Serial\nOutputList3Cue116=[~UNUSED3~]\nOutputList3SigType116=Serial\nOutputList3Cue117=[~UNUSED3~]\nOutputList3SigType117=Serial\nOutputList3Cue118=[~UNUSED3~]\nOutputList3SigType118=Serial\nOutputList3Cue119=[~UNUSED3~]\nOutputList3SigType119=Serial\nOutputList3Cue120=[~UNUSED3~]\nOutputList3SigType120=Serial\nOutputList3Cue121=[~UNUSED3~]\nOutputList3SigType121=Serial\nOutputList3Cue122=[~UNUSED3~]\nOutputList3SigType122=Serial\nOutputList3Cue123=[~UNUSED3~]\nOutputList3SigType123=Serial\nOutputList3Cue124=[~UNUSED3~]\nOutputList3SigType124=Serial\nOutputList3Cue125=[~UNUSED3~]\nOutputList3SigType125=Serial\nOutputList3Cue126=[~UNUSED3~]\nOutputList3SigType126=Serial\nOutputList3Cue127=[~UNUSED3~]\nOutputList3SigType127=Serial\nOutputList3Cue128=[~UNUSED3~]\nOutputList3SigType128=Serial\nOutputList3Cue129=[~UNUSED3~]\nOutputList3SigType129=Serial\nOutputList3Cue130=[~UNUSED3~]\nOutputList3SigType130=Serial\nOutputList3Cue131=[~UNUSED3~]\nOutputList3SigType131=Serial\nOutputList3Cue132=[~UNUSED3~]\nOutputList3SigType132=Serial\nOutputList3Cue133=[~UNUSED3~]\nOutputList3SigType133=Serial\nOutputList3Cue134=[~UNUSED3~]\nOutputList3SigType134=Serial\nOutputList3Cue135=[~UNUSED3~]\nOutputList3SigType135=Serial\nOutputList3Cue136=[~UNUSED3~]\nOutputList3SigType136=Serial\nOutputList3Cue137=[~UNUSED3~]\nOutputList3SigType137=Serial\nOutputList3Cue138=[~UNUSED3~]\nOutputList3SigType138=Serial\nOutputList3Cue139=[~UNUSED3~]\nOutputList3SigType139=Serial\nOutputList3Cue140=[~UNUSED3~]\nOutputList3SigType140=Serial\nOutputList3Cue141=[~UNUSED3~]\nOutputList3SigType141=Serial\nOutputList3Cue142=[~UNUSED3~]\nOutputList3SigType142=Serial\nOutputList3Cue143=[~UNUSED3~]\nOutputList3SigType143=Serial\nOutputList3Cue144=[~UNUSED3~]\nOutputList3SigType144=Serial\nOutputList3Cue145=[~UNUSED3~]\nOutputList3SigType145=Serial\nOutputList3Cue146=[~UNUSED3~]\nOutputList3SigType146=Serial\nOutputList3Cue147=[~UNUSED3~]\nOutputList3SigType147=Serial\nOutputList3Cue148=[~UNUSED3~]\nOutputList3SigType148=Serial\nOutputList3Cue149=[~UNUSED3~]\nOutputList3SigType149=Serial\nOutputList3Cue150=[~UNUSED3~]\nOutputList3SigType150=Serial\nOutputList3Cue151=[~UNUSED3~]\nOutputList3SigType151=Serial\nOutputList3Cue152=[~UNUSED3~]\nOutputList3SigType152=Serial\nOutputList3Cue153=[~UNUSED3~]\nOutputList3SigType153=Serial\nOutputList3Cue154=[~UNUSED3~]\nOutputList3SigType154=Serial\nOutputList3Cue155=[~UNUSED3~]\nOutputList3SigType155=Serial\nOutputList3Cue156=[~UNUSED3~]\nOutputList3SigType156=Serial\nOutputList3Cue157=[~UNUSED3~]\nOutputList3SigType157=Serial\nOutputList3Cue158=[~UNUSED3~]\nOutputList3SigType158=Serial\nOutputList3Cue159=[~UNUSED3~]\nOutputList3SigType159=Serial\nOutputList3Cue160=[~UNUSED3~]\nOutputList3SigType160=Serial\nOutputList3Cue161=[~UNUSED3~]\nOutputList3SigType161=Serial\nOutputList3Cue162=[~UNUSED3~]\nOutputList3SigType162=Serial\nOutputList3Cue163=[~UNUSED3~]\nOutputList3SigType163=Serial\nOutputList3Cue164=[~UNUSED3~]\nOutputList3SigType164=Serial\nOutputList3Cue165=[~UNUSED3~]\nOutputList3SigType165=Serial\nOutputList3Cue166=[~UNUSED3~]\nOutputList3SigType166=Serial\nOutputList3Cue167=[~UNUSED3~]\nOutputList3SigType167=Serial\nOutputList3Cue168=[~UNUSED3~]\nOutputList3SigType168=Serial\nOutputList3Cue169=[~UNUSED3~]\nOutputList3SigType169=Serial\nOutputList3Cue170=[~UNUSED3~]\nOutputList3SigType170=Serial\nOutputList3Cue171=[~UNUSED3~]\nOutputList3SigType171=Serial\nOutputList3Cue172=[~UNUSED3~]\nOutputList3SigType172=Serial\nOutputList3Cue173=[~UNUSED3~]\nOutputList3SigType173=Serial\nOutputList3Cue174=[~UNUSED3~]\nOutputList3SigType174=Serial\nOutputList3Cue175=[~UNUSED3~]\nOutputList3SigType175=Serial\nOutputList3Cue176=[~UNUSED3~]\nOutputList3SigType176=Serial\nOutputList3Cue177=[~UNUSED3~]\nOutputList3SigType177=Serial\nOutputList3Cue178=[~UNUSED3~]\nOutputList3SigType178=Serial\nOutputList3Cue179=[~UNUSED3~]\nOutputList3SigType179=Serial\nOutputList3Cue180=[~UNUSED3~]\nOutputList3SigType180=Serial\nOutputList3Cue181=[~UNUSED3~]\nOutputList3SigType181=Serial\nOutputList3Cue182=[~UNUSED3~]\nOutputList3SigType182=Serial\nOutputList3Cue183=[~UNUSED3~]\nOutputList3SigType183=Serial\nOutputList3Cue184=[~UNUSED3~]\nOutputList3SigType184=Serial\nOutputList3Cue185=[~UNUSED3~]\nOutputList3SigType185=Serial\nOutputList3Cue186=[~UNUSED3~]\nOutputList3SigType186=Serial\nOutputList3Cue187=[~UNUSED3~]\nOutputList3SigType187=Serial\nOutputList3Cue188=[~UNUSED3~]\nOutputList3SigType188=Serial\nOutputList3Cue189=[~UNUSED3~]\nOutputList3SigType189=Serial\nOutputList3Cue190=[~UNUSED3~]\nOutputList3SigType190=Serial\nOutputList3Cue191=[~UNUSED3~]\nOutputList3SigType191=Serial\nOutputList3Cue192=[~UNUSED3~]\nOutputList3SigType192=Serial\nOutputList3Cue193=[~UNUSED3~]\nOutputList3SigType193=Serial\nOutputList3Cue194=[~UNUSED3~]\nOutputList3SigType194=Serial\nOutputList3Cue195=[~UNUSED3~]\nOutputList3SigType195=Serial\nOutputList3Cue196=[~UNUSED3~]\nOutputList3SigType196=Serial\nOutputList3Cue197=[~UNUSED3~]\nOutputList3SigType197=Serial\nOutputList3Cue198=[~UNUSED3~]\nOutputList3SigType198=Serial\nOutputList3Cue199=[~UNUSED3~]\nOutputList3SigType199=Serial\nOutputList3Cue200=[~UNUSED3~]\nOutputList3SigType200=Serial\nOutputList3Cue201=[~UNUSED3~]\nOutputList3SigType201=Serial\nOutputList3Cue202=[~UNUSED3~]\nOutputList3SigType202=Serial\nOutputList3Cue203=[~UNUSED3~]\nOutputList3SigType203=Serial\nOutputList3Cue204=[~UNUSED3~]\nOutputList3SigType204=Serial\nOutputList3Cue205=[~UNUSED3~]\nOutputList3SigType205=Serial\nOutputList3Cue206=[~UNUSED3~]\nOutputList3SigType206=Serial\nOutputList3Cue207=[~UNUSED3~]\nOutputList3SigType207=Serial\nOutputList3Cue208=[~UNUSED3~]\nOutputList3SigType208=Serial\nOutputList3Cue209=[~UNUSED3~]\nOutputList3SigType209=Serial\nOutputList3Cue210=[~UNUSED3~]\nOutputList3SigType210=Serial\nOutputList3Cue211=[~UNUSED3~]\nOutputList3SigType211=Serial\nOutputList3Cue212=[~UNUSED3~]\nOutputList3SigType212=Serial\nOutputList3Cue213=[~UNUSED3~]\nOutputList3SigType213=Serial\nOutputList3Cue214=[~UNUSED3~]\nOutputList3SigType214=Serial\nOutputList3Cue215=[~UNUSED3~]\nOutputList3SigType215=Serial\nOutputList3Cue216=[~UNUSED3~]\nOutputList3SigType216=Serial\nOutputList3Cue217=[~UNUSED3~]\nOutputList3SigType217=Serial\nOutputList3Cue218=[~UNUSED3~]\nOutputList3SigType218=Serial\nOutputList3Cue219=[~UNUSED3~]\nOutputList3SigType219=Serial\nOutputList3Cue220=[~UNUSED3~]\nOutputList3SigType220=Serial\nOutputList3Cue221=[~UNUSED3~]\nOutputList3SigType221=Serial\nOutputList3Cue222=[~UNUSED3~]\nOutputList3SigType222=Serial\nOutputList3Cue223=[~UNUSED3~]\nOutputList3SigType223=Serial\nOutputList3Cue224=[~UNUSED3~]\nOutputList3SigType224=Serial\nOutputList3Cue225=[~UNUSED3~]\nOutputList3SigType225=Serial\nOutputList3Cue226=[~UNUSED3~]\nOutputList3SigType226=Serial\nOutputList3Cue227=[~UNUSED3~]\nOutputList3SigType227=Serial\nOutputList3Cue228=[~UNUSED3~]\nOutputList3SigType228=Serial\nOutputList3Cue229=[~UNUSED3~]\nOutputList3SigType229=Serial\nOutputList3Cue230=[~UNUSED3~]\nOutputList3SigType230=Serial\nOutputList3Cue231=[~UNUSED3~]\nOutputList3SigType231=Serial\nOutputList3Cue232=[~UNUSED3~]\nOutputList3SigType232=Serial\nOutputList3Cue233=[~UNUSED3~]\nOutputList3SigType233=Serial\nOutputList3Cue234=[~UNUSED3~]\nOutputList3SigType234=Serial\nOutputList3Cue235=[~UNUSED3~]\nOutputList3SigType235=Serial\nOutputList3Cue236=[~UNUSED3~]\nOutputList3SigType236=Serial\nOutputList3Cue237=[~UNUSED3~]\nOutputList3SigType237=Serial\nOutputList3Cue238=[~UNUSED3~]\nOutputList3SigType238=Serial\nOutputList3Cue239=[~UNUSED3~]\nOutputList3SigType239=Serial\nOutputList3Cue240=[~UNUSED3~]\nOutputList3SigType240=Serial\nOutputList3Cue241=[~UNUSED3~]\nOutputList3SigType241=Serial\nOutputList3Cue242=[~UNUSED3~]\nOutputList3SigType242=Serial\nOutputList3Cue243=[~UNUSED3~]\nOutputList3SigType243=Serial\nOutputList3Cue244=[~UNUSED3~]\nOutputList3SigType244=Serial\nOutputList3Cue245=[~UNUSED3~]\nOutputList3SigType245=Serial\nOutputList3Cue246=[~UNUSED3~]\nOutputList3SigType246=Serial\nOutputList3Cue247=[~UNUSED3~]\nOutputList3SigType247=Serial\nOutputList3Cue248=[~UNUSED3~]\nOutputList3SigType248=Serial\nOutputList3Cue249=[~UNUSED3~]\nOutputList3SigType249=Serial\nOutputList3Cue250=[~UNUSED3~]\nOutputList3SigType250=Serial\nOutputList3Cue251=[~UNUSED3~]\nOutputList3SigType251=Serial\nOutputList3Cue252=[~UNUSED3~]\nOutputList3SigType252=Serial\nOutputList3Cue253=[~UNUSED3~]\nOutputList3SigType253=Serial\nOutputList3Cue254=[~UNUSED3~]\nOutputList3SigType254=Serial\nOutputList3Cue255=[~UNUSED3~]\nOutputList3SigType255=Serial\nOutputList3Cue256=[~UNUSED3~]\nOutputList3SigType256=Serial\nOutputList3Cue257=[~UNUSED3~]\nOutputList3SigType257=Serial\nOutputList3Cue258=[~UNUSED3~]\nOutputList3SigType258=Serial\nOutputList3Cue259=[~UNUSED3~]\nOutputList3SigType259=Serial\nOutputList3Cue260=[~UNUSED3~]\nOutputList3SigType260=Serial\nOutputList3Cue261=[~UNUSED3~]\nOutputList3SigType261=Serial\nOutputList3Cue262=[~UNUSED3~]\nOutputList3SigType262=Serial\nOutputList3Cue263=[~UNUSED3~]\nOutputList3SigType263=Serial\nOutputList3Cue264=[~UNUSED3~]\nOutputList3SigType264=Serial\nOutputList3Cue265=[~UNUSED3~]\nOutputList3SigType265=Serial\nOutputList3Cue266=[~UNUSED3~]\nOutputList3SigType266=Serial\nOutputList3Cue267=[~UNUSED2~]\nOutputList3SigType267=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED3~]\nOutputList3SigType2024=Serial\nOutputList3Cue2025=[~UNUSED3~]\nOutputList3SigType2025=Serial\nOutputList3Cue2026=[~UNUSED3~]\nOutputList3SigType2026=Serial\nOutputList3Cue2027=[~UNUSED3~]\nOutputList3SigType2027=Serial\nOutputList3Cue2028=[~UNUSED3~]\nOutputList3SigType2028=Serial\nOutputList3Cue2029=[~UNUSED3~]\nOutputList3SigType2029=Serial\nOutputList3Cue2030=[~UNUSED3~]\nOutputList3SigType2030=Serial\nOutputList3Cue2031=[~UNUSED3~]\nOutputList3SigType2031=Serial\nOutputList3Cue2032=[~UNUSED3~]\nOutputList3SigType2032=Serial\nOutputList3Cue2033=[~UNUSED3~]\nOutputList3SigType2033=Serial\nOutputList3Cue2034=[~UNUSED3~]\nOutputList3SigType2034=Serial\nOutputList3Cue2035=[~UNUSED3~]\nOutputList3SigType2035=Serial\nOutputList3Cue2036=[~UNUSED3~]\nOutputList3SigType2036=Serial\nOutputList3Cue2037=[~UNUSED3~]\nOutputList3SigType2037=Serial\nOutputList3Cue2038=[~UNUSED3~]\nOutputList3SigType2038=Serial\nOutputList3Cue2039=[~UNUSED3~]\nOutputList3SigType2039=Serial\nOutputList3Cue2040=[~UNUSED3~]\nOutputList3SigType2040=Serial\nOutputList3Cue2041=[~UNUSED3~]\nOutputList3SigType2041=Serial\nOutputList3Cue2042=[~UNUSED3~]\nOutputList3SigType2042=Serial\nOutputList3Cue2043=[~UNUSED3~]\nOutputList3SigType2043=Serial\nOutputList3Cue2044=[~UNUSED3~]\nOutputList3SigType2044=Serial\nOutputList3Cue2045=[~UNUSED3~]\nOutputList3SigType2045=Serial\nOutputList3Cue2046=[~UNUSED3~]\nOutputList3SigType2046=Serial\nOutputList3Cue2047=[~UNUSED3~]\nOutputList3SigType2047=Serial\nOutputList3Cue2048=[~UNUSED3~]\nOutputList3SigType2048=Serial\nOutputList3Cue2049=[~UNUSED3~]\nOutputList3SigType2049=Serial\nOutputList3Cue2050=[~UNUSED3~]\nOutputList3SigType2050=Serial\nOutputList3Cue2051=[~UNUSED3~]\nOutputList3SigType2051=Serial\nOutputList3Cue2052=[~UNUSED3~]\nOutputList3SigType2052=Serial\nOutputList3Cue2053=[~UNUSED3~]\nOutputList3SigType2053=Serial\nOutputList3Cue2054=[~UNUSED3~]\nOutputList3SigType2054=Serial\nOutputList3Cue2055=[~UNUSED3~]\nOutputList3SigType2055=Serial\nOutputList3Cue2056=[~UNUSED3~]\nOutputList3SigType2056=Serial\nOutputList3Cue2057=[~UNUSED3~]\nOutputList3SigType2057=Serial\nOutputList3Cue2058=[~UNUSED3~]\nOutputList3SigType2058=Serial\nOutputList3Cue2059=[~UNUSED3~]\nOutputList3SigType2059=Serial\nOutputList3Cue2060=[~UNUSED3~]\nOutputList3SigType2060=Serial\nOutputList3Cue2061=[~UNUSED3~]\nOutputList3SigType2061=Serial\nOutputList3Cue2062=[~UNUSED3~]\nOutputList3SigType2062=Serial\nOutputList3Cue2063=[~UNUSED3~]\nOutputList3SigType2063=Serial\nOutputList3Cue2064=[~UNUSED3~]\nOutputList3SigType2064=Serial\nOutputList3Cue2065=[~UNUSED3~]\nOutputList3SigType2065=Serial\nOutputList3Cue2066=[~UNUSED3~]\nOutputList3SigType2066=Serial\nOutputList3Cue2067=[~UNUSED3~]\nOutputList3SigType2067=Serial\nOutputList3Cue2068=[~UNUSED3~]\nOutputList3SigType2068=Serial\nOutputList3Cue2069=[~UNUSED3~]\nOutputList3SigType2069=Serial\nOutputList3Cue2070=[~UNUSED3~]\nOutputList3SigType2070=Serial\nOutputList3Cue2071=[~UNUSED3~]\nOutputList3SigType2071=Serial\nOutputList3Cue2072=[~UNUSED3~]\nOutputList3SigType2072=Serial\nOutputList3Cue2073=[~UNUSED3~]\nOutputList3SigType2073=Serial\nOutputList3Cue2074=[~UNUSED3~]\nOutputList3SigType2074=Serial\nOutputList3Cue2075=[~UNUSED3~]\nOutputList3SigType2075=Serial\nOutputList3Cue2076=[~UNUSED3~]\nOutputList3SigType2076=Serial\nOutputList3Cue2077=[~UNUSED3~]\nOutputList3SigType2077=Serial\nOutputList3Cue2078=[~UNUSED3~]\nOutputList3SigType2078=Serial\nOutputList3Cue2079=[~UNUSED3~]\nOutputList3SigType2079=Serial\nOutputList3Cue2080=[~UNUSED3~]\nOutputList3SigType2080=Serial\nOutputList3Cue2081=[~UNUSED3~]\nOutputList3SigType2081=Serial\nOutputList3Cue2082=[~UNUSED3~]\nOutputList3SigType2082=Serial\nOutputList3Cue2083=[~UNUSED3~]\nOutputList3SigType2083=Serial\nOutputList3Cue2084=[~UNUSED3~]\nOutputList3SigType2084=Serial\nOutputList3Cue2085=[~UNUSED3~]\nOutputList3SigType2085=Serial\nOutputList3Cue2086=[~UNUSED3~]\nOutputList3SigType2086=Serial\nOutputList3Cue2087=[~UNUSED3~]\nOutputList3SigType2087=Serial\nOutputList3Cue2088=[~UNUSED3~]\nOutputList3SigType2088=Serial\nOutputList3Cue2089=[~UNUSED3~]\nOutputList3SigType2089=Serial\nOutputList3Cue2090=[~UNUSED3~]\nOutputList3SigType2090=Serial\nOutputList3Cue2091=[~UNUSED3~]\nOutputList3SigType2091=Serial\nOutputList3Cue2092=[~UNUSED3~]\nOutputList3SigType2092=Serial\nOutputList3Cue2093=[~UNUSED3~]\nOutputList3SigType2093=Serial\nOutputList3Cue2094=[~UNUSED3~]\nOutputList3SigType2094=Serial\nOutputList3Cue2095=[~UNUSED3~]\nOutputList3SigType2095=Serial\nOutputList3Cue2096=[~UNUSED3~]\nOutputList3SigType2096=Serial\nOutputList3Cue2097=[~UNUSED3~]\nOutputList3SigType2097=Serial\nOutputList3Cue2098=[~UNUSED3~]\nOutputList3SigType2098=Serial\nOutputList3Cue2099=[~UNUSED3~]\nOutputList3SigType2099=Serial\nOutputList3Cue2100=[~UNUSED3~]\nOutputList3SigType2100=Serial\nOutputList3Cue2101=[~UNUSED3~]\nOutputList3SigType2101=Serial\nOutputList3Cue2102=[~UNUSED3~]\nOutputList3SigType2102=Serial\nOutputList3Cue2103=[~UNUSED3~]\nOutputList3SigType2103=Serial\nOutputList3Cue2104=[~UNUSED3~]\nOutputList3SigType2104=Serial\nOutputList3Cue2105=[~UNUSED3~]\nOutputList3SigType2105=Serial\nOutputList3Cue2106=[~UNUSED3~]\nOutputList3SigType2106=Serial\nOutputList3Cue2107=[~UNUSED3~]\nOutputList3SigType2107=Serial\nOutputList3Cue2108=[~UNUSED3~]\nOutputList3SigType2108=Serial\nOutputList3Cue2109=[~UNUSED3~]\nOutputList3SigType2109=Serial\nOutputList3Cue2110=[~UNUSED3~]\nOutputList3SigType2110=Serial\nOutputList3Cue2111=[~UNUSED3~]\nOutputList3SigType2111=Serial\nOutputList3Cue2112=[~UNUSED3~]\nOutputList3SigType2112=Serial\nOutputList3Cue2113=[~UNUSED3~]\nOutputList3SigType2113=Serial\nOutputList3Cue2114=[~UNUSED3~]\nOutputList3SigType2114=Serial\nOutputList3Cue2115=[~UNUSED3~]\nOutputList3SigType2115=Serial\nOutputList3Cue2116=[~UNUSED3~]\nOutputList3SigType2116=Serial\nOutputList3Cue2117=[~UNUSED3~]\nOutputList3SigType2117=Serial\nOutputList3Cue2118=[~UNUSED3~]\nOutputList3SigType2118=Serial\nOutputList3Cue2119=[~UNUSED3~]\nOutputList3SigType2119=Serial\nOutputList3Cue2120=[~UNUSED3~]\nOutputList3SigType2120=Serial\nOutputList3Cue2121=[~UNUSED3~]\nOutputList3SigType2121=Serial\nOutputList3Cue2122=[~UNUSED3~]\nOutputList3SigType2122=Serial\nOutputList3Cue2123=[~UNUSED3~]\nOutputList3SigType2123=Serial\nOutputList3Cue2124=[~UNUSED3~]\nOutputList3SigType2124=Serial\nOutputList3Cue2125=[~UNUSED3~]\nOutputList3SigType2125=Serial\nOutputList3Cue2126=[~UNUSED3~]\nOutputList3SigType2126=Serial\nOutputList3Cue2127=[~UNUSED3~]\nOutputList3SigType2127=Serial\nOutputList3Cue2128=[~UNUSED3~]\nOutputList3SigType2128=Serial\nOutputList3Cue2129=[~UNUSED3~]\nOutputList3SigType2129=Serial\nOutputList3Cue2130=[~UNUSED3~]\nOutputList3SigType2130=Serial\nOutputList3Cue2131=[~UNUSED3~]\nOutputList3SigType2131=Serial\nOutputList3Cue2132=[~UNUSED3~]\nOutputList3SigType2132=Serial\nOutputList3Cue2133=[~UNUSED3~]\nOutputList3SigType2133=Serial\nOutputList3Cue2134=[~UNUSED3~]\nOutputList3SigType2134=Serial\nOutputList3Cue2135=[~UNUSED3~]\nOutputList3SigType2135=Serial\nOutputList3Cue2136=[~UNUSED3~]\nOutputList3SigType2136=Serial\nOutputList3Cue2137=[~UNUSED3~]\nOutputList3SigType2137=Serial\nOutputList3Cue2138=[~UNUSED3~]\nOutputList3SigType2138=Serial\nOutputList3Cue2139=[~UNUSED3~]\nOutputList3SigType2139=Serial\nOutputList3Cue2140=[~UNUSED3~]\nOutputList3SigType2140=Serial\nOutputList3Cue2141=[~UNUSED3~]\nOutputList3SigType2141=Serial\nOutputList3Cue2142=[~UNUSED3~]\nOutputList3SigType2142=Serial\nOutputList3Cue2143=[~UNUSED3~]\nOutputList3SigType2143=Serial\nOutputList3Cue2144=[~UNUSED3~]\nOutputList3SigType2144=Serial\nOutputList3Cue2145=[~UNUSED3~]\nOutputList3SigType2145=Serial\nOutputList3Cue2146=[~UNUSED3~]\nOutputList3SigType2146=Serial\nOutputList3Cue2147=[~UNUSED3~]\nOutputList3SigType2147=Serial\nOutputList3Cue2148=[~UNUSED3~]\nOutputList3SigType2148=Serial\nOutputList3Cue2149=[~UNUSED3~]\nOutputList3SigType2149=Serial\nOutputList3Cue2150=[~UNUSED3~]\nOutputList3SigType2150=Serial\nOutputList3Cue2151=[~UNUSED3~]\nOutputList3SigType2151=Serial\nOutputList3Cue2152=[~UNUSED3~]\nOutputList3SigType2152=Serial\nOutputList3Cue2153=[~UNUSED3~]\nOutputList3SigType2153=Serial\nOutputList3Cue2154=[~UNUSED3~]\nOutputList3SigType2154=Serial\nOutputList3Cue2155=[~UNUSED3~]\nOutputList3SigType2155=Serial\nOutputList3Cue2156=[~UNUSED3~]\nOutputList3SigType2156=Serial\nOutputList3Cue2157=[~UNUSED3~]\nOutputList3SigType2157=Serial\nOutputList3Cue2158=[~UNUSED3~]\nOutputList3SigType2158=Serial\nOutputList3Cue2159=[~UNUSED3~]\nOutputList3SigType2159=Serial\nOutputList3Cue2160=[~UNUSED3~]\nOutputList3SigType2160=Serial\nOutputList3Cue2161=[~UNUSED3~]\nOutputList3SigType2161=Serial\nOutputList3Cue2162=[~UNUSED3~]\nOutputList3SigType2162=Serial\nOutputList3Cue2163=[~UNUSED3~]\nOutputList3SigType2163=Serial\nOutputList3Cue2164=[~UNUSED3~]\nOutputList3SigType2164=Serial\nOutputList3Cue2165=[~UNUSED3~]\nOutputList3SigType2165=Serial\nOutputList3Cue2166=[~UNUSED3~]\nOutputList3SigType2166=Serial\nOutputList3Cue2167=[~UNUSED3~]\nOutputList3SigType2167=Serial\nOutputList3Cue2168=[~UNUSED3~]\nOutputList3SigType2168=Serial\nOutputList3Cue2169=[~UNUSED3~]\nOutputList3SigType2169=Serial\nOutputList3Cue2170=[~UNUSED3~]\nOutputList3SigType2170=Serial\nOutputList3Cue2171=[~UNUSED3~]\nOutputList3SigType2171=Serial\nOutputList3Cue2172=[~UNUSED3~]\nOutputList3SigType2172=Serial\nOutputList3Cue2173=[~UNUSED3~]\nOutputList3SigType2173=Serial\nOutputList3Cue2174=[~UNUSED3~]\nOutputList3SigType2174=Serial\nOutputList3Cue2175=[~UNUSED3~]\nOutputList3SigType2175=Serial\nOutputList3Cue2176=[~UNUSED3~]\nOutputList3SigType2176=Serial\nOutputList3Cue2177=[~UNUSED3~]\nOutputList3SigType2177=Serial\nOutputList3Cue2178=[~UNUSED3~]\nOutputList3SigType2178=Serial\nOutputList3Cue2179=[~UNUSED3~]\nOutputList3SigType2179=Serial\nOutputList3Cue2180=[~UNUSED3~]\nOutputList3SigType2180=Serial\nOutputList3Cue2181=[~UNUSED3~]\nOutputList3SigType2181=Serial\nOutputList3Cue2182=[~UNUSED3~]\nOutputList3SigType2182=Serial\nOutputList3Cue2183=[~UNUSED3~]\nOutputList3SigType2183=Serial\nOutputList3Cue2184=[~UNUSED3~]\nOutputList3SigType2184=Serial\nOutputList3Cue2185=[~UNUSED3~]\nOutputList3SigType2185=Serial\nOutputList3Cue2186=[~UNUSED3~]\nOutputList3SigType2186=Serial\nOutputList3Cue2187=[~UNUSED3~]\nOutputList3SigType2187=Serial\nOutputList3Cue2188=[~UNUSED3~]\nOutputList3SigType2188=Serial\nOutputList3Cue2189=[~UNUSED3~]\nOutputList3SigType2189=Serial\nOutputList3Cue2190=[~UNUSED3~]\nOutputList3SigType2190=Serial\nOutputList3Cue2191=[~UNUSED3~]\nOutputList3SigType2191=Serial\nOutputList3Cue2192=[~UNUSED3~]\nOutputList3SigType2192=Serial\nOutputList3Cue2193=[~UNUSED3~]\nOutputList3SigType2193=Serial\nOutputList3Cue2194=[~UNUSED3~]\nOutputList3SigType2194=Serial\nOutputList3Cue2195=[~UNUSED3~]\nOutputList3SigType2195=Serial\nOutputList3Cue2196=[~UNUSED3~]\nOutputList3SigType2196=Serial\nOutputList3Cue2197=[~UNUSED3~]\nOutputList3SigType2197=Serial\nOutputList3Cue2198=[~UNUSED3~]\nOutputList3SigType2198=Serial\nOutputList3Cue2199=[~UNUSED3~]\nOutputList3SigType2199=Serial\nOutputList3Cue2200=[~UNUSED3~]\nOutputList3SigType2200=Serial\nOutputList3Cue2201=[~UNUSED3~]\nOutputList3SigType2201=Serial\nOutputList3Cue2202=[~UNUSED3~]\nOutputList3SigType2202=Serial\nOutputList3Cue2203=[~UNUSED3~]\nOutputList3SigType2203=Serial\nOutputList3Cue2204=[~UNUSED3~]\nOutputList3SigType2204=Serial\nOutputList3Cue2205=[~UNUSED3~]\nOutputList3SigType2205=Serial\nOutputList3Cue2206=[~UNUSED3~]\nOutputList3SigType2206=Serial\nOutputList3Cue2207=[~UNUSED3~]\nOutputList3SigType2207=Serial\nOutputList3Cue2208=[~UNUSED3~]\nOutputList3SigType2208=Serial\nOutputList3Cue2209=[~UNUSED3~]\nOutputList3SigType2209=Serial\nOutputList3Cue2210=[~UNUSED3~]\nOutputList3SigType2210=Serial\nOutputList3Cue2211=[~UNUSED3~]\nOutputList3SigType2211=Serial\nOutputList3Cue2212=[~UNUSED3~]\nOutputList3SigType2212=Serial\nOutputList3Cue2213=[~UNUSED3~]\nOutputList3SigType2213=Serial\nOutputList3Cue2214=[~UNUSED3~]\nOutputList3SigType2214=Serial\nOutputList3Cue2215=[~UNUSED3~]\nOutputList3SigType2215=Serial\nOutputList3Cue2216=[~UNUSED3~]\nOutputList3SigType2216=Serial\nOutputList3Cue2217=[~UNUSED3~]\nOutputList3SigType2217=Serial\nOutputList3Cue2218=[~UNUSED3~]\nOutputList3SigType2218=Serial\nOutputList3Cue2219=[~UNUSED3~]\nOutputList3SigType2219=Serial\nOutputList3Cue2220=[~UNUSED3~]\nOutputList3SigType2220=Serial\nOutputList3Cue2221=[~UNUSED3~]\nOutputList3SigType2221=Serial\nOutputList3Cue2222=[~UNUSED3~]\nOutputList3SigType2222=Serial\nOutputList3Cue2223=[~UNUSED3~]\nOutputList3SigType2223=Serial\nOutputList3Cue2224=[~UNUSED3~]\nOutputList3SigType2224=Serial\nOutputList3Cue2225=[~UNUSED3~]\nOutputList3SigType2225=Serial\nOutputList3Cue2226=[~UNUSED3~]\nOutputList3SigType2226=Serial\nOutputList3Cue2227=[~UNUSED3~]\nOutputList3SigType2227=Serial\nOutputList3Cue2228=[~UNUSED3~]\nOutputList3SigType2228=Serial\nOutputList3Cue2229=[~UNUSED3~]\nOutputList3SigType2229=Serial\nOutputList3Cue2230=[~UNUSED3~]\nOutputList3SigType2230=Serial\nOutputList3Cue2231=[~UNUSED3~]\nOutputList3SigType2231=Serial\nOutputList3Cue2232=[~UNUSED3~]\nOutputList3SigType2232=Serial\nOutputList3Cue2233=[~UNUSED3~]\nOutputList3SigType2233=Serial\nOutputList3Cue2234=[~UNUSED3~]\nOutputList3SigType2234=Serial\nOutputList3Cue2235=[~UNUSED3~]\nOutputList3SigType2235=Serial\nOutputList3Cue2236=[~UNUSED3~]\nOutputList3SigType2236=Serial\nOutputList3Cue2237=[~UNUSED3~]\nOutputList3SigType2237=Serial\nOutputList3Cue2238=[~UNUSED3~]\nOutputList3SigType2238=Serial\nOutputList3Cue2239=[~UNUSED3~]\nOutputList3SigType2239=Serial\nOutputList3Cue2240=[~UNUSED3~]\nOutputList3SigType2240=Serial\nOutputList3Cue2241=[~UNUSED3~]\nOutputList3SigType2241=Serial\nOutputList3Cue2242=[~UNUSED3~]\nOutputList3SigType2242=Serial\nOutputList3Cue2243=[~UNUSED3~]\nOutputList3SigType2243=Serial\nOutputList3Cue2244=[~UNUSED3~]\nOutputList3SigType2244=Serial\nOutputList3Cue2245=[~UNUSED3~]\nOutputList3SigType2245=Serial\nOutputList3Cue2246=[~UNUSED3~]\nOutputList3SigType2246=Serial\nOutputList3Cue2247=[~UNUSED3~]\nOutputList3SigType2247=Serial\nOutputList3Cue2248=[~UNUSED3~]\nOutputList3SigType2248=Serial\nOutputList3Cue2249=[~UNUSED3~]\nOutputList3SigType2249=Serial\nOutputList3Cue2250=[~UNUSED3~]\nOutputList3SigType2250=Serial\nOutputList3Cue2251=[~UNUSED3~]\nOutputList3SigType2251=Serial\nOutputList3Cue2252=[~UNUSED3~]\nOutputList3SigType2252=Serial\nOutputList3Cue2253=[~UNUSED3~]\nOutputList3SigType2253=Serial\nOutputList3Cue2254=[~UNUSED3~]\nOutputList3SigType2254=Serial\nOutputList3Cue2255=[~UNUSED3~]\nOutputList3SigType2255=Serial\nOutputList3Cue2256=[~UNUSED3~]\nOutputList3SigType2256=Serial\nOutputList3Cue2257=[~UNUSED3~]\nOutputList3SigType2257=Serial\nOutputList3Cue2258=[~UNUSED3~]\nOutputList3SigType2258=Serial\nOutputList3Cue2259=[~UNUSED3~]\nOutputList3SigType2259=Serial\nOutputList3Cue2260=[~UNUSED3~]\nOutputList3SigType2260=Serial\nOutputList3Cue2261=[~UNUSED3~]\nOutputList3SigType2261=Serial\nOutputList3Cue2262=[~UNUSED3~]\nOutputList3SigType2262=Serial\nOutputList3Cue2263=[~UNUSED3~]\nOutputList3SigType2263=Serial\nOutputList3Cue2264=[~UNUSED3~]\nOutputList3SigType2264=Serial\nOutputList3Cue2265=[~UNUSED3~]\nOutputList3SigType2265=Serial\nOutputList3Cue2266=[~UNUSED3~]\nOutputList3SigType2266=Serial\nOutputList3Cue2267=[~UNUSED3~]\nOutputList3SigType2267=Serial\nOutputList3Cue2268=[~UNUSED3~]\nOutputList3SigType2268=Serial\nOutputList3Cue2269=[~UNUSED2~]\nOutputList3SigType2269=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=2\nCedH=2\nSmartObjId=1202d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=2\nTp=1\nHD=TRUE\nDV=1202d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=2\nName=PepperDash Essentials iPad_[D.VC] Directory_Dynamic Button List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\nHint=VC DTMF Keypad (Smart Object ID=1201)\nCode=3\nSGControlType=Simple Keypad\nSGControlName=VC DTMF Keypad\nGUID=B4D53525-26EF-4326-8234-1D53F6E9BF04\nSmplCName=PepperDash Essentials iPad_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=*\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=/#\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=3\nCedH=3\nSmartObjId=1201d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=3\nTp=1\nHD=TRUE\nDV=1201d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=3\nName=PepperDash Essentials iPad_[D.VC] Keypad with favorites_VC DTMF Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[Z.Dialogs] PIN_PIN Keypad.ced\nHint=PIN Keypad (Smart Object ID=3903)\nCode=4\nSGControlType=Simple Keypad\nSGControlName=PIN Keypad\nGUID=D85E5DFB-DE45-45FE-93C5-2FA177B68BFB\nSmplCName=PepperDash Essentials iPad_[Z.Dialogs] PIN_PIN Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=Misc_1\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=Misc_2\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=4\nCedH=4\nSmartObjId=3903d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=4\nTp=1\nHD=TRUE\nDV=3903d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=4\nName=PepperDash Essentials iPad_[Z.Dialogs] PIN_PIN Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\nHint=Subpage Reference List Vertical_3 (Smart Object ID=3902)\nCode=5\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_3\nGUID=3490D547-0B98-444B-A284-D6C1BAB2FDCE\nSmplCName=PepperDash Essentials iPad_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4076\nMaxVariableInputs=4076\nMinVariableOutputs=4076\nMaxVariableOutputs=4076\nNumFixedParams=1\nMinVariableInputsList2=72\nMaxVariableInputsList2=72\nMinVariableOutputsList2=72\nMaxVariableOutputsList2=72\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=[~UNUSED2~]\nInputSigType32=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=[~UNUSED2~]\nInputSigType2034=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=[~EndGroup~]fb\nInputSigType4076=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED2~]\nOutputSigType32=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED2~]\nOutputSigType2034=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=[~EndGroup~]Press\nOutputSigType4076=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=an_fb37\nInputList2SigType48=Analog\nInputList2Cue49=an_fb38\nInputList2SigType49=Analog\nInputList2Cue50=an_fb39\nInputList2SigType50=Analog\nInputList2Cue51=an_fb40\nInputList2SigType51=Analog\nInputList2Cue52=an_fb41\nInputList2SigType52=Analog\nInputList2Cue53=an_fb42\nInputList2SigType53=Analog\nInputList2Cue54=an_fb43\nInputList2SigType54=Analog\nInputList2Cue55=an_fb44\nInputList2SigType55=Analog\nInputList2Cue56=an_fb45\nInputList2SigType56=Analog\nInputList2Cue57=an_fb46\nInputList2SigType57=Analog\nInputList2Cue58=an_fb47\nInputList2SigType58=Analog\nInputList2Cue59=an_fb48\nInputList2SigType59=Analog\nInputList2Cue60=an_fb49\nInputList2SigType60=Analog\nInputList2Cue61=an_fb50\nInputList2SigType61=Analog\nInputList2Cue62=an_fb51\nInputList2SigType62=Analog\nInputList2Cue63=an_fb52\nInputList2SigType63=Analog\nInputList2Cue64=an_fb53\nInputList2SigType64=Analog\nInputList2Cue65=an_fb54\nInputList2SigType65=Analog\nInputList2Cue66=an_fb55\nInputList2SigType66=Analog\nInputList2Cue67=an_fb56\nInputList2SigType67=Analog\nInputList2Cue68=an_fb57\nInputList2SigType68=Analog\nInputList2Cue69=an_fb58\nInputList2SigType69=Analog\nInputList2Cue70=an_fb59\nInputList2SigType70=Analog\nInputList2Cue71=an_fb60\nInputList2SigType71=Analog\nInputList2Cue72=[~EndGroup~]an_fb\nInputList2SigType72=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=an_act37\nOutputList2SigType48=Analog\nOutputList2Cue49=an_act38\nOutputList2SigType49=Analog\nOutputList2Cue50=an_act39\nOutputList2SigType50=Analog\nOutputList2Cue51=an_act40\nOutputList2SigType51=Analog\nOutputList2Cue52=an_act41\nOutputList2SigType52=Analog\nOutputList2Cue53=an_act42\nOutputList2SigType53=Analog\nOutputList2Cue54=an_act43\nOutputList2SigType54=Analog\nOutputList2Cue55=an_act44\nOutputList2SigType55=Analog\nOutputList2Cue56=an_act45\nOutputList2SigType56=Analog\nOutputList2Cue57=an_act46\nOutputList2SigType57=Analog\nOutputList2Cue58=an_act47\nOutputList2SigType58=Analog\nOutputList2Cue59=an_act48\nOutputList2SigType59=Analog\nOutputList2Cue60=an_act49\nOutputList2SigType60=Analog\nOutputList2Cue61=an_act50\nOutputList2SigType61=Analog\nOutputList2Cue62=an_act51\nOutputList2SigType62=Analog\nOutputList2Cue63=an_act52\nOutputList2SigType63=Analog\nOutputList2Cue64=an_act53\nOutputList2SigType64=Analog\nOutputList2Cue65=an_act54\nOutputList2SigType65=Analog\nOutputList2Cue66=an_act55\nOutputList2SigType66=Analog\nOutputList2Cue67=an_act56\nOutputList2SigType67=Analog\nOutputList2Cue68=an_act57\nOutputList2SigType68=Analog\nOutputList2Cue69=an_act58\nOutputList2SigType69=Analog\nOutputList2Cue70=an_act59\nOutputList2SigType70=Analog\nOutputList2Cue71=an_act60\nOutputList2SigType71=Analog\nOutputList2Cue72=[~EndGroup~]an_act\nOutputList2SigType72=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=5\nCedH=5\nSmartObjId=3902d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=5\nTp=1\nHD=TRUE\nDV=3902d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=5\nName=PepperDash Essentials iPad_[E.Technician Controls] Sys Status_Subpage Reference List Vertical_3.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\nHint=Subpage Reference List Vertical_4 (Smart Object ID=3904)\nCode=6\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_4\nGUID=AC10DF35-3FD4-4F62-9708-0A06AE7A5A14\nSmplCName=PepperDash Essentials iPad_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4116\nMaxVariableInputs=4116\nMinVariableOutputs=4116\nMaxVariableOutputs=4116\nNumFixedParams=1\nMinVariableInputsList2=42\nMaxVariableInputsList2=42\nMinVariableOutputsList2=42\nMaxVariableOutputsList2=42\nMinVariableInputsList3=42\nMaxVariableInputsList3=42\nMinVariableOutputsList3=42\nMaxVariableOutputsList3=42\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=[~UNUSED2~]\nInputSigType22=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=[~UNUSED2~]\nInputSigType2024=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=fb91\nInputSigType4106=Digital\nInputCue4107=fb92\nInputSigType4107=Digital\nInputCue4108=fb93\nInputSigType4108=Digital\nInputCue4109=fb94\nInputSigType4109=Digital\nInputCue4110=fb95\nInputSigType4110=Digital\nInputCue4111=fb96\nInputSigType4111=Digital\nInputCue4112=fb97\nInputSigType4112=Digital\nInputCue4113=fb98\nInputSigType4113=Digital\nInputCue4114=fb99\nInputSigType4114=Digital\nInputCue4115=fb100\nInputSigType4115=Digital\nInputCue4116=[~EndGroup~]fb\nInputSigType4116=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED2~]\nOutputSigType22=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED2~]\nOutputSigType2024=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=press91\nOutputSigType4106=Digital\nOutputCue4107=press92\nOutputSigType4107=Digital\nOutputCue4108=press93\nOutputSigType4108=Digital\nOutputCue4109=press94\nOutputSigType4109=Digital\nOutputCue4110=press95\nOutputSigType4110=Digital\nOutputCue4111=press96\nOutputSigType4111=Digital\nOutputCue4112=press97\nOutputSigType4112=Digital\nOutputCue4113=press98\nOutputSigType4113=Digital\nOutputCue4114=press99\nOutputSigType4114=Digital\nOutputCue4115=press100\nOutputSigType4115=Digital\nOutputCue4116=[~EndGroup~]Press\nOutputSigType4116=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=[~EndGroup~]an_fb\nInputList2SigType42=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=[~EndGroup~]an_act\nOutputList2SigType42=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=[~EndGroup~]text-o\nInputList3SigType42=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=[~EndGroup~]text-i\nOutputList3SigType42=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=6\nCedH=6\nSmartObjId=3904d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=6\nTp=1\nHD=TRUE\nDV=3904d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=6\nName=PepperDash Essentials iPad_[E.Technician Controls] Display Controls_Subpage Reference List Vertical_4.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\nHint=Subpage Reference List Horizontal_3 (Smart Object ID=3200)\nCode=7\nSGControlType=Subpage Reference List Horizontal\nSGControlName=Subpage Reference List Horizontal_3\nGUID=D638D437-6722-43E1-B435-EF13DCE5ECF0\nSmplCName=PepperDash Essentials iPad_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10124\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4076\nMaxVariableInputs=4076\nMinVariableOutputs=4076\nMaxVariableOutputs=4076\nNumFixedParams=1\nMinVariableInputsList2=72\nMaxVariableInputsList2=72\nMinVariableOutputsList2=72\nMaxVariableOutputsList2=72\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=[~UNUSED2~]\nInputSigType32=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=[~UNUSED2~]\nInputSigType2034=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=[~EndGroup~]fb\nInputSigType4076=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED2~]\nOutputSigType32=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED2~]\nOutputSigType2034=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=[~EndGroup~]Press\nOutputSigType4076=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=an_fb37\nInputList2SigType48=Analog\nInputList2Cue49=an_fb38\nInputList2SigType49=Analog\nInputList2Cue50=an_fb39\nInputList2SigType50=Analog\nInputList2Cue51=an_fb40\nInputList2SigType51=Analog\nInputList2Cue52=an_fb41\nInputList2SigType52=Analog\nInputList2Cue53=an_fb42\nInputList2SigType53=Analog\nInputList2Cue54=an_fb43\nInputList2SigType54=Analog\nInputList2Cue55=an_fb44\nInputList2SigType55=Analog\nInputList2Cue56=an_fb45\nInputList2SigType56=Analog\nInputList2Cue57=an_fb46\nInputList2SigType57=Analog\nInputList2Cue58=an_fb47\nInputList2SigType58=Analog\nInputList2Cue59=an_fb48\nInputList2SigType59=Analog\nInputList2Cue60=an_fb49\nInputList2SigType60=Analog\nInputList2Cue61=an_fb50\nInputList2SigType61=Analog\nInputList2Cue62=an_fb51\nInputList2SigType62=Analog\nInputList2Cue63=an_fb52\nInputList2SigType63=Analog\nInputList2Cue64=an_fb53\nInputList2SigType64=Analog\nInputList2Cue65=an_fb54\nInputList2SigType65=Analog\nInputList2Cue66=an_fb55\nInputList2SigType66=Analog\nInputList2Cue67=an_fb56\nInputList2SigType67=Analog\nInputList2Cue68=an_fb57\nInputList2SigType68=Analog\nInputList2Cue69=an_fb58\nInputList2SigType69=Analog\nInputList2Cue70=an_fb59\nInputList2SigType70=Analog\nInputList2Cue71=an_fb60\nInputList2SigType71=Analog\nInputList2Cue72=[~EndGroup~]an_fb\nInputList2SigType72=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=an_act37\nOutputList2SigType48=Analog\nOutputList2Cue49=an_act38\nOutputList2SigType49=Analog\nOutputList2Cue50=an_act39\nOutputList2SigType50=Analog\nOutputList2Cue51=an_act40\nOutputList2SigType51=Analog\nOutputList2Cue52=an_act41\nOutputList2SigType52=Analog\nOutputList2Cue53=an_act42\nOutputList2SigType53=Analog\nOutputList2Cue54=an_act43\nOutputList2SigType54=Analog\nOutputList2Cue55=an_act44\nOutputList2SigType55=Analog\nOutputList2Cue56=an_act45\nOutputList2SigType56=Analog\nOutputList2Cue57=an_act46\nOutputList2SigType57=Analog\nOutputList2Cue58=an_act47\nOutputList2SigType58=Analog\nOutputList2Cue59=an_act48\nOutputList2SigType59=Analog\nOutputList2Cue60=an_act49\nOutputList2SigType60=Analog\nOutputList2Cue61=an_act50\nOutputList2SigType61=Analog\nOutputList2Cue62=an_act51\nOutputList2SigType62=Analog\nOutputList2Cue63=an_act52\nOutputList2SigType63=Analog\nOutputList2Cue64=an_act53\nOutputList2SigType64=Analog\nOutputList2Cue65=an_act54\nOutputList2SigType65=Analog\nOutputList2Cue66=an_act55\nOutputList2SigType66=Analog\nOutputList2Cue67=an_act56\nOutputList2SigType67=Analog\nOutputList2Cue68=an_act57\nOutputList2SigType68=Analog\nOutputList2Cue69=an_act58\nOutputList2SigType69=Analog\nOutputList2Cue70=an_act59\nOutputList2SigType70=Analog\nOutputList2Cue71=an_act60\nOutputList2SigType71=Analog\nOutputList2Cue72=[~EndGroup~]an_act\nOutputList2SigType72=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=7\nCedH=7\nSmartObjId=3200d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=7\nTp=1\nHD=TRUE\nDV=3200d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=7\nName=PepperDash Essentials iPad_[B.AV] Staging_Subpage Reference List Horizontal_3.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\nHint=Channel Preset List Basic (Smart Object ID=1806)\nCode=8\nSGControlType=Subpage Reference List Vertical\nSGControlName=Channel Preset List Basic\nGUID=46C43CE4-6BB9-4B76-A13E-B4279202F998\nSmplCName=PepperDash Essentials iPad_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4106\nMaxVariableInputs=4106\nMinVariableOutputs=4106\nMaxVariableOutputs=4106\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=132\nMaxVariableInputsList3=132\nMinVariableOutputsList3=132\nMaxVariableOutputsList3=132\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=[~EndGroup~]fb\nInputSigType4106=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=[~EndGroup~]Press\nOutputSigType4106=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=[~EndGroup~]text-o\nInputList3SigType132=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=[~EndGroup~]text-i\nOutputList3SigType132=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=8\nCedH=8\nSmartObjId=1806d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=8\nTp=1\nHD=TRUE\nDV=1806d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=8\nName=PepperDash Essentials iPad_[B.AV] Source Ctrl-CATV-Basic_Channel Preset List Basic.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\nHint=Modes Bottom Bar SRL (Smart Object ID=15022)\nCode=9\nSGControlType=Subpage Reference List Horizontal\nSGControlName=Modes Bottom Bar SRL\nGUID=812FF0C4-486D-4ABC-90FA-405F19104323\nSmplCName=PepperDash Essentials iPad_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10124\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4028\nMaxVariableInputs=4028\nMinVariableOutputs=4028\nMaxVariableOutputs=4028\nNumFixedParams=1\nMinVariableInputsList2=24\nMaxVariableInputsList2=24\nMinVariableOutputsList2=24\nMaxVariableOutputsList2=24\nMinVariableInputsList3=24\nMaxVariableInputsList3=24\nMinVariableOutputsList3=24\nMaxVariableOutputsList3=24\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=[~UNUSED2~]\nInputSigType16=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=[~UNUSED2~]\nInputSigType2018=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=[~EndGroup~]fb\nInputSigType4028=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED2~]\nOutputSigType16=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED2~]\nOutputSigType2018=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=[~EndGroup~]Press\nOutputSigType4028=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=[~EndGroup~]an_fb\nInputList2SigType24=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=[~EndGroup~]an_act\nOutputList2SigType24=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=[~EndGroup~]text-o\nInputList3SigType24=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=[~EndGroup~]text-i\nOutputList3SigType24=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=9\nCedH=9\nSmartObjId=15022d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=9\nTp=1\nHD=TRUE\nDV=15022d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=9\nName=PepperDash Essentials iPad_[A.Global] Activity Footer SRL_Modes Bottom Bar SRL.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[B.AV] Object - Presets_CATV Icon List Vertical.ced\nHint=CATV Icon List Vertical (Smart Object ID=10012)\nCode=10\nSGControlType=Subpage Reference List Vertical\nSGControlName=CATV Icon List Vertical\nGUID=B34C4DC0-DD10-4C49-AEBA-F968934EFDC6\nSmplCName=PepperDash Essentials iPad_[B.AV] Object - Presets_CATV Icon List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4316\nMaxVariableInputs=4316\nMinVariableOutputs=4316\nMaxVariableOutputs=4316\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=412\nMaxVariableInputsList3=412\nMinVariableOutputsList3=412\nMaxVariableOutputsList3=412\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=Item 31 Enable\nInputSigType42=Digital\nInputCue43=Item 32 Enable\nInputSigType43=Digital\nInputCue44=Item 33 Enable\nInputSigType44=Digital\nInputCue45=Item 34 Enable\nInputSigType45=Digital\nInputCue46=Item 35 Enable\nInputSigType46=Digital\nInputCue47=Item 36 Enable\nInputSigType47=Digital\nInputCue48=Item 37 Enable\nInputSigType48=Digital\nInputCue49=Item 38 Enable\nInputSigType49=Digital\nInputCue50=Item 39 Enable\nInputSigType50=Digital\nInputCue51=Item 40 Enable\nInputSigType51=Digital\nInputCue52=Item 41 Enable\nInputSigType52=Digital\nInputCue53=Item 42 Enable\nInputSigType53=Digital\nInputCue54=Item 43 Enable\nInputSigType54=Digital\nInputCue55=Item 44 Enable\nInputSigType55=Digital\nInputCue56=Item 45 Enable\nInputSigType56=Digital\nInputCue57=Item 46 Enable\nInputSigType57=Digital\nInputCue58=Item 47 Enable\nInputSigType58=Digital\nInputCue59=Item 48 Enable\nInputSigType59=Digital\nInputCue60=Item 49 Enable\nInputSigType60=Digital\nInputCue61=Item 50 Enable\nInputSigType61=Digital\nInputCue62=Item 51 Enable\nInputSigType62=Digital\nInputCue63=Item 52 Enable\nInputSigType63=Digital\nInputCue64=Item 53 Enable\nInputSigType64=Digital\nInputCue65=Item 54 Enable\nInputSigType65=Digital\nInputCue66=Item 55 Enable\nInputSigType66=Digital\nInputCue67=Item 56 Enable\nInputSigType67=Digital\nInputCue68=Item 57 Enable\nInputSigType68=Digital\nInputCue69=Item 58 Enable\nInputSigType69=Digital\nInputCue70=Item 59 Enable\nInputSigType70=Digital\nInputCue71=Item 60 Enable\nInputSigType71=Digital\nInputCue72=Item 61 Enable\nInputSigType72=Digital\nInputCue73=Item 62 Enable\nInputSigType73=Digital\nInputCue74=Item 63 Enable\nInputSigType74=Digital\nInputCue75=Item 64 Enable\nInputSigType75=Digital\nInputCue76=Item 65 Enable\nInputSigType76=Digital\nInputCue77=Item 66 Enable\nInputSigType77=Digital\nInputCue78=Item 67 Enable\nInputSigType78=Digital\nInputCue79=Item 68 Enable\nInputSigType79=Digital\nInputCue80=Item 69 Enable\nInputSigType80=Digital\nInputCue81=Item 70 Enable\nInputSigType81=Digital\nInputCue82=Item 71 Enable\nInputSigType82=Digital\nInputCue83=Item 72 Enable\nInputSigType83=Digital\nInputCue84=Item 73 Enable\nInputSigType84=Digital\nInputCue85=Item 74 Enable\nInputSigType85=Digital\nInputCue86=Item 75 Enable\nInputSigType86=Digital\nInputCue87=Item 76 Enable\nInputSigType87=Digital\nInputCue88=Item 77 Enable\nInputSigType88=Digital\nInputCue89=Item 78 Enable\nInputSigType89=Digital\nInputCue90=Item 79 Enable\nInputSigType90=Digital\nInputCue91=Item 80 Enable\nInputSigType91=Digital\nInputCue92=Item 81 Enable\nInputSigType92=Digital\nInputCue93=Item 82 Enable\nInputSigType93=Digital\nInputCue94=Item 83 Enable\nInputSigType94=Digital\nInputCue95=Item 84 Enable\nInputSigType95=Digital\nInputCue96=Item 85 Enable\nInputSigType96=Digital\nInputCue97=Item 86 Enable\nInputSigType97=Digital\nInputCue98=Item 87 Enable\nInputSigType98=Digital\nInputCue99=Item 88 Enable\nInputSigType99=Digital\nInputCue100=Item 89 Enable\nInputSigType100=Digital\nInputCue101=Item 90 Enable\nInputSigType101=Digital\nInputCue102=Item 91 Enable\nInputSigType102=Digital\nInputCue103=Item 92 Enable\nInputSigType103=Digital\nInputCue104=Item 93 Enable\nInputSigType104=Digital\nInputCue105=Item 94 Enable\nInputSigType105=Digital\nInputCue106=Item 95 Enable\nInputSigType106=Digital\nInputCue107=Item 96 Enable\nInputSigType107=Digital\nInputCue108=Item 97 Enable\nInputSigType108=Digital\nInputCue109=Item 98 Enable\nInputSigType109=Digital\nInputCue110=Item 99 Enable\nInputSigType110=Digital\nInputCue111=Item 100 Enable\nInputSigType111=Digital\nInputCue112=[~UNUSED2~]\nInputSigType112=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=Item 31 Visible\nInputSigType2044=Digital\nInputCue2045=Item 32 Visible\nInputSigType2045=Digital\nInputCue2046=Item 33 Visible\nInputSigType2046=Digital\nInputCue2047=Item 34 Visible\nInputSigType2047=Digital\nInputCue2048=Item 35 Visible\nInputSigType2048=Digital\nInputCue2049=Item 36 Visible\nInputSigType2049=Digital\nInputCue2050=Item 37 Visible\nInputSigType2050=Digital\nInputCue2051=Item 38 Visible\nInputSigType2051=Digital\nInputCue2052=Item 39 Visible\nInputSigType2052=Digital\nInputCue2053=Item 40 Visible\nInputSigType2053=Digital\nInputCue2054=Item 41 Visible\nInputSigType2054=Digital\nInputCue2055=Item 42 Visible\nInputSigType2055=Digital\nInputCue2056=Item 43 Visible\nInputSigType2056=Digital\nInputCue2057=Item 44 Visible\nInputSigType2057=Digital\nInputCue2058=Item 45 Visible\nInputSigType2058=Digital\nInputCue2059=Item 46 Visible\nInputSigType2059=Digital\nInputCue2060=Item 47 Visible\nInputSigType2060=Digital\nInputCue2061=Item 48 Visible\nInputSigType2061=Digital\nInputCue2062=Item 49 Visible\nInputSigType2062=Digital\nInputCue2063=Item 50 Visible\nInputSigType2063=Digital\nInputCue2064=Item 51 Visible\nInputSigType2064=Digital\nInputCue2065=Item 52 Visible\nInputSigType2065=Digital\nInputCue2066=Item 53 Visible\nInputSigType2066=Digital\nInputCue2067=Item 54 Visible\nInputSigType2067=Digital\nInputCue2068=Item 55 Visible\nInputSigType2068=Digital\nInputCue2069=Item 56 Visible\nInputSigType2069=Digital\nInputCue2070=Item 57 Visible\nInputSigType2070=Digital\nInputCue2071=Item 58 Visible\nInputSigType2071=Digital\nInputCue2072=Item 59 Visible\nInputSigType2072=Digital\nInputCue2073=Item 60 Visible\nInputSigType2073=Digital\nInputCue2074=Item 61 Visible\nInputSigType2074=Digital\nInputCue2075=Item 62 Visible\nInputSigType2075=Digital\nInputCue2076=Item 63 Visible\nInputSigType2076=Digital\nInputCue2077=Item 64 Visible\nInputSigType2077=Digital\nInputCue2078=Item 65 Visible\nInputSigType2078=Digital\nInputCue2079=Item 66 Visible\nInputSigType2079=Digital\nInputCue2080=Item 67 Visible\nInputSigType2080=Digital\nInputCue2081=Item 68 Visible\nInputSigType2081=Digital\nInputCue2082=Item 69 Visible\nInputSigType2082=Digital\nInputCue2083=Item 70 Visible\nInputSigType2083=Digital\nInputCue2084=Item 71 Visible\nInputSigType2084=Digital\nInputCue2085=Item 72 Visible\nInputSigType2085=Digital\nInputCue2086=Item 73 Visible\nInputSigType2086=Digital\nInputCue2087=Item 74 Visible\nInputSigType2087=Digital\nInputCue2088=Item 75 Visible\nInputSigType2088=Digital\nInputCue2089=Item 76 Visible\nInputSigType2089=Digital\nInputCue2090=Item 77 Visible\nInputSigType2090=Digital\nInputCue2091=Item 78 Visible\nInputSigType2091=Digital\nInputCue2092=Item 79 Visible\nInputSigType2092=Digital\nInputCue2093=Item 80 Visible\nInputSigType2093=Digital\nInputCue2094=Item 81 Visible\nInputSigType2094=Digital\nInputCue2095=Item 82 Visible\nInputSigType2095=Digital\nInputCue2096=Item 83 Visible\nInputSigType2096=Digital\nInputCue2097=Item 84 Visible\nInputSigType2097=Digital\nInputCue2098=Item 85 Visible\nInputSigType2098=Digital\nInputCue2099=Item 86 Visible\nInputSigType2099=Digital\nInputCue2100=Item 87 Visible\nInputSigType2100=Digital\nInputCue2101=Item 88 Visible\nInputSigType2101=Digital\nInputCue2102=Item 89 Visible\nInputSigType2102=Digital\nInputCue2103=Item 90 Visible\nInputSigType2103=Digital\nInputCue2104=Item 91 Visible\nInputSigType2104=Digital\nInputCue2105=Item 92 Visible\nInputSigType2105=Digital\nInputCue2106=Item 93 Visible\nInputSigType2106=Digital\nInputCue2107=Item 94 Visible\nInputSigType2107=Digital\nInputCue2108=Item 95 Visible\nInputSigType2108=Digital\nInputCue2109=Item 96 Visible\nInputSigType2109=Digital\nInputCue2110=Item 97 Visible\nInputSigType2110=Digital\nInputCue2111=Item 98 Visible\nInputSigType2111=Digital\nInputCue2112=Item 99 Visible\nInputSigType2112=Digital\nInputCue2113=Item 100 Visible\nInputSigType2113=Digital\nInputCue2114=[~UNUSED2~]\nInputSigType2114=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=fb91\nInputSigType4106=Digital\nInputCue4107=fb92\nInputSigType4107=Digital\nInputCue4108=fb93\nInputSigType4108=Digital\nInputCue4109=fb94\nInputSigType4109=Digital\nInputCue4110=fb95\nInputSigType4110=Digital\nInputCue4111=fb96\nInputSigType4111=Digital\nInputCue4112=fb97\nInputSigType4112=Digital\nInputCue4113=fb98\nInputSigType4113=Digital\nInputCue4114=fb99\nInputSigType4114=Digital\nInputCue4115=fb100\nInputSigType4115=Digital\nInputCue4116=fb101\nInputSigType4116=Digital\nInputCue4117=fb102\nInputSigType4117=Digital\nInputCue4118=fb103\nInputSigType4118=Digital\nInputCue4119=fb104\nInputSigType4119=Digital\nInputCue4120=fb105\nInputSigType4120=Digital\nInputCue4121=fb106\nInputSigType4121=Digital\nInputCue4122=fb107\nInputSigType4122=Digital\nInputCue4123=fb108\nInputSigType4123=Digital\nInputCue4124=fb109\nInputSigType4124=Digital\nInputCue4125=fb110\nInputSigType4125=Digital\nInputCue4126=fb111\nInputSigType4126=Digital\nInputCue4127=fb112\nInputSigType4127=Digital\nInputCue4128=fb113\nInputSigType4128=Digital\nInputCue4129=fb114\nInputSigType4129=Digital\nInputCue4130=fb115\nInputSigType4130=Digital\nInputCue4131=fb116\nInputSigType4131=Digital\nInputCue4132=fb117\nInputSigType4132=Digital\nInputCue4133=fb118\nInputSigType4133=Digital\nInputCue4134=fb119\nInputSigType4134=Digital\nInputCue4135=fb120\nInputSigType4135=Digital\nInputCue4136=fb121\nInputSigType4136=Digital\nInputCue4137=fb122\nInputSigType4137=Digital\nInputCue4138=fb123\nInputSigType4138=Digital\nInputCue4139=fb124\nInputSigType4139=Digital\nInputCue4140=fb125\nInputSigType4140=Digital\nInputCue4141=fb126\nInputSigType4141=Digital\nInputCue4142=fb127\nInputSigType4142=Digital\nInputCue4143=fb128\nInputSigType4143=Digital\nInputCue4144=fb129\nInputSigType4144=Digital\nInputCue4145=fb130\nInputSigType4145=Digital\nInputCue4146=fb131\nInputSigType4146=Digital\nInputCue4147=fb132\nInputSigType4147=Digital\nInputCue4148=fb133\nInputSigType4148=Digital\nInputCue4149=fb134\nInputSigType4149=Digital\nInputCue4150=fb135\nInputSigType4150=Digital\nInputCue4151=fb136\nInputSigType4151=Digital\nInputCue4152=fb137\nInputSigType4152=Digital\nInputCue4153=fb138\nInputSigType4153=Digital\nInputCue4154=fb139\nInputSigType4154=Digital\nInputCue4155=fb140\nInputSigType4155=Digital\nInputCue4156=fb141\nInputSigType4156=Digital\nInputCue4157=fb142\nInputSigType4157=Digital\nInputCue4158=fb143\nInputSigType4158=Digital\nInputCue4159=fb144\nInputSigType4159=Digital\nInputCue4160=fb145\nInputSigType4160=Digital\nInputCue4161=fb146\nInputSigType4161=Digital\nInputCue4162=fb147\nInputSigType4162=Digital\nInputCue4163=fb148\nInputSigType4163=Digital\nInputCue4164=fb149\nInputSigType4164=Digital\nInputCue4165=fb150\nInputSigType4165=Digital\nInputCue4166=fb151\nInputSigType4166=Digital\nInputCue4167=fb152\nInputSigType4167=Digital\nInputCue4168=fb153\nInputSigType4168=Digital\nInputCue4169=fb154\nInputSigType4169=Digital\nInputCue4170=fb155\nInputSigType4170=Digital\nInputCue4171=fb156\nInputSigType4171=Digital\nInputCue4172=fb157\nInputSigType4172=Digital\nInputCue4173=fb158\nInputSigType4173=Digital\nInputCue4174=fb159\nInputSigType4174=Digital\nInputCue4175=fb160\nInputSigType4175=Digital\nInputCue4176=fb161\nInputSigType4176=Digital\nInputCue4177=fb162\nInputSigType4177=Digital\nInputCue4178=fb163\nInputSigType4178=Digital\nInputCue4179=fb164\nInputSigType4179=Digital\nInputCue4180=fb165\nInputSigType4180=Digital\nInputCue4181=fb166\nInputSigType4181=Digital\nInputCue4182=fb167\nInputSigType4182=Digital\nInputCue4183=fb168\nInputSigType4183=Digital\nInputCue4184=fb169\nInputSigType4184=Digital\nInputCue4185=fb170\nInputSigType4185=Digital\nInputCue4186=fb171\nInputSigType4186=Digital\nInputCue4187=fb172\nInputSigType4187=Digital\nInputCue4188=fb173\nInputSigType4188=Digital\nInputCue4189=fb174\nInputSigType4189=Digital\nInputCue4190=fb175\nInputSigType4190=Digital\nInputCue4191=fb176\nInputSigType4191=Digital\nInputCue4192=fb177\nInputSigType4192=Digital\nInputCue4193=fb178\nInputSigType4193=Digital\nInputCue4194=fb179\nInputSigType4194=Digital\nInputCue4195=fb180\nInputSigType4195=Digital\nInputCue4196=fb181\nInputSigType4196=Digital\nInputCue4197=fb182\nInputSigType4197=Digital\nInputCue4198=fb183\nInputSigType4198=Digital\nInputCue4199=fb184\nInputSigType4199=Digital\nInputCue4200=fb185\nInputSigType4200=Digital\nInputCue4201=fb186\nInputSigType4201=Digital\nInputCue4202=fb187\nInputSigType4202=Digital\nInputCue4203=fb188\nInputSigType4203=Digital\nInputCue4204=fb189\nInputSigType4204=Digital\nInputCue4205=fb190\nInputSigType4205=Digital\nInputCue4206=fb191\nInputSigType4206=Digital\nInputCue4207=fb192\nInputSigType4207=Digital\nInputCue4208=fb193\nInputSigType4208=Digital\nInputCue4209=fb194\nInputSigType4209=Digital\nInputCue4210=fb195\nInputSigType4210=Digital\nInputCue4211=fb196\nInputSigType4211=Digital\nInputCue4212=fb197\nInputSigType4212=Digital\nInputCue4213=fb198\nInputSigType4213=Digital\nInputCue4214=fb199\nInputSigType4214=Digital\nInputCue4215=fb200\nInputSigType4215=Digital\nInputCue4216=fb201\nInputSigType4216=Digital\nInputCue4217=fb202\nInputSigType4217=Digital\nInputCue4218=fb203\nInputSigType4218=Digital\nInputCue4219=fb204\nInputSigType4219=Digital\nInputCue4220=fb205\nInputSigType4220=Digital\nInputCue4221=fb206\nInputSigType4221=Digital\nInputCue4222=fb207\nInputSigType4222=Digital\nInputCue4223=fb208\nInputSigType4223=Digital\nInputCue4224=fb209\nInputSigType4224=Digital\nInputCue4225=fb210\nInputSigType4225=Digital\nInputCue4226=fb211\nInputSigType4226=Digital\nInputCue4227=fb212\nInputSigType4227=Digital\nInputCue4228=fb213\nInputSigType4228=Digital\nInputCue4229=fb214\nInputSigType4229=Digital\nInputCue4230=fb215\nInputSigType4230=Digital\nInputCue4231=fb216\nInputSigType4231=Digital\nInputCue4232=fb217\nInputSigType4232=Digital\nInputCue4233=fb218\nInputSigType4233=Digital\nInputCue4234=fb219\nInputSigType4234=Digital\nInputCue4235=fb220\nInputSigType4235=Digital\nInputCue4236=fb221\nInputSigType4236=Digital\nInputCue4237=fb222\nInputSigType4237=Digital\nInputCue4238=fb223\nInputSigType4238=Digital\nInputCue4239=fb224\nInputSigType4239=Digital\nInputCue4240=fb225\nInputSigType4240=Digital\nInputCue4241=fb226\nInputSigType4241=Digital\nInputCue4242=fb227\nInputSigType4242=Digital\nInputCue4243=fb228\nInputSigType4243=Digital\nInputCue4244=fb229\nInputSigType4244=Digital\nInputCue4245=fb230\nInputSigType4245=Digital\nInputCue4246=fb231\nInputSigType4246=Digital\nInputCue4247=fb232\nInputSigType4247=Digital\nInputCue4248=fb233\nInputSigType4248=Digital\nInputCue4249=fb234\nInputSigType4249=Digital\nInputCue4250=fb235\nInputSigType4250=Digital\nInputCue4251=fb236\nInputSigType4251=Digital\nInputCue4252=fb237\nInputSigType4252=Digital\nInputCue4253=fb238\nInputSigType4253=Digital\nInputCue4254=fb239\nInputSigType4254=Digital\nInputCue4255=fb240\nInputSigType4255=Digital\nInputCue4256=fb241\nInputSigType4256=Digital\nInputCue4257=fb242\nInputSigType4257=Digital\nInputCue4258=fb243\nInputSigType4258=Digital\nInputCue4259=fb244\nInputSigType4259=Digital\nInputCue4260=fb245\nInputSigType4260=Digital\nInputCue4261=fb246\nInputSigType4261=Digital\nInputCue4262=fb247\nInputSigType4262=Digital\nInputCue4263=fb248\nInputSigType4263=Digital\nInputCue4264=fb249\nInputSigType4264=Digital\nInputCue4265=fb250\nInputSigType4265=Digital\nInputCue4266=fb251\nInputSigType4266=Digital\nInputCue4267=fb252\nInputSigType4267=Digital\nInputCue4268=fb253\nInputSigType4268=Digital\nInputCue4269=fb254\nInputSigType4269=Digital\nInputCue4270=fb255\nInputSigType4270=Digital\nInputCue4271=fb256\nInputSigType4271=Digital\nInputCue4272=fb257\nInputSigType4272=Digital\nInputCue4273=fb258\nInputSigType4273=Digital\nInputCue4274=fb259\nInputSigType4274=Digital\nInputCue4275=fb260\nInputSigType4275=Digital\nInputCue4276=fb261\nInputSigType4276=Digital\nInputCue4277=fb262\nInputSigType4277=Digital\nInputCue4278=fb263\nInputSigType4278=Digital\nInputCue4279=fb264\nInputSigType4279=Digital\nInputCue4280=fb265\nInputSigType4280=Digital\nInputCue4281=fb266\nInputSigType4281=Digital\nInputCue4282=fb267\nInputSigType4282=Digital\nInputCue4283=fb268\nInputSigType4283=Digital\nInputCue4284=fb269\nInputSigType4284=Digital\nInputCue4285=fb270\nInputSigType4285=Digital\nInputCue4286=fb271\nInputSigType4286=Digital\nInputCue4287=fb272\nInputSigType4287=Digital\nInputCue4288=fb273\nInputSigType4288=Digital\nInputCue4289=fb274\nInputSigType4289=Digital\nInputCue4290=fb275\nInputSigType4290=Digital\nInputCue4291=fb276\nInputSigType4291=Digital\nInputCue4292=fb277\nInputSigType4292=Digital\nInputCue4293=fb278\nInputSigType4293=Digital\nInputCue4294=fb279\nInputSigType4294=Digital\nInputCue4295=fb280\nInputSigType4295=Digital\nInputCue4296=fb281\nInputSigType4296=Digital\nInputCue4297=fb282\nInputSigType4297=Digital\nInputCue4298=fb283\nInputSigType4298=Digital\nInputCue4299=fb284\nInputSigType4299=Digital\nInputCue4300=fb285\nInputSigType4300=Digital\nInputCue4301=fb286\nInputSigType4301=Digital\nInputCue4302=fb287\nInputSigType4302=Digital\nInputCue4303=fb288\nInputSigType4303=Digital\nInputCue4304=fb289\nInputSigType4304=Digital\nInputCue4305=fb290\nInputSigType4305=Digital\nInputCue4306=fb291\nInputSigType4306=Digital\nInputCue4307=fb292\nInputSigType4307=Digital\nInputCue4308=fb293\nInputSigType4308=Digital\nInputCue4309=fb294\nInputSigType4309=Digital\nInputCue4310=fb295\nInputSigType4310=Digital\nInputCue4311=fb296\nInputSigType4311=Digital\nInputCue4312=fb297\nInputSigType4312=Digital\nInputCue4313=fb298\nInputSigType4313=Digital\nInputCue4314=fb299\nInputSigType4314=Digital\nInputCue4315=fb300\nInputSigType4315=Digital\nInputCue4316=[~EndGroup~]fb\nInputSigType4316=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED3~]\nOutputSigType42=Digital\nOutputCue43=[~UNUSED3~]\nOutputSigType43=Digital\nOutputCue44=[~UNUSED3~]\nOutputSigType44=Digital\nOutputCue45=[~UNUSED3~]\nOutputSigType45=Digital\nOutputCue46=[~UNUSED3~]\nOutputSigType46=Digital\nOutputCue47=[~UNUSED3~]\nOutputSigType47=Digital\nOutputCue48=[~UNUSED3~]\nOutputSigType48=Digital\nOutputCue49=[~UNUSED3~]\nOutputSigType49=Digital\nOutputCue50=[~UNUSED3~]\nOutputSigType50=Digital\nOutputCue51=[~UNUSED3~]\nOutputSigType51=Digital\nOutputCue52=[~UNUSED3~]\nOutputSigType52=Digital\nOutputCue53=[~UNUSED3~]\nOutputSigType53=Digital\nOutputCue54=[~UNUSED3~]\nOutputSigType54=Digital\nOutputCue55=[~UNUSED3~]\nOutputSigType55=Digital\nOutputCue56=[~UNUSED3~]\nOutputSigType56=Digital\nOutputCue57=[~UNUSED3~]\nOutputSigType57=Digital\nOutputCue58=[~UNUSED3~]\nOutputSigType58=Digital\nOutputCue59=[~UNUSED3~]\nOutputSigType59=Digital\nOutputCue60=[~UNUSED3~]\nOutputSigType60=Digital\nOutputCue61=[~UNUSED3~]\nOutputSigType61=Digital\nOutputCue62=[~UNUSED3~]\nOutputSigType62=Digital\nOutputCue63=[~UNUSED3~]\nOutputSigType63=Digital\nOutputCue64=[~UNUSED3~]\nOutputSigType64=Digital\nOutputCue65=[~UNUSED3~]\nOutputSigType65=Digital\nOutputCue66=[~UNUSED3~]\nOutputSigType66=Digital\nOutputCue67=[~UNUSED3~]\nOutputSigType67=Digital\nOutputCue68=[~UNUSED3~]\nOutputSigType68=Digital\nOutputCue69=[~UNUSED3~]\nOutputSigType69=Digital\nOutputCue70=[~UNUSED3~]\nOutputSigType70=Digital\nOutputCue71=[~UNUSED3~]\nOutputSigType71=Digital\nOutputCue72=[~UNUSED3~]\nOutputSigType72=Digital\nOutputCue73=[~UNUSED3~]\nOutputSigType73=Digital\nOutputCue74=[~UNUSED3~]\nOutputSigType74=Digital\nOutputCue75=[~UNUSED3~]\nOutputSigType75=Digital\nOutputCue76=[~UNUSED3~]\nOutputSigType76=Digital\nOutputCue77=[~UNUSED3~]\nOutputSigType77=Digital\nOutputCue78=[~UNUSED3~]\nOutputSigType78=Digital\nOutputCue79=[~UNUSED3~]\nOutputSigType79=Digital\nOutputCue80=[~UNUSED3~]\nOutputSigType80=Digital\nOutputCue81=[~UNUSED3~]\nOutputSigType81=Digital\nOutputCue82=[~UNUSED3~]\nOutputSigType82=Digital\nOutputCue83=[~UNUSED3~]\nOutputSigType83=Digital\nOutputCue84=[~UNUSED3~]\nOutputSigType84=Digital\nOutputCue85=[~UNUSED3~]\nOutputSigType85=Digital\nOutputCue86=[~UNUSED3~]\nOutputSigType86=Digital\nOutputCue87=[~UNUSED3~]\nOutputSigType87=Digital\nOutputCue88=[~UNUSED3~]\nOutputSigType88=Digital\nOutputCue89=[~UNUSED3~]\nOutputSigType89=Digital\nOutputCue90=[~UNUSED3~]\nOutputSigType90=Digital\nOutputCue91=[~UNUSED3~]\nOutputSigType91=Digital\nOutputCue92=[~UNUSED3~]\nOutputSigType92=Digital\nOutputCue93=[~UNUSED3~]\nOutputSigType93=Digital\nOutputCue94=[~UNUSED3~]\nOutputSigType94=Digital\nOutputCue95=[~UNUSED3~]\nOutputSigType95=Digital\nOutputCue96=[~UNUSED3~]\nOutputSigType96=Digital\nOutputCue97=[~UNUSED3~]\nOutputSigType97=Digital\nOutputCue98=[~UNUSED3~]\nOutputSigType98=Digital\nOutputCue99=[~UNUSED3~]\nOutputSigType99=Digital\nOutputCue100=[~UNUSED3~]\nOutputSigType100=Digital\nOutputCue101=[~UNUSED3~]\nOutputSigType101=Digital\nOutputCue102=[~UNUSED3~]\nOutputSigType102=Digital\nOutputCue103=[~UNUSED3~]\nOutputSigType103=Digital\nOutputCue104=[~UNUSED3~]\nOutputSigType104=Digital\nOutputCue105=[~UNUSED3~]\nOutputSigType105=Digital\nOutputCue106=[~UNUSED3~]\nOutputSigType106=Digital\nOutputCue107=[~UNUSED3~]\nOutputSigType107=Digital\nOutputCue108=[~UNUSED3~]\nOutputSigType108=Digital\nOutputCue109=[~UNUSED3~]\nOutputSigType109=Digital\nOutputCue110=[~UNUSED3~]\nOutputSigType110=Digital\nOutputCue111=[~UNUSED3~]\nOutputSigType111=Digital\nOutputCue112=[~UNUSED2~]\nOutputSigType112=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED3~]\nOutputSigType2044=Digital\nOutputCue2045=[~UNUSED3~]\nOutputSigType2045=Digital\nOutputCue2046=[~UNUSED3~]\nOutputSigType2046=Digital\nOutputCue2047=[~UNUSED3~]\nOutputSigType2047=Digital\nOutputCue2048=[~UNUSED3~]\nOutputSigType2048=Digital\nOutputCue2049=[~UNUSED3~]\nOutputSigType2049=Digital\nOutputCue2050=[~UNUSED3~]\nOutputSigType2050=Digital\nOutputCue2051=[~UNUSED3~]\nOutputSigType2051=Digital\nOutputCue2052=[~UNUSED3~]\nOutputSigType2052=Digital\nOutputCue2053=[~UNUSED3~]\nOutputSigType2053=Digital\nOutputCue2054=[~UNUSED3~]\nOutputSigType2054=Digital\nOutputCue2055=[~UNUSED3~]\nOutputSigType2055=Digital\nOutputCue2056=[~UNUSED3~]\nOutputSigType2056=Digital\nOutputCue2057=[~UNUSED3~]\nOutputSigType2057=Digital\nOutputCue2058=[~UNUSED3~]\nOutputSigType2058=Digital\nOutputCue2059=[~UNUSED3~]\nOutputSigType2059=Digital\nOutputCue2060=[~UNUSED3~]\nOutputSigType2060=Digital\nOutputCue2061=[~UNUSED3~]\nOutputSigType2061=Digital\nOutputCue2062=[~UNUSED3~]\nOutputSigType2062=Digital\nOutputCue2063=[~UNUSED3~]\nOutputSigType2063=Digital\nOutputCue2064=[~UNUSED3~]\nOutputSigType2064=Digital\nOutputCue2065=[~UNUSED3~]\nOutputSigType2065=Digital\nOutputCue2066=[~UNUSED3~]\nOutputSigType2066=Digital\nOutputCue2067=[~UNUSED3~]\nOutputSigType2067=Digital\nOutputCue2068=[~UNUSED3~]\nOutputSigType2068=Digital\nOutputCue2069=[~UNUSED3~]\nOutputSigType2069=Digital\nOutputCue2070=[~UNUSED3~]\nOutputSigType2070=Digital\nOutputCue2071=[~UNUSED3~]\nOutputSigType2071=Digital\nOutputCue2072=[~UNUSED3~]\nOutputSigType2072=Digital\nOutputCue2073=[~UNUSED3~]\nOutputSigType2073=Digital\nOutputCue2074=[~UNUSED3~]\nOutputSigType2074=Digital\nOutputCue2075=[~UNUSED3~]\nOutputSigType2075=Digital\nOutputCue2076=[~UNUSED3~]\nOutputSigType2076=Digital\nOutputCue2077=[~UNUSED3~]\nOutputSigType2077=Digital\nOutputCue2078=[~UNUSED3~]\nOutputSigType2078=Digital\nOutputCue2079=[~UNUSED3~]\nOutputSigType2079=Digital\nOutputCue2080=[~UNUSED3~]\nOutputSigType2080=Digital\nOutputCue2081=[~UNUSED3~]\nOutputSigType2081=Digital\nOutputCue2082=[~UNUSED3~]\nOutputSigType2082=Digital\nOutputCue2083=[~UNUSED3~]\nOutputSigType2083=Digital\nOutputCue2084=[~UNUSED3~]\nOutputSigType2084=Digital\nOutputCue2085=[~UNUSED3~]\nOutputSigType2085=Digital\nOutputCue2086=[~UNUSED3~]\nOutputSigType2086=Digital\nOutputCue2087=[~UNUSED3~]\nOutputSigType2087=Digital\nOutputCue2088=[~UNUSED3~]\nOutputSigType2088=Digital\nOutputCue2089=[~UNUSED3~]\nOutputSigType2089=Digital\nOutputCue2090=[~UNUSED3~]\nOutputSigType2090=Digital\nOutputCue2091=[~UNUSED3~]\nOutputSigType2091=Digital\nOutputCue2092=[~UNUSED3~]\nOutputSigType2092=Digital\nOutputCue2093=[~UNUSED3~]\nOutputSigType2093=Digital\nOutputCue2094=[~UNUSED3~]\nOutputSigType2094=Digital\nOutputCue2095=[~UNUSED3~]\nOutputSigType2095=Digital\nOutputCue2096=[~UNUSED3~]\nOutputSigType2096=Digital\nOutputCue2097=[~UNUSED3~]\nOutputSigType2097=Digital\nOutputCue2098=[~UNUSED3~]\nOutputSigType2098=Digital\nOutputCue2099=[~UNUSED3~]\nOutputSigType2099=Digital\nOutputCue2100=[~UNUSED3~]\nOutputSigType2100=Digital\nOutputCue2101=[~UNUSED3~]\nOutputSigType2101=Digital\nOutputCue2102=[~UNUSED3~]\nOutputSigType2102=Digital\nOutputCue2103=[~UNUSED3~]\nOutputSigType2103=Digital\nOutputCue2104=[~UNUSED3~]\nOutputSigType2104=Digital\nOutputCue2105=[~UNUSED3~]\nOutputSigType2105=Digital\nOutputCue2106=[~UNUSED3~]\nOutputSigType2106=Digital\nOutputCue2107=[~UNUSED3~]\nOutputSigType2107=Digital\nOutputCue2108=[~UNUSED3~]\nOutputSigType2108=Digital\nOutputCue2109=[~UNUSED3~]\nOutputSigType2109=Digital\nOutputCue2110=[~UNUSED3~]\nOutputSigType2110=Digital\nOutputCue2111=[~UNUSED3~]\nOutputSigType2111=Digital\nOutputCue2112=[~UNUSED3~]\nOutputSigType2112=Digital\nOutputCue2113=[~UNUSED3~]\nOutputSigType2113=Digital\nOutputCue2114=[~UNUSED2~]\nOutputSigType2114=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=press91\nOutputSigType4106=Digital\nOutputCue4107=press92\nOutputSigType4107=Digital\nOutputCue4108=press93\nOutputSigType4108=Digital\nOutputCue4109=press94\nOutputSigType4109=Digital\nOutputCue4110=press95\nOutputSigType4110=Digital\nOutputCue4111=press96\nOutputSigType4111=Digital\nOutputCue4112=press97\nOutputSigType4112=Digital\nOutputCue4113=press98\nOutputSigType4113=Digital\nOutputCue4114=press99\nOutputSigType4114=Digital\nOutputCue4115=press100\nOutputSigType4115=Digital\nOutputCue4116=press101\nOutputSigType4116=Digital\nOutputCue4117=press102\nOutputSigType4117=Digital\nOutputCue4118=press103\nOutputSigType4118=Digital\nOutputCue4119=press104\nOutputSigType4119=Digital\nOutputCue4120=press105\nOutputSigType4120=Digital\nOutputCue4121=press106\nOutputSigType4121=Digital\nOutputCue4122=press107\nOutputSigType4122=Digital\nOutputCue4123=press108\nOutputSigType4123=Digital\nOutputCue4124=press109\nOutputSigType4124=Digital\nOutputCue4125=press110\nOutputSigType4125=Digital\nOutputCue4126=press111\nOutputSigType4126=Digital\nOutputCue4127=press112\nOutputSigType4127=Digital\nOutputCue4128=press113\nOutputSigType4128=Digital\nOutputCue4129=press114\nOutputSigType4129=Digital\nOutputCue4130=press115\nOutputSigType4130=Digital\nOutputCue4131=press116\nOutputSigType4131=Digital\nOutputCue4132=press117\nOutputSigType4132=Digital\nOutputCue4133=press118\nOutputSigType4133=Digital\nOutputCue4134=press119\nOutputSigType4134=Digital\nOutputCue4135=press120\nOutputSigType4135=Digital\nOutputCue4136=press121\nOutputSigType4136=Digital\nOutputCue4137=press122\nOutputSigType4137=Digital\nOutputCue4138=press123\nOutputSigType4138=Digital\nOutputCue4139=press124\nOutputSigType4139=Digital\nOutputCue4140=press125\nOutputSigType4140=Digital\nOutputCue4141=press126\nOutputSigType4141=Digital\nOutputCue4142=press127\nOutputSigType4142=Digital\nOutputCue4143=press128\nOutputSigType4143=Digital\nOutputCue4144=press129\nOutputSigType4144=Digital\nOutputCue4145=press130\nOutputSigType4145=Digital\nOutputCue4146=press131\nOutputSigType4146=Digital\nOutputCue4147=press132\nOutputSigType4147=Digital\nOutputCue4148=press133\nOutputSigType4148=Digital\nOutputCue4149=press134\nOutputSigType4149=Digital\nOutputCue4150=press135\nOutputSigType4150=Digital\nOutputCue4151=press136\nOutputSigType4151=Digital\nOutputCue4152=press137\nOutputSigType4152=Digital\nOutputCue4153=press138\nOutputSigType4153=Digital\nOutputCue4154=press139\nOutputSigType4154=Digital\nOutputCue4155=press140\nOutputSigType4155=Digital\nOutputCue4156=press141\nOutputSigType4156=Digital\nOutputCue4157=press142\nOutputSigType4157=Digital\nOutputCue4158=press143\nOutputSigType4158=Digital\nOutputCue4159=press144\nOutputSigType4159=Digital\nOutputCue4160=press145\nOutputSigType4160=Digital\nOutputCue4161=press146\nOutputSigType4161=Digital\nOutputCue4162=press147\nOutputSigType4162=Digital\nOutputCue4163=press148\nOutputSigType4163=Digital\nOutputCue4164=press149\nOutputSigType4164=Digital\nOutputCue4165=press150\nOutputSigType4165=Digital\nOutputCue4166=press151\nOutputSigType4166=Digital\nOutputCue4167=press152\nOutputSigType4167=Digital\nOutputCue4168=press153\nOutputSigType4168=Digital\nOutputCue4169=press154\nOutputSigType4169=Digital\nOutputCue4170=press155\nOutputSigType4170=Digital\nOutputCue4171=press156\nOutputSigType4171=Digital\nOutputCue4172=press157\nOutputSigType4172=Digital\nOutputCue4173=press158\nOutputSigType4173=Digital\nOutputCue4174=press159\nOutputSigType4174=Digital\nOutputCue4175=press160\nOutputSigType4175=Digital\nOutputCue4176=press161\nOutputSigType4176=Digital\nOutputCue4177=press162\nOutputSigType4177=Digital\nOutputCue4178=press163\nOutputSigType4178=Digital\nOutputCue4179=press164\nOutputSigType4179=Digital\nOutputCue4180=press165\nOutputSigType4180=Digital\nOutputCue4181=press166\nOutputSigType4181=Digital\nOutputCue4182=press167\nOutputSigType4182=Digital\nOutputCue4183=press168\nOutputSigType4183=Digital\nOutputCue4184=press169\nOutputSigType4184=Digital\nOutputCue4185=press170\nOutputSigType4185=Digital\nOutputCue4186=press171\nOutputSigType4186=Digital\nOutputCue4187=press172\nOutputSigType4187=Digital\nOutputCue4188=press173\nOutputSigType4188=Digital\nOutputCue4189=press174\nOutputSigType4189=Digital\nOutputCue4190=press175\nOutputSigType4190=Digital\nOutputCue4191=press176\nOutputSigType4191=Digital\nOutputCue4192=press177\nOutputSigType4192=Digital\nOutputCue4193=press178\nOutputSigType4193=Digital\nOutputCue4194=press179\nOutputSigType4194=Digital\nOutputCue4195=press180\nOutputSigType4195=Digital\nOutputCue4196=press181\nOutputSigType4196=Digital\nOutputCue4197=press182\nOutputSigType4197=Digital\nOutputCue4198=press183\nOutputSigType4198=Digital\nOutputCue4199=press184\nOutputSigType4199=Digital\nOutputCue4200=press185\nOutputSigType4200=Digital\nOutputCue4201=press186\nOutputSigType4201=Digital\nOutputCue4202=press187\nOutputSigType4202=Digital\nOutputCue4203=press188\nOutputSigType4203=Digital\nOutputCue4204=press189\nOutputSigType4204=Digital\nOutputCue4205=press190\nOutputSigType4205=Digital\nOutputCue4206=press191\nOutputSigType4206=Digital\nOutputCue4207=press192\nOutputSigType4207=Digital\nOutputCue4208=press193\nOutputSigType4208=Digital\nOutputCue4209=press194\nOutputSigType4209=Digital\nOutputCue4210=press195\nOutputSigType4210=Digital\nOutputCue4211=press196\nOutputSigType4211=Digital\nOutputCue4212=press197\nOutputSigType4212=Digital\nOutputCue4213=press198\nOutputSigType4213=Digital\nOutputCue4214=press199\nOutputSigType4214=Digital\nOutputCue4215=press200\nOutputSigType4215=Digital\nOutputCue4216=press201\nOutputSigType4216=Digital\nOutputCue4217=press202\nOutputSigType4217=Digital\nOutputCue4218=press203\nOutputSigType4218=Digital\nOutputCue4219=press204\nOutputSigType4219=Digital\nOutputCue4220=press205\nOutputSigType4220=Digital\nOutputCue4221=press206\nOutputSigType4221=Digital\nOutputCue4222=press207\nOutputSigType4222=Digital\nOutputCue4223=press208\nOutputSigType4223=Digital\nOutputCue4224=press209\nOutputSigType4224=Digital\nOutputCue4225=press210\nOutputSigType4225=Digital\nOutputCue4226=press211\nOutputSigType4226=Digital\nOutputCue4227=press212\nOutputSigType4227=Digital\nOutputCue4228=press213\nOutputSigType4228=Digital\nOutputCue4229=press214\nOutputSigType4229=Digital\nOutputCue4230=press215\nOutputSigType4230=Digital\nOutputCue4231=press216\nOutputSigType4231=Digital\nOutputCue4232=press217\nOutputSigType4232=Digital\nOutputCue4233=press218\nOutputSigType4233=Digital\nOutputCue4234=press219\nOutputSigType4234=Digital\nOutputCue4235=press220\nOutputSigType4235=Digital\nOutputCue4236=press221\nOutputSigType4236=Digital\nOutputCue4237=press222\nOutputSigType4237=Digital\nOutputCue4238=press223\nOutputSigType4238=Digital\nOutputCue4239=press224\nOutputSigType4239=Digital\nOutputCue4240=press225\nOutputSigType4240=Digital\nOutputCue4241=press226\nOutputSigType4241=Digital\nOutputCue4242=press227\nOutputSigType4242=Digital\nOutputCue4243=press228\nOutputSigType4243=Digital\nOutputCue4244=press229\nOutputSigType4244=Digital\nOutputCue4245=press230\nOutputSigType4245=Digital\nOutputCue4246=press231\nOutputSigType4246=Digital\nOutputCue4247=press232\nOutputSigType4247=Digital\nOutputCue4248=press233\nOutputSigType4248=Digital\nOutputCue4249=press234\nOutputSigType4249=Digital\nOutputCue4250=press235\nOutputSigType4250=Digital\nOutputCue4251=press236\nOutputSigType4251=Digital\nOutputCue4252=press237\nOutputSigType4252=Digital\nOutputCue4253=press238\nOutputSigType4253=Digital\nOutputCue4254=press239\nOutputSigType4254=Digital\nOutputCue4255=press240\nOutputSigType4255=Digital\nOutputCue4256=press241\nOutputSigType4256=Digital\nOutputCue4257=press242\nOutputSigType4257=Digital\nOutputCue4258=press243\nOutputSigType4258=Digital\nOutputCue4259=press244\nOutputSigType4259=Digital\nOutputCue4260=press245\nOutputSigType4260=Digital\nOutputCue4261=press246\nOutputSigType4261=Digital\nOutputCue4262=press247\nOutputSigType4262=Digital\nOutputCue4263=press248\nOutputSigType4263=Digital\nOutputCue4264=press249\nOutputSigType4264=Digital\nOutputCue4265=press250\nOutputSigType4265=Digital\nOutputCue4266=press251\nOutputSigType4266=Digital\nOutputCue4267=press252\nOutputSigType4267=Digital\nOutputCue4268=press253\nOutputSigType4268=Digital\nOutputCue4269=press254\nOutputSigType4269=Digital\nOutputCue4270=press255\nOutputSigType4270=Digital\nOutputCue4271=press256\nOutputSigType4271=Digital\nOutputCue4272=press257\nOutputSigType4272=Digital\nOutputCue4273=press258\nOutputSigType4273=Digital\nOutputCue4274=press259\nOutputSigType4274=Digital\nOutputCue4275=press260\nOutputSigType4275=Digital\nOutputCue4276=press261\nOutputSigType4276=Digital\nOutputCue4277=press262\nOutputSigType4277=Digital\nOutputCue4278=press263\nOutputSigType4278=Digital\nOutputCue4279=press264\nOutputSigType4279=Digital\nOutputCue4280=press265\nOutputSigType4280=Digital\nOutputCue4281=press266\nOutputSigType4281=Digital\nOutputCue4282=press267\nOutputSigType4282=Digital\nOutputCue4283=press268\nOutputSigType4283=Digital\nOutputCue4284=press269\nOutputSigType4284=Digital\nOutputCue4285=press270\nOutputSigType4285=Digital\nOutputCue4286=press271\nOutputSigType4286=Digital\nOutputCue4287=press272\nOutputSigType4287=Digital\nOutputCue4288=press273\nOutputSigType4288=Digital\nOutputCue4289=press274\nOutputSigType4289=Digital\nOutputCue4290=press275\nOutputSigType4290=Digital\nOutputCue4291=press276\nOutputSigType4291=Digital\nOutputCue4292=press277\nOutputSigType4292=Digital\nOutputCue4293=press278\nOutputSigType4293=Digital\nOutputCue4294=press279\nOutputSigType4294=Digital\nOutputCue4295=press280\nOutputSigType4295=Digital\nOutputCue4296=press281\nOutputSigType4296=Digital\nOutputCue4297=press282\nOutputSigType4297=Digital\nOutputCue4298=press283\nOutputSigType4298=Digital\nOutputCue4299=press284\nOutputSigType4299=Digital\nOutputCue4300=press285\nOutputSigType4300=Digital\nOutputCue4301=press286\nOutputSigType4301=Digital\nOutputCue4302=press287\nOutputSigType4302=Digital\nOutputCue4303=press288\nOutputSigType4303=Digital\nOutputCue4304=press289\nOutputSigType4304=Digital\nOutputCue4305=press290\nOutputSigType4305=Digital\nOutputCue4306=press291\nOutputSigType4306=Digital\nOutputCue4307=press292\nOutputSigType4307=Digital\nOutputCue4308=press293\nOutputSigType4308=Digital\nOutputCue4309=press294\nOutputSigType4309=Digital\nOutputCue4310=press295\nOutputSigType4310=Digital\nOutputCue4311=press296\nOutputSigType4311=Digital\nOutputCue4312=press297\nOutputSigType4312=Digital\nOutputCue4313=press298\nOutputSigType4313=Digital\nOutputCue4314=press299\nOutputSigType4314=Digital\nOutputCue4315=press300\nOutputSigType4315=Digital\nOutputCue4316=[~EndGroup~]Press\nOutputSigType4316=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=text-o121\nInputList3SigType132=Serial\nInputList3Cue133=text-o122\nInputList3SigType133=Serial\nInputList3Cue134=text-o123\nInputList3SigType134=Serial\nInputList3Cue135=text-o124\nInputList3SigType135=Serial\nInputList3Cue136=text-o125\nInputList3SigType136=Serial\nInputList3Cue137=text-o126\nInputList3SigType137=Serial\nInputList3Cue138=text-o127\nInputList3SigType138=Serial\nInputList3Cue139=text-o128\nInputList3SigType139=Serial\nInputList3Cue140=text-o129\nInputList3SigType140=Serial\nInputList3Cue141=text-o130\nInputList3SigType141=Serial\nInputList3Cue142=text-o131\nInputList3SigType142=Serial\nInputList3Cue143=text-o132\nInputList3SigType143=Serial\nInputList3Cue144=text-o133\nInputList3SigType144=Serial\nInputList3Cue145=text-o134\nInputList3SigType145=Serial\nInputList3Cue146=text-o135\nInputList3SigType146=Serial\nInputList3Cue147=text-o136\nInputList3SigType147=Serial\nInputList3Cue148=text-o137\nInputList3SigType148=Serial\nInputList3Cue149=text-o138\nInputList3SigType149=Serial\nInputList3Cue150=text-o139\nInputList3SigType150=Serial\nInputList3Cue151=text-o140\nInputList3SigType151=Serial\nInputList3Cue152=text-o141\nInputList3SigType152=Serial\nInputList3Cue153=text-o142\nInputList3SigType153=Serial\nInputList3Cue154=text-o143\nInputList3SigType154=Serial\nInputList3Cue155=text-o144\nInputList3SigType155=Serial\nInputList3Cue156=text-o145\nInputList3SigType156=Serial\nInputList3Cue157=text-o146\nInputList3SigType157=Serial\nInputList3Cue158=text-o147\nInputList3SigType158=Serial\nInputList3Cue159=text-o148\nInputList3SigType159=Serial\nInputList3Cue160=text-o149\nInputList3SigType160=Serial\nInputList3Cue161=text-o150\nInputList3SigType161=Serial\nInputList3Cue162=text-o151\nInputList3SigType162=Serial\nInputList3Cue163=text-o152\nInputList3SigType163=Serial\nInputList3Cue164=text-o153\nInputList3SigType164=Serial\nInputList3Cue165=text-o154\nInputList3SigType165=Serial\nInputList3Cue166=text-o155\nInputList3SigType166=Serial\nInputList3Cue167=text-o156\nInputList3SigType167=Serial\nInputList3Cue168=text-o157\nInputList3SigType168=Serial\nInputList3Cue169=text-o158\nInputList3SigType169=Serial\nInputList3Cue170=text-o159\nInputList3SigType170=Serial\nInputList3Cue171=text-o160\nInputList3SigType171=Serial\nInputList3Cue172=text-o161\nInputList3SigType172=Serial\nInputList3Cue173=text-o162\nInputList3SigType173=Serial\nInputList3Cue174=text-o163\nInputList3SigType174=Serial\nInputList3Cue175=text-o164\nInputList3SigType175=Serial\nInputList3Cue176=text-o165\nInputList3SigType176=Serial\nInputList3Cue177=text-o166\nInputList3SigType177=Serial\nInputList3Cue178=text-o167\nInputList3SigType178=Serial\nInputList3Cue179=text-o168\nInputList3SigType179=Serial\nInputList3Cue180=text-o169\nInputList3SigType180=Serial\nInputList3Cue181=text-o170\nInputList3SigType181=Serial\nInputList3Cue182=text-o171\nInputList3SigType182=Serial\nInputList3Cue183=text-o172\nInputList3SigType183=Serial\nInputList3Cue184=text-o173\nInputList3SigType184=Serial\nInputList3Cue185=text-o174\nInputList3SigType185=Serial\nInputList3Cue186=text-o175\nInputList3SigType186=Serial\nInputList3Cue187=text-o176\nInputList3SigType187=Serial\nInputList3Cue188=text-o177\nInputList3SigType188=Serial\nInputList3Cue189=text-o178\nInputList3SigType189=Serial\nInputList3Cue190=text-o179\nInputList3SigType190=Serial\nInputList3Cue191=text-o180\nInputList3SigType191=Serial\nInputList3Cue192=text-o181\nInputList3SigType192=Serial\nInputList3Cue193=text-o182\nInputList3SigType193=Serial\nInputList3Cue194=text-o183\nInputList3SigType194=Serial\nInputList3Cue195=text-o184\nInputList3SigType195=Serial\nInputList3Cue196=text-o185\nInputList3SigType196=Serial\nInputList3Cue197=text-o186\nInputList3SigType197=Serial\nInputList3Cue198=text-o187\nInputList3SigType198=Serial\nInputList3Cue199=text-o188\nInputList3SigType199=Serial\nInputList3Cue200=text-o189\nInputList3SigType200=Serial\nInputList3Cue201=text-o190\nInputList3SigType201=Serial\nInputList3Cue202=text-o191\nInputList3SigType202=Serial\nInputList3Cue203=text-o192\nInputList3SigType203=Serial\nInputList3Cue204=text-o193\nInputList3SigType204=Serial\nInputList3Cue205=text-o194\nInputList3SigType205=Serial\nInputList3Cue206=text-o195\nInputList3SigType206=Serial\nInputList3Cue207=text-o196\nInputList3SigType207=Serial\nInputList3Cue208=text-o197\nInputList3SigType208=Serial\nInputList3Cue209=text-o198\nInputList3SigType209=Serial\nInputList3Cue210=text-o199\nInputList3SigType210=Serial\nInputList3Cue211=text-o200\nInputList3SigType211=Serial\nInputList3Cue212=text-o201\nInputList3SigType212=Serial\nInputList3Cue213=text-o202\nInputList3SigType213=Serial\nInputList3Cue214=text-o203\nInputList3SigType214=Serial\nInputList3Cue215=text-o204\nInputList3SigType215=Serial\nInputList3Cue216=text-o205\nInputList3SigType216=Serial\nInputList3Cue217=text-o206\nInputList3SigType217=Serial\nInputList3Cue218=text-o207\nInputList3SigType218=Serial\nInputList3Cue219=text-o208\nInputList3SigType219=Serial\nInputList3Cue220=text-o209\nInputList3SigType220=Serial\nInputList3Cue221=text-o210\nInputList3SigType221=Serial\nInputList3Cue222=text-o211\nInputList3SigType222=Serial\nInputList3Cue223=text-o212\nInputList3SigType223=Serial\nInputList3Cue224=text-o213\nInputList3SigType224=Serial\nInputList3Cue225=text-o214\nInputList3SigType225=Serial\nInputList3Cue226=text-o215\nInputList3SigType226=Serial\nInputList3Cue227=text-o216\nInputList3SigType227=Serial\nInputList3Cue228=text-o217\nInputList3SigType228=Serial\nInputList3Cue229=text-o218\nInputList3SigType229=Serial\nInputList3Cue230=text-o219\nInputList3SigType230=Serial\nInputList3Cue231=text-o220\nInputList3SigType231=Serial\nInputList3Cue232=text-o221\nInputList3SigType232=Serial\nInputList3Cue233=text-o222\nInputList3SigType233=Serial\nInputList3Cue234=text-o223\nInputList3SigType234=Serial\nInputList3Cue235=text-o224\nInputList3SigType235=Serial\nInputList3Cue236=text-o225\nInputList3SigType236=Serial\nInputList3Cue237=text-o226\nInputList3SigType237=Serial\nInputList3Cue238=text-o227\nInputList3SigType238=Serial\nInputList3Cue239=text-o228\nInputList3SigType239=Serial\nInputList3Cue240=text-o229\nInputList3SigType240=Serial\nInputList3Cue241=text-o230\nInputList3SigType241=Serial\nInputList3Cue242=text-o231\nInputList3SigType242=Serial\nInputList3Cue243=text-o232\nInputList3SigType243=Serial\nInputList3Cue244=text-o233\nInputList3SigType244=Serial\nInputList3Cue245=text-o234\nInputList3SigType245=Serial\nInputList3Cue246=text-o235\nInputList3SigType246=Serial\nInputList3Cue247=text-o236\nInputList3SigType247=Serial\nInputList3Cue248=text-o237\nInputList3SigType248=Serial\nInputList3Cue249=text-o238\nInputList3SigType249=Serial\nInputList3Cue250=text-o239\nInputList3SigType250=Serial\nInputList3Cue251=text-o240\nInputList3SigType251=Serial\nInputList3Cue252=text-o241\nInputList3SigType252=Serial\nInputList3Cue253=text-o242\nInputList3SigType253=Serial\nInputList3Cue254=text-o243\nInputList3SigType254=Serial\nInputList3Cue255=text-o244\nInputList3SigType255=Serial\nInputList3Cue256=text-o245\nInputList3SigType256=Serial\nInputList3Cue257=text-o246\nInputList3SigType257=Serial\nInputList3Cue258=text-o247\nInputList3SigType258=Serial\nInputList3Cue259=text-o248\nInputList3SigType259=Serial\nInputList3Cue260=text-o249\nInputList3SigType260=Serial\nInputList3Cue261=text-o250\nInputList3SigType261=Serial\nInputList3Cue262=text-o251\nInputList3SigType262=Serial\nInputList3Cue263=text-o252\nInputList3SigType263=Serial\nInputList3Cue264=text-o253\nInputList3SigType264=Serial\nInputList3Cue265=text-o254\nInputList3SigType265=Serial\nInputList3Cue266=text-o255\nInputList3SigType266=Serial\nInputList3Cue267=text-o256\nInputList3SigType267=Serial\nInputList3Cue268=text-o257\nInputList3SigType268=Serial\nInputList3Cue269=text-o258\nInputList3SigType269=Serial\nInputList3Cue270=text-o259\nInputList3SigType270=Serial\nInputList3Cue271=text-o260\nInputList3SigType271=Serial\nInputList3Cue272=text-o261\nInputList3SigType272=Serial\nInputList3Cue273=text-o262\nInputList3SigType273=Serial\nInputList3Cue274=text-o263\nInputList3SigType274=Serial\nInputList3Cue275=text-o264\nInputList3SigType275=Serial\nInputList3Cue276=text-o265\nInputList3SigType276=Serial\nInputList3Cue277=text-o266\nInputList3SigType277=Serial\nInputList3Cue278=text-o267\nInputList3SigType278=Serial\nInputList3Cue279=text-o268\nInputList3SigType279=Serial\nInputList3Cue280=text-o269\nInputList3SigType280=Serial\nInputList3Cue281=text-o270\nInputList3SigType281=Serial\nInputList3Cue282=text-o271\nInputList3SigType282=Serial\nInputList3Cue283=text-o272\nInputList3SigType283=Serial\nInputList3Cue284=text-o273\nInputList3SigType284=Serial\nInputList3Cue285=text-o274\nInputList3SigType285=Serial\nInputList3Cue286=text-o275\nInputList3SigType286=Serial\nInputList3Cue287=text-o276\nInputList3SigType287=Serial\nInputList3Cue288=text-o277\nInputList3SigType288=Serial\nInputList3Cue289=text-o278\nInputList3SigType289=Serial\nInputList3Cue290=text-o279\nInputList3SigType290=Serial\nInputList3Cue291=text-o280\nInputList3SigType291=Serial\nInputList3Cue292=text-o281\nInputList3SigType292=Serial\nInputList3Cue293=text-o282\nInputList3SigType293=Serial\nInputList3Cue294=text-o283\nInputList3SigType294=Serial\nInputList3Cue295=text-o284\nInputList3SigType295=Serial\nInputList3Cue296=text-o285\nInputList3SigType296=Serial\nInputList3Cue297=text-o286\nInputList3SigType297=Serial\nInputList3Cue298=text-o287\nInputList3SigType298=Serial\nInputList3Cue299=text-o288\nInputList3SigType299=Serial\nInputList3Cue300=text-o289\nInputList3SigType300=Serial\nInputList3Cue301=text-o290\nInputList3SigType301=Serial\nInputList3Cue302=text-o291\nInputList3SigType302=Serial\nInputList3Cue303=text-o292\nInputList3SigType303=Serial\nInputList3Cue304=text-o293\nInputList3SigType304=Serial\nInputList3Cue305=text-o294\nInputList3SigType305=Serial\nInputList3Cue306=text-o295\nInputList3SigType306=Serial\nInputList3Cue307=text-o296\nInputList3SigType307=Serial\nInputList3Cue308=text-o297\nInputList3SigType308=Serial\nInputList3Cue309=text-o298\nInputList3SigType309=Serial\nInputList3Cue310=text-o299\nInputList3SigType310=Serial\nInputList3Cue311=text-o300\nInputList3SigType311=Serial\nInputList3Cue312=text-o301\nInputList3SigType312=Serial\nInputList3Cue313=text-o302\nInputList3SigType313=Serial\nInputList3Cue314=text-o303\nInputList3SigType314=Serial\nInputList3Cue315=text-o304\nInputList3SigType315=Serial\nInputList3Cue316=text-o305\nInputList3SigType316=Serial\nInputList3Cue317=text-o306\nInputList3SigType317=Serial\nInputList3Cue318=text-o307\nInputList3SigType318=Serial\nInputList3Cue319=text-o308\nInputList3SigType319=Serial\nInputList3Cue320=text-o309\nInputList3SigType320=Serial\nInputList3Cue321=text-o310\nInputList3SigType321=Serial\nInputList3Cue322=text-o311\nInputList3SigType322=Serial\nInputList3Cue323=text-o312\nInputList3SigType323=Serial\nInputList3Cue324=text-o313\nInputList3SigType324=Serial\nInputList3Cue325=text-o314\nInputList3SigType325=Serial\nInputList3Cue326=text-o315\nInputList3SigType326=Serial\nInputList3Cue327=text-o316\nInputList3SigType327=Serial\nInputList3Cue328=text-o317\nInputList3SigType328=Serial\nInputList3Cue329=text-o318\nInputList3SigType329=Serial\nInputList3Cue330=text-o319\nInputList3SigType330=Serial\nInputList3Cue331=text-o320\nInputList3SigType331=Serial\nInputList3Cue332=text-o321\nInputList3SigType332=Serial\nInputList3Cue333=text-o322\nInputList3SigType333=Serial\nInputList3Cue334=text-o323\nInputList3SigType334=Serial\nInputList3Cue335=text-o324\nInputList3SigType335=Serial\nInputList3Cue336=text-o325\nInputList3SigType336=Serial\nInputList3Cue337=text-o326\nInputList3SigType337=Serial\nInputList3Cue338=text-o327\nInputList3SigType338=Serial\nInputList3Cue339=text-o328\nInputList3SigType339=Serial\nInputList3Cue340=text-o329\nInputList3SigType340=Serial\nInputList3Cue341=text-o330\nInputList3SigType341=Serial\nInputList3Cue342=text-o331\nInputList3SigType342=Serial\nInputList3Cue343=text-o332\nInputList3SigType343=Serial\nInputList3Cue344=text-o333\nInputList3SigType344=Serial\nInputList3Cue345=text-o334\nInputList3SigType345=Serial\nInputList3Cue346=text-o335\nInputList3SigType346=Serial\nInputList3Cue347=text-o336\nInputList3SigType347=Serial\nInputList3Cue348=text-o337\nInputList3SigType348=Serial\nInputList3Cue349=text-o338\nInputList3SigType349=Serial\nInputList3Cue350=text-o339\nInputList3SigType350=Serial\nInputList3Cue351=text-o340\nInputList3SigType351=Serial\nInputList3Cue352=text-o341\nInputList3SigType352=Serial\nInputList3Cue353=text-o342\nInputList3SigType353=Serial\nInputList3Cue354=text-o343\nInputList3SigType354=Serial\nInputList3Cue355=text-o344\nInputList3SigType355=Serial\nInputList3Cue356=text-o345\nInputList3SigType356=Serial\nInputList3Cue357=text-o346\nInputList3SigType357=Serial\nInputList3Cue358=text-o347\nInputList3SigType358=Serial\nInputList3Cue359=text-o348\nInputList3SigType359=Serial\nInputList3Cue360=text-o349\nInputList3SigType360=Serial\nInputList3Cue361=text-o350\nInputList3SigType361=Serial\nInputList3Cue362=text-o351\nInputList3SigType362=Serial\nInputList3Cue363=text-o352\nInputList3SigType363=Serial\nInputList3Cue364=text-o353\nInputList3SigType364=Serial\nInputList3Cue365=text-o354\nInputList3SigType365=Serial\nInputList3Cue366=text-o355\nInputList3SigType366=Serial\nInputList3Cue367=text-o356\nInputList3SigType367=Serial\nInputList3Cue368=text-o357\nInputList3SigType368=Serial\nInputList3Cue369=text-o358\nInputList3SigType369=Serial\nInputList3Cue370=text-o359\nInputList3SigType370=Serial\nInputList3Cue371=text-o360\nInputList3SigType371=Serial\nInputList3Cue372=text-o361\nInputList3SigType372=Serial\nInputList3Cue373=text-o362\nInputList3SigType373=Serial\nInputList3Cue374=text-o363\nInputList3SigType374=Serial\nInputList3Cue375=text-o364\nInputList3SigType375=Serial\nInputList3Cue376=text-o365\nInputList3SigType376=Serial\nInputList3Cue377=text-o366\nInputList3SigType377=Serial\nInputList3Cue378=text-o367\nInputList3SigType378=Serial\nInputList3Cue379=text-o368\nInputList3SigType379=Serial\nInputList3Cue380=text-o369\nInputList3SigType380=Serial\nInputList3Cue381=text-o370\nInputList3SigType381=Serial\nInputList3Cue382=text-o371\nInputList3SigType382=Serial\nInputList3Cue383=text-o372\nInputList3SigType383=Serial\nInputList3Cue384=text-o373\nInputList3SigType384=Serial\nInputList3Cue385=text-o374\nInputList3SigType385=Serial\nInputList3Cue386=text-o375\nInputList3SigType386=Serial\nInputList3Cue387=text-o376\nInputList3SigType387=Serial\nInputList3Cue388=text-o377\nInputList3SigType388=Serial\nInputList3Cue389=text-o378\nInputList3SigType389=Serial\nInputList3Cue390=text-o379\nInputList3SigType390=Serial\nInputList3Cue391=text-o380\nInputList3SigType391=Serial\nInputList3Cue392=text-o381\nInputList3SigType392=Serial\nInputList3Cue393=text-o382\nInputList3SigType393=Serial\nInputList3Cue394=text-o383\nInputList3SigType394=Serial\nInputList3Cue395=text-o384\nInputList3SigType395=Serial\nInputList3Cue396=text-o385\nInputList3SigType396=Serial\nInputList3Cue397=text-o386\nInputList3SigType397=Serial\nInputList3Cue398=text-o387\nInputList3SigType398=Serial\nInputList3Cue399=text-o388\nInputList3SigType399=Serial\nInputList3Cue400=text-o389\nInputList3SigType400=Serial\nInputList3Cue401=text-o390\nInputList3SigType401=Serial\nInputList3Cue402=text-o391\nInputList3SigType402=Serial\nInputList3Cue403=text-o392\nInputList3SigType403=Serial\nInputList3Cue404=text-o393\nInputList3SigType404=Serial\nInputList3Cue405=text-o394\nInputList3SigType405=Serial\nInputList3Cue406=text-o395\nInputList3SigType406=Serial\nInputList3Cue407=text-o396\nInputList3SigType407=Serial\nInputList3Cue408=text-o397\nInputList3SigType408=Serial\nInputList3Cue409=text-o398\nInputList3SigType409=Serial\nInputList3Cue410=text-o399\nInputList3SigType410=Serial\nInputList3Cue411=text-o400\nInputList3SigType411=Serial\nInputList3Cue412=[~EndGroup~]text-o\nInputList3SigType412=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=text-i121\nOutputList3SigType132=Serial\nOutputList3Cue133=text-i122\nOutputList3SigType133=Serial\nOutputList3Cue134=text-i123\nOutputList3SigType134=Serial\nOutputList3Cue135=text-i124\nOutputList3SigType135=Serial\nOutputList3Cue136=text-i125\nOutputList3SigType136=Serial\nOutputList3Cue137=text-i126\nOutputList3SigType137=Serial\nOutputList3Cue138=text-i127\nOutputList3SigType138=Serial\nOutputList3Cue139=text-i128\nOutputList3SigType139=Serial\nOutputList3Cue140=text-i129\nOutputList3SigType140=Serial\nOutputList3Cue141=text-i130\nOutputList3SigType141=Serial\nOutputList3Cue142=text-i131\nOutputList3SigType142=Serial\nOutputList3Cue143=text-i132\nOutputList3SigType143=Serial\nOutputList3Cue144=text-i133\nOutputList3SigType144=Serial\nOutputList3Cue145=text-i134\nOutputList3SigType145=Serial\nOutputList3Cue146=text-i135\nOutputList3SigType146=Serial\nOutputList3Cue147=text-i136\nOutputList3SigType147=Serial\nOutputList3Cue148=text-i137\nOutputList3SigType148=Serial\nOutputList3Cue149=text-i138\nOutputList3SigType149=Serial\nOutputList3Cue150=text-i139\nOutputList3SigType150=Serial\nOutputList3Cue151=text-i140\nOutputList3SigType151=Serial\nOutputList3Cue152=text-i141\nOutputList3SigType152=Serial\nOutputList3Cue153=text-i142\nOutputList3SigType153=Serial\nOutputList3Cue154=text-i143\nOutputList3SigType154=Serial\nOutputList3Cue155=text-i144\nOutputList3SigType155=Serial\nOutputList3Cue156=text-i145\nOutputList3SigType156=Serial\nOutputList3Cue157=text-i146\nOutputList3SigType157=Serial\nOutputList3Cue158=text-i147\nOutputList3SigType158=Serial\nOutputList3Cue159=text-i148\nOutputList3SigType159=Serial\nOutputList3Cue160=text-i149\nOutputList3SigType160=Serial\nOutputList3Cue161=text-i150\nOutputList3SigType161=Serial\nOutputList3Cue162=text-i151\nOutputList3SigType162=Serial\nOutputList3Cue163=text-i152\nOutputList3SigType163=Serial\nOutputList3Cue164=text-i153\nOutputList3SigType164=Serial\nOutputList3Cue165=text-i154\nOutputList3SigType165=Serial\nOutputList3Cue166=text-i155\nOutputList3SigType166=Serial\nOutputList3Cue167=text-i156\nOutputList3SigType167=Serial\nOutputList3Cue168=text-i157\nOutputList3SigType168=Serial\nOutputList3Cue169=text-i158\nOutputList3SigType169=Serial\nOutputList3Cue170=text-i159\nOutputList3SigType170=Serial\nOutputList3Cue171=text-i160\nOutputList3SigType171=Serial\nOutputList3Cue172=text-i161\nOutputList3SigType172=Serial\nOutputList3Cue173=text-i162\nOutputList3SigType173=Serial\nOutputList3Cue174=text-i163\nOutputList3SigType174=Serial\nOutputList3Cue175=text-i164\nOutputList3SigType175=Serial\nOutputList3Cue176=text-i165\nOutputList3SigType176=Serial\nOutputList3Cue177=text-i166\nOutputList3SigType177=Serial\nOutputList3Cue178=text-i167\nOutputList3SigType178=Serial\nOutputList3Cue179=text-i168\nOutputList3SigType179=Serial\nOutputList3Cue180=text-i169\nOutputList3SigType180=Serial\nOutputList3Cue181=text-i170\nOutputList3SigType181=Serial\nOutputList3Cue182=text-i171\nOutputList3SigType182=Serial\nOutputList3Cue183=text-i172\nOutputList3SigType183=Serial\nOutputList3Cue184=text-i173\nOutputList3SigType184=Serial\nOutputList3Cue185=text-i174\nOutputList3SigType185=Serial\nOutputList3Cue186=text-i175\nOutputList3SigType186=Serial\nOutputList3Cue187=text-i176\nOutputList3SigType187=Serial\nOutputList3Cue188=text-i177\nOutputList3SigType188=Serial\nOutputList3Cue189=text-i178\nOutputList3SigType189=Serial\nOutputList3Cue190=text-i179\nOutputList3SigType190=Serial\nOutputList3Cue191=text-i180\nOutputList3SigType191=Serial\nOutputList3Cue192=text-i181\nOutputList3SigType192=Serial\nOutputList3Cue193=text-i182\nOutputList3SigType193=Serial\nOutputList3Cue194=text-i183\nOutputList3SigType194=Serial\nOutputList3Cue195=text-i184\nOutputList3SigType195=Serial\nOutputList3Cue196=text-i185\nOutputList3SigType196=Serial\nOutputList3Cue197=text-i186\nOutputList3SigType197=Serial\nOutputList3Cue198=text-i187\nOutputList3SigType198=Serial\nOutputList3Cue199=text-i188\nOutputList3SigType199=Serial\nOutputList3Cue200=text-i189\nOutputList3SigType200=Serial\nOutputList3Cue201=text-i190\nOutputList3SigType201=Serial\nOutputList3Cue202=text-i191\nOutputList3SigType202=Serial\nOutputList3Cue203=text-i192\nOutputList3SigType203=Serial\nOutputList3Cue204=text-i193\nOutputList3SigType204=Serial\nOutputList3Cue205=text-i194\nOutputList3SigType205=Serial\nOutputList3Cue206=text-i195\nOutputList3SigType206=Serial\nOutputList3Cue207=text-i196\nOutputList3SigType207=Serial\nOutputList3Cue208=text-i197\nOutputList3SigType208=Serial\nOutputList3Cue209=text-i198\nOutputList3SigType209=Serial\nOutputList3Cue210=text-i199\nOutputList3SigType210=Serial\nOutputList3Cue211=text-i200\nOutputList3SigType211=Serial\nOutputList3Cue212=text-i201\nOutputList3SigType212=Serial\nOutputList3Cue213=text-i202\nOutputList3SigType213=Serial\nOutputList3Cue214=text-i203\nOutputList3SigType214=Serial\nOutputList3Cue215=text-i204\nOutputList3SigType215=Serial\nOutputList3Cue216=text-i205\nOutputList3SigType216=Serial\nOutputList3Cue217=text-i206\nOutputList3SigType217=Serial\nOutputList3Cue218=text-i207\nOutputList3SigType218=Serial\nOutputList3Cue219=text-i208\nOutputList3SigType219=Serial\nOutputList3Cue220=text-i209\nOutputList3SigType220=Serial\nOutputList3Cue221=text-i210\nOutputList3SigType221=Serial\nOutputList3Cue222=text-i211\nOutputList3SigType222=Serial\nOutputList3Cue223=text-i212\nOutputList3SigType223=Serial\nOutputList3Cue224=text-i213\nOutputList3SigType224=Serial\nOutputList3Cue225=text-i214\nOutputList3SigType225=Serial\nOutputList3Cue226=text-i215\nOutputList3SigType226=Serial\nOutputList3Cue227=text-i216\nOutputList3SigType227=Serial\nOutputList3Cue228=text-i217\nOutputList3SigType228=Serial\nOutputList3Cue229=text-i218\nOutputList3SigType229=Serial\nOutputList3Cue230=text-i219\nOutputList3SigType230=Serial\nOutputList3Cue231=text-i220\nOutputList3SigType231=Serial\nOutputList3Cue232=text-i221\nOutputList3SigType232=Serial\nOutputList3Cue233=text-i222\nOutputList3SigType233=Serial\nOutputList3Cue234=text-i223\nOutputList3SigType234=Serial\nOutputList3Cue235=text-i224\nOutputList3SigType235=Serial\nOutputList3Cue236=text-i225\nOutputList3SigType236=Serial\nOutputList3Cue237=text-i226\nOutputList3SigType237=Serial\nOutputList3Cue238=text-i227\nOutputList3SigType238=Serial\nOutputList3Cue239=text-i228\nOutputList3SigType239=Serial\nOutputList3Cue240=text-i229\nOutputList3SigType240=Serial\nOutputList3Cue241=text-i230\nOutputList3SigType241=Serial\nOutputList3Cue242=text-i231\nOutputList3SigType242=Serial\nOutputList3Cue243=text-i232\nOutputList3SigType243=Serial\nOutputList3Cue244=text-i233\nOutputList3SigType244=Serial\nOutputList3Cue245=text-i234\nOutputList3SigType245=Serial\nOutputList3Cue246=text-i235\nOutputList3SigType246=Serial\nOutputList3Cue247=text-i236\nOutputList3SigType247=Serial\nOutputList3Cue248=text-i237\nOutputList3SigType248=Serial\nOutputList3Cue249=text-i238\nOutputList3SigType249=Serial\nOutputList3Cue250=text-i239\nOutputList3SigType250=Serial\nOutputList3Cue251=text-i240\nOutputList3SigType251=Serial\nOutputList3Cue252=text-i241\nOutputList3SigType252=Serial\nOutputList3Cue253=text-i242\nOutputList3SigType253=Serial\nOutputList3Cue254=text-i243\nOutputList3SigType254=Serial\nOutputList3Cue255=text-i244\nOutputList3SigType255=Serial\nOutputList3Cue256=text-i245\nOutputList3SigType256=Serial\nOutputList3Cue257=text-i246\nOutputList3SigType257=Serial\nOutputList3Cue258=text-i247\nOutputList3SigType258=Serial\nOutputList3Cue259=text-i248\nOutputList3SigType259=Serial\nOutputList3Cue260=text-i249\nOutputList3SigType260=Serial\nOutputList3Cue261=text-i250\nOutputList3SigType261=Serial\nOutputList3Cue262=text-i251\nOutputList3SigType262=Serial\nOutputList3Cue263=text-i252\nOutputList3SigType263=Serial\nOutputList3Cue264=text-i253\nOutputList3SigType264=Serial\nOutputList3Cue265=text-i254\nOutputList3SigType265=Serial\nOutputList3Cue266=text-i255\nOutputList3SigType266=Serial\nOutputList3Cue267=text-i256\nOutputList3SigType267=Serial\nOutputList3Cue268=text-i257\nOutputList3SigType268=Serial\nOutputList3Cue269=text-i258\nOutputList3SigType269=Serial\nOutputList3Cue270=text-i259\nOutputList3SigType270=Serial\nOutputList3Cue271=text-i260\nOutputList3SigType271=Serial\nOutputList3Cue272=text-i261\nOutputList3SigType272=Serial\nOutputList3Cue273=text-i262\nOutputList3SigType273=Serial\nOutputList3Cue274=text-i263\nOutputList3SigType274=Serial\nOutputList3Cue275=text-i264\nOutputList3SigType275=Serial\nOutputList3Cue276=text-i265\nOutputList3SigType276=Serial\nOutputList3Cue277=text-i266\nOutputList3SigType277=Serial\nOutputList3Cue278=text-i267\nOutputList3SigType278=Serial\nOutputList3Cue279=text-i268\nOutputList3SigType279=Serial\nOutputList3Cue280=text-i269\nOutputList3SigType280=Serial\nOutputList3Cue281=text-i270\nOutputList3SigType281=Serial\nOutputList3Cue282=text-i271\nOutputList3SigType282=Serial\nOutputList3Cue283=text-i272\nOutputList3SigType283=Serial\nOutputList3Cue284=text-i273\nOutputList3SigType284=Serial\nOutputList3Cue285=text-i274\nOutputList3SigType285=Serial\nOutputList3Cue286=text-i275\nOutputList3SigType286=Serial\nOutputList3Cue287=text-i276\nOutputList3SigType287=Serial\nOutputList3Cue288=text-i277\nOutputList3SigType288=Serial\nOutputList3Cue289=text-i278\nOutputList3SigType289=Serial\nOutputList3Cue290=text-i279\nOutputList3SigType290=Serial\nOutputList3Cue291=text-i280\nOutputList3SigType291=Serial\nOutputList3Cue292=text-i281\nOutputList3SigType292=Serial\nOutputList3Cue293=text-i282\nOutputList3SigType293=Serial\nOutputList3Cue294=text-i283\nOutputList3SigType294=Serial\nOutputList3Cue295=text-i284\nOutputList3SigType295=Serial\nOutputList3Cue296=text-i285\nOutputList3SigType296=Serial\nOutputList3Cue297=text-i286\nOutputList3SigType297=Serial\nOutputList3Cue298=text-i287\nOutputList3SigType298=Serial\nOutputList3Cue299=text-i288\nOutputList3SigType299=Serial\nOutputList3Cue300=text-i289\nOutputList3SigType300=Serial\nOutputList3Cue301=text-i290\nOutputList3SigType301=Serial\nOutputList3Cue302=text-i291\nOutputList3SigType302=Serial\nOutputList3Cue303=text-i292\nOutputList3SigType303=Serial\nOutputList3Cue304=text-i293\nOutputList3SigType304=Serial\nOutputList3Cue305=text-i294\nOutputList3SigType305=Serial\nOutputList3Cue306=text-i295\nOutputList3SigType306=Serial\nOutputList3Cue307=text-i296\nOutputList3SigType307=Serial\nOutputList3Cue308=text-i297\nOutputList3SigType308=Serial\nOutputList3Cue309=text-i298\nOutputList3SigType309=Serial\nOutputList3Cue310=text-i299\nOutputList3SigType310=Serial\nOutputList3Cue311=text-i300\nOutputList3SigType311=Serial\nOutputList3Cue312=text-i301\nOutputList3SigType312=Serial\nOutputList3Cue313=text-i302\nOutputList3SigType313=Serial\nOutputList3Cue314=text-i303\nOutputList3SigType314=Serial\nOutputList3Cue315=text-i304\nOutputList3SigType315=Serial\nOutputList3Cue316=text-i305\nOutputList3SigType316=Serial\nOutputList3Cue317=text-i306\nOutputList3SigType317=Serial\nOutputList3Cue318=text-i307\nOutputList3SigType318=Serial\nOutputList3Cue319=text-i308\nOutputList3SigType319=Serial\nOutputList3Cue320=text-i309\nOutputList3SigType320=Serial\nOutputList3Cue321=text-i310\nOutputList3SigType321=Serial\nOutputList3Cue322=text-i311\nOutputList3SigType322=Serial\nOutputList3Cue323=text-i312\nOutputList3SigType323=Serial\nOutputList3Cue324=text-i313\nOutputList3SigType324=Serial\nOutputList3Cue325=text-i314\nOutputList3SigType325=Serial\nOutputList3Cue326=text-i315\nOutputList3SigType326=Serial\nOutputList3Cue327=text-i316\nOutputList3SigType327=Serial\nOutputList3Cue328=text-i317\nOutputList3SigType328=Serial\nOutputList3Cue329=text-i318\nOutputList3SigType329=Serial\nOutputList3Cue330=text-i319\nOutputList3SigType330=Serial\nOutputList3Cue331=text-i320\nOutputList3SigType331=Serial\nOutputList3Cue332=text-i321\nOutputList3SigType332=Serial\nOutputList3Cue333=text-i322\nOutputList3SigType333=Serial\nOutputList3Cue334=text-i323\nOutputList3SigType334=Serial\nOutputList3Cue335=text-i324\nOutputList3SigType335=Serial\nOutputList3Cue336=text-i325\nOutputList3SigType336=Serial\nOutputList3Cue337=text-i326\nOutputList3SigType337=Serial\nOutputList3Cue338=text-i327\nOutputList3SigType338=Serial\nOutputList3Cue339=text-i328\nOutputList3SigType339=Serial\nOutputList3Cue340=text-i329\nOutputList3SigType340=Serial\nOutputList3Cue341=text-i330\nOutputList3SigType341=Serial\nOutputList3Cue342=text-i331\nOutputList3SigType342=Serial\nOutputList3Cue343=text-i332\nOutputList3SigType343=Serial\nOutputList3Cue344=text-i333\nOutputList3SigType344=Serial\nOutputList3Cue345=text-i334\nOutputList3SigType345=Serial\nOutputList3Cue346=text-i335\nOutputList3SigType346=Serial\nOutputList3Cue347=text-i336\nOutputList3SigType347=Serial\nOutputList3Cue348=text-i337\nOutputList3SigType348=Serial\nOutputList3Cue349=text-i338\nOutputList3SigType349=Serial\nOutputList3Cue350=text-i339\nOutputList3SigType350=Serial\nOutputList3Cue351=text-i340\nOutputList3SigType351=Serial\nOutputList3Cue352=text-i341\nOutputList3SigType352=Serial\nOutputList3Cue353=text-i342\nOutputList3SigType353=Serial\nOutputList3Cue354=text-i343\nOutputList3SigType354=Serial\nOutputList3Cue355=text-i344\nOutputList3SigType355=Serial\nOutputList3Cue356=text-i345\nOutputList3SigType356=Serial\nOutputList3Cue357=text-i346\nOutputList3SigType357=Serial\nOutputList3Cue358=text-i347\nOutputList3SigType358=Serial\nOutputList3Cue359=text-i348\nOutputList3SigType359=Serial\nOutputList3Cue360=text-i349\nOutputList3SigType360=Serial\nOutputList3Cue361=text-i350\nOutputList3SigType361=Serial\nOutputList3Cue362=text-i351\nOutputList3SigType362=Serial\nOutputList3Cue363=text-i352\nOutputList3SigType363=Serial\nOutputList3Cue364=text-i353\nOutputList3SigType364=Serial\nOutputList3Cue365=text-i354\nOutputList3SigType365=Serial\nOutputList3Cue366=text-i355\nOutputList3SigType366=Serial\nOutputList3Cue367=text-i356\nOutputList3SigType367=Serial\nOutputList3Cue368=text-i357\nOutputList3SigType368=Serial\nOutputList3Cue369=text-i358\nOutputList3SigType369=Serial\nOutputList3Cue370=text-i359\nOutputList3SigType370=Serial\nOutputList3Cue371=text-i360\nOutputList3SigType371=Serial\nOutputList3Cue372=text-i361\nOutputList3SigType372=Serial\nOutputList3Cue373=text-i362\nOutputList3SigType373=Serial\nOutputList3Cue374=text-i363\nOutputList3SigType374=Serial\nOutputList3Cue375=text-i364\nOutputList3SigType375=Serial\nOutputList3Cue376=text-i365\nOutputList3SigType376=Serial\nOutputList3Cue377=text-i366\nOutputList3SigType377=Serial\nOutputList3Cue378=text-i367\nOutputList3SigType378=Serial\nOutputList3Cue379=text-i368\nOutputList3SigType379=Serial\nOutputList3Cue380=text-i369\nOutputList3SigType380=Serial\nOutputList3Cue381=text-i370\nOutputList3SigType381=Serial\nOutputList3Cue382=text-i371\nOutputList3SigType382=Serial\nOutputList3Cue383=text-i372\nOutputList3SigType383=Serial\nOutputList3Cue384=text-i373\nOutputList3SigType384=Serial\nOutputList3Cue385=text-i374\nOutputList3SigType385=Serial\nOutputList3Cue386=text-i375\nOutputList3SigType386=Serial\nOutputList3Cue387=text-i376\nOutputList3SigType387=Serial\nOutputList3Cue388=text-i377\nOutputList3SigType388=Serial\nOutputList3Cue389=text-i378\nOutputList3SigType389=Serial\nOutputList3Cue390=text-i379\nOutputList3SigType390=Serial\nOutputList3Cue391=text-i380\nOutputList3SigType391=Serial\nOutputList3Cue392=text-i381\nOutputList3SigType392=Serial\nOutputList3Cue393=text-i382\nOutputList3SigType393=Serial\nOutputList3Cue394=text-i383\nOutputList3SigType394=Serial\nOutputList3Cue395=text-i384\nOutputList3SigType395=Serial\nOutputList3Cue396=text-i385\nOutputList3SigType396=Serial\nOutputList3Cue397=text-i386\nOutputList3SigType397=Serial\nOutputList3Cue398=text-i387\nOutputList3SigType398=Serial\nOutputList3Cue399=text-i388\nOutputList3SigType399=Serial\nOutputList3Cue400=text-i389\nOutputList3SigType400=Serial\nOutputList3Cue401=text-i390\nOutputList3SigType401=Serial\nOutputList3Cue402=text-i391\nOutputList3SigType402=Serial\nOutputList3Cue403=text-i392\nOutputList3SigType403=Serial\nOutputList3Cue404=text-i393\nOutputList3SigType404=Serial\nOutputList3Cue405=text-i394\nOutputList3SigType405=Serial\nOutputList3Cue406=text-i395\nOutputList3SigType406=Serial\nOutputList3Cue407=text-i396\nOutputList3SigType407=Serial\nOutputList3Cue408=text-i397\nOutputList3SigType408=Serial\nOutputList3Cue409=text-i398\nOutputList3SigType409=Serial\nOutputList3Cue410=text-i399\nOutputList3SigType410=Serial\nOutputList3Cue411=text-i400\nOutputList3SigType411=Serial\nOutputList3Cue412=[~EndGroup~]text-i\nOutputList3SigType412=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=10\nCedH=10\nSmartObjId=10012d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=10\nTp=1\nHD=TRUE\nDV=10012d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=10\nName=PepperDash Essentials iPad_[B.AV] Object - Presets_CATV Icon List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[B.AV] Object - DPad STB_STB DPad.ced\nHint=STB DPad (Smart Object ID=10011)\nCode=11\nSGControlType=DPad\nSGControlName=STB DPad\nGUID=F2340FAD-85E9-4E49-B46E-15E9B096F58B\nSmplCName=PepperDash Essentials iPad_[B.AV] Object - DPad STB_STB DPad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10015\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=5\nMaxVariableInputs=5\nMinVariableOutputs=5\nMaxVariableOutputs=5\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Up\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=Down\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Left\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=Right\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=OK\nOutputSigType5=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=11\nCedH=11\nSmartObjId=10011d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=11\nTp=1\nHD=TRUE\nDV=10011d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=11\nName=PepperDash Essentials iPad_[B.AV] Object - DPad STB_STB DPad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\nHint=Tab Button Horizontal (Smart Object ID=10081)\nCode=12\nSGControlType=Horizontal Tab Button\nSGControlName=Tab Button Horizontal\nGUID=198C97B0-9D0C-46C2-9557-27EA33C0BE9F\nSmplCName=PepperDash Essentials iPad_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10031\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4\nMaxVariableInputs=4\nMinVariableOutputs=4\nMaxVariableOutputs=4\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Tab Button 1 Press\nOutputSigType1=Digital\nInputCue2=Tab Button 1 Select\nInputSigType2=Digital\nOutputCue2=[~UNUSED3~]\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Tab Button 2 Press\nOutputSigType3=Digital\nInputCue4=Tab Button 2 Select\nInputSigType4=Digital\nOutputCue4=[~UNUSED3~]\nOutputSigType4=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=12\nCedH=12\nSmartObjId=10081d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=12\nTp=1\nHD=TRUE\nDV=10081d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=12\nName=PepperDash Essentials iPad_[B.AV] Object - Tab SetTopBox_Tab Button Horizontal.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[B.AV] Object - Dpad DVD_DVD DPad.ced\nHint=DVD DPad (Smart Object ID=10411)\nCode=13\nSGControlType=DPad\nSGControlName=DVD DPad\nGUID=D49BCF22-C770-4146-93E6-00867D885E1E\nSmplCName=PepperDash Essentials iPad_[B.AV] Object - Dpad DVD_DVD DPad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10015\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=5\nMaxVariableInputs=5\nMinVariableOutputs=5\nMaxVariableOutputs=5\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=Up\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=Down\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=Left\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=Right\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=OK\nOutputSigType5=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=13\nCedH=13\nSmartObjId=10411d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=13\nTp=1\nHD=TRUE\nDV=10411d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=13\nName=PepperDash Essentials iPad_[B.AV] Object - Dpad DVD_DVD DPad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\nHint=CATV Keypad (Smart Object ID=10014)\nCode=14\nSGControlType=Simple Keypad\nSGControlName=CATV Keypad\nGUID=5E33D4F4-5C5F-430C-879A-AE82529B3943\nSmplCName=PepperDash Essentials iPad_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\nSMWRev=4.02.19\nExpand=expand_random\nHelpID=10061\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=12\nMaxVariableInputs=12\nMinVariableOutputs=12\nMaxVariableOutputs=12\nNumFixedParams=1\nMinVariableInputsList2=0\nMaxVariableInputsList2=0\nMinVariableOutputsList2=0\nMaxVariableOutputsList2=0\nMinVariableInputsList3=0\nMaxVariableInputsList3=0\nMinVariableOutputsList3=0\nMaxVariableOutputsList3=0\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nOutputCue1=1\nOutputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nOutputCue2=2\nOutputSigType2=Digital\nInputCue3=[~UNUSED3~]\nInputSigType3=Digital\nOutputCue3=3\nOutputSigType3=Digital\nInputCue4=[~UNUSED3~]\nInputSigType4=Digital\nOutputCue4=4\nOutputSigType4=Digital\nInputCue5=[~UNUSED3~]\nInputSigType5=Digital\nOutputCue5=5\nOutputSigType5=Digital\nInputCue6=[~UNUSED3~]\nInputSigType6=Digital\nOutputCue6=6\nOutputSigType6=Digital\nInputCue7=[~UNUSED3~]\nInputSigType7=Digital\nOutputCue7=7\nOutputSigType7=Digital\nInputCue8=[~UNUSED3~]\nInputSigType8=Digital\nOutputCue8=8\nOutputSigType8=Digital\nInputCue9=[~UNUSED3~]\nInputSigType9=Digital\nOutputCue9=9\nOutputSigType9=Digital\nInputCue10=[~UNUSED3~]\nInputSigType10=Digital\nOutputCue10=0\nOutputSigType10=Digital\nInputCue11=[~UNUSED3~]\nInputSigType11=Digital\nOutputCue11=Misc_1\nOutputSigType11=Digital\nInputCue12=[~UNUSED3~]\nInputSigType12=Digital\nOutputCue12=Misc_2\nOutputSigType12=Digital\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=14\nCedH=14\nSmartObjId=10014d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=14\nTp=1\nHD=TRUE\nDV=10014d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=14\nName=PepperDash Essentials iPad_[B.AV] Source-Set Top Box Backer_CATV Keypad.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\nHint=CATV Icon List Vertical (Smart Object ID=3333)\nCode=15\nSGControlType=Subpage Reference List Vertical\nSGControlName=CATV Icon List Vertical\nGUID=D6A79678-BAD2-4F6D-8104-A941FBA9432F\nSmplCName=PepperDash Essentials iPad_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4106\nMaxVariableInputs=4106\nMinVariableOutputs=4106\nMaxVariableOutputs=4106\nNumFixedParams=1\nMinVariableInputsList2=3\nMaxVariableInputsList2=3\nMinVariableOutputsList2=3\nMaxVariableOutputsList2=3\nMinVariableInputsList3=132\nMaxVariableInputsList3=132\nMinVariableOutputsList3=132\nMaxVariableOutputsList3=132\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=Item 21 Enable\nInputSigType32=Digital\nInputCue33=Item 22 Enable\nInputSigType33=Digital\nInputCue34=Item 23 Enable\nInputSigType34=Digital\nInputCue35=Item 24 Enable\nInputSigType35=Digital\nInputCue36=Item 25 Enable\nInputSigType36=Digital\nInputCue37=Item 26 Enable\nInputSigType37=Digital\nInputCue38=Item 27 Enable\nInputSigType38=Digital\nInputCue39=Item 28 Enable\nInputSigType39=Digital\nInputCue40=Item 29 Enable\nInputSigType40=Digital\nInputCue41=Item 30 Enable\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=Item 21 Visible\nInputSigType2034=Digital\nInputCue2035=Item 22 Visible\nInputSigType2035=Digital\nInputCue2036=Item 23 Visible\nInputSigType2036=Digital\nInputCue2037=Item 24 Visible\nInputSigType2037=Digital\nInputCue2038=Item 25 Visible\nInputSigType2038=Digital\nInputCue2039=Item 26 Visible\nInputSigType2039=Digital\nInputCue2040=Item 27 Visible\nInputSigType2040=Digital\nInputCue2041=Item 28 Visible\nInputSigType2041=Digital\nInputCue2042=Item 29 Visible\nInputSigType2042=Digital\nInputCue2043=Item 30 Visible\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=[~EndGroup~]fb\nInputSigType4106=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED3~]\nOutputSigType32=Digital\nOutputCue33=[~UNUSED3~]\nOutputSigType33=Digital\nOutputCue34=[~UNUSED3~]\nOutputSigType34=Digital\nOutputCue35=[~UNUSED3~]\nOutputSigType35=Digital\nOutputCue36=[~UNUSED3~]\nOutputSigType36=Digital\nOutputCue37=[~UNUSED3~]\nOutputSigType37=Digital\nOutputCue38=[~UNUSED3~]\nOutputSigType38=Digital\nOutputCue39=[~UNUSED3~]\nOutputSigType39=Digital\nOutputCue40=[~UNUSED3~]\nOutputSigType40=Digital\nOutputCue41=[~UNUSED3~]\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=[~EndGroup~]Press\nOutputSigType4106=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=text-o101\nInputList3SigType112=Serial\nInputList3Cue113=text-o102\nInputList3SigType113=Serial\nInputList3Cue114=text-o103\nInputList3SigType114=Serial\nInputList3Cue115=text-o104\nInputList3SigType115=Serial\nInputList3Cue116=text-o105\nInputList3SigType116=Serial\nInputList3Cue117=text-o106\nInputList3SigType117=Serial\nInputList3Cue118=text-o107\nInputList3SigType118=Serial\nInputList3Cue119=text-o108\nInputList3SigType119=Serial\nInputList3Cue120=text-o109\nInputList3SigType120=Serial\nInputList3Cue121=text-o110\nInputList3SigType121=Serial\nInputList3Cue122=text-o111\nInputList3SigType122=Serial\nInputList3Cue123=text-o112\nInputList3SigType123=Serial\nInputList3Cue124=text-o113\nInputList3SigType124=Serial\nInputList3Cue125=text-o114\nInputList3SigType125=Serial\nInputList3Cue126=text-o115\nInputList3SigType126=Serial\nInputList3Cue127=text-o116\nInputList3SigType127=Serial\nInputList3Cue128=text-o117\nInputList3SigType128=Serial\nInputList3Cue129=text-o118\nInputList3SigType129=Serial\nInputList3Cue130=text-o119\nInputList3SigType130=Serial\nInputList3Cue131=text-o120\nInputList3SigType131=Serial\nInputList3Cue132=[~EndGroup~]text-o\nInputList3SigType132=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=text-i101\nOutputList3SigType112=Serial\nOutputList3Cue113=text-i102\nOutputList3SigType113=Serial\nOutputList3Cue114=text-i103\nOutputList3SigType114=Serial\nOutputList3Cue115=text-i104\nOutputList3SigType115=Serial\nOutputList3Cue116=text-i105\nOutputList3SigType116=Serial\nOutputList3Cue117=text-i106\nOutputList3SigType117=Serial\nOutputList3Cue118=text-i107\nOutputList3SigType118=Serial\nOutputList3Cue119=text-i108\nOutputList3SigType119=Serial\nOutputList3Cue120=text-i109\nOutputList3SigType120=Serial\nOutputList3Cue121=text-i110\nOutputList3SigType121=Serial\nOutputList3Cue122=text-i111\nOutputList3SigType122=Serial\nOutputList3Cue123=text-i112\nOutputList3SigType123=Serial\nOutputList3Cue124=text-i113\nOutputList3SigType124=Serial\nOutputList3Cue125=text-i114\nOutputList3SigType125=Serial\nOutputList3Cue126=text-i115\nOutputList3SigType126=Serial\nOutputList3Cue127=text-i116\nOutputList3SigType127=Serial\nOutputList3Cue128=text-i117\nOutputList3SigType128=Serial\nOutputList3Cue129=text-i118\nOutputList3SigType129=Serial\nOutputList3Cue130=text-i119\nOutputList3SigType130=Serial\nOutputList3Cue131=text-i120\nOutputList3SigType131=Serial\nOutputList3Cue132=[~EndGroup~]text-i\nOutputList3SigType132=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=15\nCedH=15\nSmartObjId=3333d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=15\nTp=1\nHD=TRUE\nDV=3333d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=15\nName=PepperDash Essentials iPad_[B.AV] Source-Set Top Box Backer_CATV Icon List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\nHint=Subpage Reference List Vertical_2 (Smart Object ID=1001)\nCode=16\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical_2\nGUID=5F63F5C2-A987-4957-9243-C28805CE297E\nSmplCName=PepperDash Essentials iPad_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4116\nMaxVariableInputs=4116\nMinVariableOutputs=4116\nMaxVariableOutputs=4116\nNumFixedParams=1\nMinVariableInputsList2=112\nMaxVariableInputsList2=112\nMinVariableOutputsList2=112\nMaxVariableOutputsList2=112\nMinVariableInputsList3=112\nMaxVariableInputsList3=112\nMinVariableOutputsList3=112\nMaxVariableOutputsList3=112\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=Item 13 Enable\nInputSigType24=Digital\nInputCue25=Item 14 Enable\nInputSigType25=Digital\nInputCue26=Item 15 Enable\nInputSigType26=Digital\nInputCue27=Item 16 Enable\nInputSigType27=Digital\nInputCue28=Item 17 Enable\nInputSigType28=Digital\nInputCue29=Item 18 Enable\nInputSigType29=Digital\nInputCue30=Item 19 Enable\nInputSigType30=Digital\nInputCue31=Item 20 Enable\nInputSigType31=Digital\nInputCue32=[~UNUSED2~]\nInputSigType32=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=Item 13 Visible\nInputSigType2026=Digital\nInputCue2027=Item 14 Visible\nInputSigType2027=Digital\nInputCue2028=Item 15 Visible\nInputSigType2028=Digital\nInputCue2029=Item 16 Visible\nInputSigType2029=Digital\nInputCue2030=Item 17 Visible\nInputSigType2030=Digital\nInputCue2031=Item 18 Visible\nInputSigType2031=Digital\nInputCue2032=Item 19 Visible\nInputSigType2032=Digital\nInputCue2033=Item 20 Visible\nInputSigType2033=Digital\nInputCue2034=[~UNUSED2~]\nInputSigType2034=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=fb37\nInputSigType4052=Digital\nInputCue4053=fb38\nInputSigType4053=Digital\nInputCue4054=fb39\nInputSigType4054=Digital\nInputCue4055=fb40\nInputSigType4055=Digital\nInputCue4056=fb41\nInputSigType4056=Digital\nInputCue4057=fb42\nInputSigType4057=Digital\nInputCue4058=fb43\nInputSigType4058=Digital\nInputCue4059=fb44\nInputSigType4059=Digital\nInputCue4060=fb45\nInputSigType4060=Digital\nInputCue4061=fb46\nInputSigType4061=Digital\nInputCue4062=fb47\nInputSigType4062=Digital\nInputCue4063=fb48\nInputSigType4063=Digital\nInputCue4064=fb49\nInputSigType4064=Digital\nInputCue4065=fb50\nInputSigType4065=Digital\nInputCue4066=fb51\nInputSigType4066=Digital\nInputCue4067=fb52\nInputSigType4067=Digital\nInputCue4068=fb53\nInputSigType4068=Digital\nInputCue4069=fb54\nInputSigType4069=Digital\nInputCue4070=fb55\nInputSigType4070=Digital\nInputCue4071=fb56\nInputSigType4071=Digital\nInputCue4072=fb57\nInputSigType4072=Digital\nInputCue4073=fb58\nInputSigType4073=Digital\nInputCue4074=fb59\nInputSigType4074=Digital\nInputCue4075=fb60\nInputSigType4075=Digital\nInputCue4076=fb61\nInputSigType4076=Digital\nInputCue4077=fb62\nInputSigType4077=Digital\nInputCue4078=fb63\nInputSigType4078=Digital\nInputCue4079=fb64\nInputSigType4079=Digital\nInputCue4080=fb65\nInputSigType4080=Digital\nInputCue4081=fb66\nInputSigType4081=Digital\nInputCue4082=fb67\nInputSigType4082=Digital\nInputCue4083=fb68\nInputSigType4083=Digital\nInputCue4084=fb69\nInputSigType4084=Digital\nInputCue4085=fb70\nInputSigType4085=Digital\nInputCue4086=fb71\nInputSigType4086=Digital\nInputCue4087=fb72\nInputSigType4087=Digital\nInputCue4088=fb73\nInputSigType4088=Digital\nInputCue4089=fb74\nInputSigType4089=Digital\nInputCue4090=fb75\nInputSigType4090=Digital\nInputCue4091=fb76\nInputSigType4091=Digital\nInputCue4092=fb77\nInputSigType4092=Digital\nInputCue4093=fb78\nInputSigType4093=Digital\nInputCue4094=fb79\nInputSigType4094=Digital\nInputCue4095=fb80\nInputSigType4095=Digital\nInputCue4096=fb81\nInputSigType4096=Digital\nInputCue4097=fb82\nInputSigType4097=Digital\nInputCue4098=fb83\nInputSigType4098=Digital\nInputCue4099=fb84\nInputSigType4099=Digital\nInputCue4100=fb85\nInputSigType4100=Digital\nInputCue4101=fb86\nInputSigType4101=Digital\nInputCue4102=fb87\nInputSigType4102=Digital\nInputCue4103=fb88\nInputSigType4103=Digital\nInputCue4104=fb89\nInputSigType4104=Digital\nInputCue4105=fb90\nInputSigType4105=Digital\nInputCue4106=fb91\nInputSigType4106=Digital\nInputCue4107=fb92\nInputSigType4107=Digital\nInputCue4108=fb93\nInputSigType4108=Digital\nInputCue4109=fb94\nInputSigType4109=Digital\nInputCue4110=fb95\nInputSigType4110=Digital\nInputCue4111=fb96\nInputSigType4111=Digital\nInputCue4112=fb97\nInputSigType4112=Digital\nInputCue4113=fb98\nInputSigType4113=Digital\nInputCue4114=fb99\nInputSigType4114=Digital\nInputCue4115=fb100\nInputSigType4115=Digital\nInputCue4116=[~EndGroup~]fb\nInputSigType4116=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED3~]\nOutputSigType24=Digital\nOutputCue25=[~UNUSED3~]\nOutputSigType25=Digital\nOutputCue26=[~UNUSED3~]\nOutputSigType26=Digital\nOutputCue27=[~UNUSED3~]\nOutputSigType27=Digital\nOutputCue28=[~UNUSED3~]\nOutputSigType28=Digital\nOutputCue29=[~UNUSED3~]\nOutputSigType29=Digital\nOutputCue30=[~UNUSED3~]\nOutputSigType30=Digital\nOutputCue31=[~UNUSED3~]\nOutputSigType31=Digital\nOutputCue32=[~UNUSED2~]\nOutputSigType32=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED2~]\nOutputSigType2034=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=press37\nOutputSigType4052=Digital\nOutputCue4053=press38\nOutputSigType4053=Digital\nOutputCue4054=press39\nOutputSigType4054=Digital\nOutputCue4055=press40\nOutputSigType4055=Digital\nOutputCue4056=press41\nOutputSigType4056=Digital\nOutputCue4057=press42\nOutputSigType4057=Digital\nOutputCue4058=press43\nOutputSigType4058=Digital\nOutputCue4059=press44\nOutputSigType4059=Digital\nOutputCue4060=press45\nOutputSigType4060=Digital\nOutputCue4061=press46\nOutputSigType4061=Digital\nOutputCue4062=press47\nOutputSigType4062=Digital\nOutputCue4063=press48\nOutputSigType4063=Digital\nOutputCue4064=press49\nOutputSigType4064=Digital\nOutputCue4065=press50\nOutputSigType4065=Digital\nOutputCue4066=press51\nOutputSigType4066=Digital\nOutputCue4067=press52\nOutputSigType4067=Digital\nOutputCue4068=press53\nOutputSigType4068=Digital\nOutputCue4069=press54\nOutputSigType4069=Digital\nOutputCue4070=press55\nOutputSigType4070=Digital\nOutputCue4071=press56\nOutputSigType4071=Digital\nOutputCue4072=press57\nOutputSigType4072=Digital\nOutputCue4073=press58\nOutputSigType4073=Digital\nOutputCue4074=press59\nOutputSigType4074=Digital\nOutputCue4075=press60\nOutputSigType4075=Digital\nOutputCue4076=press61\nOutputSigType4076=Digital\nOutputCue4077=press62\nOutputSigType4077=Digital\nOutputCue4078=press63\nOutputSigType4078=Digital\nOutputCue4079=press64\nOutputSigType4079=Digital\nOutputCue4080=press65\nOutputSigType4080=Digital\nOutputCue4081=press66\nOutputSigType4081=Digital\nOutputCue4082=press67\nOutputSigType4082=Digital\nOutputCue4083=press68\nOutputSigType4083=Digital\nOutputCue4084=press69\nOutputSigType4084=Digital\nOutputCue4085=press70\nOutputSigType4085=Digital\nOutputCue4086=press71\nOutputSigType4086=Digital\nOutputCue4087=press72\nOutputSigType4087=Digital\nOutputCue4088=press73\nOutputSigType4088=Digital\nOutputCue4089=press74\nOutputSigType4089=Digital\nOutputCue4090=press75\nOutputSigType4090=Digital\nOutputCue4091=press76\nOutputSigType4091=Digital\nOutputCue4092=press77\nOutputSigType4092=Digital\nOutputCue4093=press78\nOutputSigType4093=Digital\nOutputCue4094=press79\nOutputSigType4094=Digital\nOutputCue4095=press80\nOutputSigType4095=Digital\nOutputCue4096=press81\nOutputSigType4096=Digital\nOutputCue4097=press82\nOutputSigType4097=Digital\nOutputCue4098=press83\nOutputSigType4098=Digital\nOutputCue4099=press84\nOutputSigType4099=Digital\nOutputCue4100=press85\nOutputSigType4100=Digital\nOutputCue4101=press86\nOutputSigType4101=Digital\nOutputCue4102=press87\nOutputSigType4102=Digital\nOutputCue4103=press88\nOutputSigType4103=Digital\nOutputCue4104=press89\nOutputSigType4104=Digital\nOutputCue4105=press90\nOutputSigType4105=Digital\nOutputCue4106=press91\nOutputSigType4106=Digital\nOutputCue4107=press92\nOutputSigType4107=Digital\nOutputCue4108=press93\nOutputSigType4108=Digital\nOutputCue4109=press94\nOutputSigType4109=Digital\nOutputCue4110=press95\nOutputSigType4110=Digital\nOutputCue4111=press96\nOutputSigType4111=Digital\nOutputCue4112=press97\nOutputSigType4112=Digital\nOutputCue4113=press98\nOutputSigType4113=Digital\nOutputCue4114=press99\nOutputSigType4114=Digital\nOutputCue4115=press100\nOutputSigType4115=Digital\nOutputCue4116=[~EndGroup~]Press\nOutputSigType4116=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=an_fb37\nInputList2SigType48=Analog\nInputList2Cue49=an_fb38\nInputList2SigType49=Analog\nInputList2Cue50=an_fb39\nInputList2SigType50=Analog\nInputList2Cue51=an_fb40\nInputList2SigType51=Analog\nInputList2Cue52=an_fb41\nInputList2SigType52=Analog\nInputList2Cue53=an_fb42\nInputList2SigType53=Analog\nInputList2Cue54=an_fb43\nInputList2SigType54=Analog\nInputList2Cue55=an_fb44\nInputList2SigType55=Analog\nInputList2Cue56=an_fb45\nInputList2SigType56=Analog\nInputList2Cue57=an_fb46\nInputList2SigType57=Analog\nInputList2Cue58=an_fb47\nInputList2SigType58=Analog\nInputList2Cue59=an_fb48\nInputList2SigType59=Analog\nInputList2Cue60=an_fb49\nInputList2SigType60=Analog\nInputList2Cue61=an_fb50\nInputList2SigType61=Analog\nInputList2Cue62=an_fb51\nInputList2SigType62=Analog\nInputList2Cue63=an_fb52\nInputList2SigType63=Analog\nInputList2Cue64=an_fb53\nInputList2SigType64=Analog\nInputList2Cue65=an_fb54\nInputList2SigType65=Analog\nInputList2Cue66=an_fb55\nInputList2SigType66=Analog\nInputList2Cue67=an_fb56\nInputList2SigType67=Analog\nInputList2Cue68=an_fb57\nInputList2SigType68=Analog\nInputList2Cue69=an_fb58\nInputList2SigType69=Analog\nInputList2Cue70=an_fb59\nInputList2SigType70=Analog\nInputList2Cue71=an_fb60\nInputList2SigType71=Analog\nInputList2Cue72=an_fb61\nInputList2SigType72=Analog\nInputList2Cue73=an_fb62\nInputList2SigType73=Analog\nInputList2Cue74=an_fb63\nInputList2SigType74=Analog\nInputList2Cue75=an_fb64\nInputList2SigType75=Analog\nInputList2Cue76=an_fb65\nInputList2SigType76=Analog\nInputList2Cue77=an_fb66\nInputList2SigType77=Analog\nInputList2Cue78=an_fb67\nInputList2SigType78=Analog\nInputList2Cue79=an_fb68\nInputList2SigType79=Analog\nInputList2Cue80=an_fb69\nInputList2SigType80=Analog\nInputList2Cue81=an_fb70\nInputList2SigType81=Analog\nInputList2Cue82=an_fb71\nInputList2SigType82=Analog\nInputList2Cue83=an_fb72\nInputList2SigType83=Analog\nInputList2Cue84=an_fb73\nInputList2SigType84=Analog\nInputList2Cue85=an_fb74\nInputList2SigType85=Analog\nInputList2Cue86=an_fb75\nInputList2SigType86=Analog\nInputList2Cue87=an_fb76\nInputList2SigType87=Analog\nInputList2Cue88=an_fb77\nInputList2SigType88=Analog\nInputList2Cue89=an_fb78\nInputList2SigType89=Analog\nInputList2Cue90=an_fb79\nInputList2SigType90=Analog\nInputList2Cue91=an_fb80\nInputList2SigType91=Analog\nInputList2Cue92=an_fb81\nInputList2SigType92=Analog\nInputList2Cue93=an_fb82\nInputList2SigType93=Analog\nInputList2Cue94=an_fb83\nInputList2SigType94=Analog\nInputList2Cue95=an_fb84\nInputList2SigType95=Analog\nInputList2Cue96=an_fb85\nInputList2SigType96=Analog\nInputList2Cue97=an_fb86\nInputList2SigType97=Analog\nInputList2Cue98=an_fb87\nInputList2SigType98=Analog\nInputList2Cue99=an_fb88\nInputList2SigType99=Analog\nInputList2Cue100=an_fb89\nInputList2SigType100=Analog\nInputList2Cue101=an_fb90\nInputList2SigType101=Analog\nInputList2Cue102=an_fb91\nInputList2SigType102=Analog\nInputList2Cue103=an_fb92\nInputList2SigType103=Analog\nInputList2Cue104=an_fb93\nInputList2SigType104=Analog\nInputList2Cue105=an_fb94\nInputList2SigType105=Analog\nInputList2Cue106=an_fb95\nInputList2SigType106=Analog\nInputList2Cue107=an_fb96\nInputList2SigType107=Analog\nInputList2Cue108=an_fb97\nInputList2SigType108=Analog\nInputList2Cue109=an_fb98\nInputList2SigType109=Analog\nInputList2Cue110=an_fb99\nInputList2SigType110=Analog\nInputList2Cue111=an_fb100\nInputList2SigType111=Analog\nInputList2Cue112=[~EndGroup~]an_fb\nInputList2SigType112=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=an_act37\nOutputList2SigType48=Analog\nOutputList2Cue49=an_act38\nOutputList2SigType49=Analog\nOutputList2Cue50=an_act39\nOutputList2SigType50=Analog\nOutputList2Cue51=an_act40\nOutputList2SigType51=Analog\nOutputList2Cue52=an_act41\nOutputList2SigType52=Analog\nOutputList2Cue53=an_act42\nOutputList2SigType53=Analog\nOutputList2Cue54=an_act43\nOutputList2SigType54=Analog\nOutputList2Cue55=an_act44\nOutputList2SigType55=Analog\nOutputList2Cue56=an_act45\nOutputList2SigType56=Analog\nOutputList2Cue57=an_act46\nOutputList2SigType57=Analog\nOutputList2Cue58=an_act47\nOutputList2SigType58=Analog\nOutputList2Cue59=an_act48\nOutputList2SigType59=Analog\nOutputList2Cue60=an_act49\nOutputList2SigType60=Analog\nOutputList2Cue61=an_act50\nOutputList2SigType61=Analog\nOutputList2Cue62=an_act51\nOutputList2SigType62=Analog\nOutputList2Cue63=an_act52\nOutputList2SigType63=Analog\nOutputList2Cue64=an_act53\nOutputList2SigType64=Analog\nOutputList2Cue65=an_act54\nOutputList2SigType65=Analog\nOutputList2Cue66=an_act55\nOutputList2SigType66=Analog\nOutputList2Cue67=an_act56\nOutputList2SigType67=Analog\nOutputList2Cue68=an_act57\nOutputList2SigType68=Analog\nOutputList2Cue69=an_act58\nOutputList2SigType69=Analog\nOutputList2Cue70=an_act59\nOutputList2SigType70=Analog\nOutputList2Cue71=an_act60\nOutputList2SigType71=Analog\nOutputList2Cue72=an_act61\nOutputList2SigType72=Analog\nOutputList2Cue73=an_act62\nOutputList2SigType73=Analog\nOutputList2Cue74=an_act63\nOutputList2SigType74=Analog\nOutputList2Cue75=an_act64\nOutputList2SigType75=Analog\nOutputList2Cue76=an_act65\nOutputList2SigType76=Analog\nOutputList2Cue77=an_act66\nOutputList2SigType77=Analog\nOutputList2Cue78=an_act67\nOutputList2SigType78=Analog\nOutputList2Cue79=an_act68\nOutputList2SigType79=Analog\nOutputList2Cue80=an_act69\nOutputList2SigType80=Analog\nOutputList2Cue81=an_act70\nOutputList2SigType81=Analog\nOutputList2Cue82=an_act71\nOutputList2SigType82=Analog\nOutputList2Cue83=an_act72\nOutputList2SigType83=Analog\nOutputList2Cue84=an_act73\nOutputList2SigType84=Analog\nOutputList2Cue85=an_act74\nOutputList2SigType85=Analog\nOutputList2Cue86=an_act75\nOutputList2SigType86=Analog\nOutputList2Cue87=an_act76\nOutputList2SigType87=Analog\nOutputList2Cue88=an_act77\nOutputList2SigType88=Analog\nOutputList2Cue89=an_act78\nOutputList2SigType89=Analog\nOutputList2Cue90=an_act79\nOutputList2SigType90=Analog\nOutputList2Cue91=an_act80\nOutputList2SigType91=Analog\nOutputList2Cue92=an_act81\nOutputList2SigType92=Analog\nOutputList2Cue93=an_act82\nOutputList2SigType93=Analog\nOutputList2Cue94=an_act83\nOutputList2SigType94=Analog\nOutputList2Cue95=an_act84\nOutputList2SigType95=Analog\nOutputList2Cue96=an_act85\nOutputList2SigType96=Analog\nOutputList2Cue97=an_act86\nOutputList2SigType97=Analog\nOutputList2Cue98=an_act87\nOutputList2SigType98=Analog\nOutputList2Cue99=an_act88\nOutputList2SigType99=Analog\nOutputList2Cue100=an_act89\nOutputList2SigType100=Analog\nOutputList2Cue101=an_act90\nOutputList2SigType101=Analog\nOutputList2Cue102=an_act91\nOutputList2SigType102=Analog\nOutputList2Cue103=an_act92\nOutputList2SigType103=Analog\nOutputList2Cue104=an_act93\nOutputList2SigType104=Analog\nOutputList2Cue105=an_act94\nOutputList2SigType105=Analog\nOutputList2Cue106=an_act95\nOutputList2SigType106=Analog\nOutputList2Cue107=an_act96\nOutputList2SigType107=Analog\nOutputList2Cue108=an_act97\nOutputList2SigType108=Analog\nOutputList2Cue109=an_act98\nOutputList2SigType109=Analog\nOutputList2Cue110=an_act99\nOutputList2SigType110=Analog\nOutputList2Cue111=an_act100\nOutputList2SigType111=Analog\nOutputList2Cue112=[~EndGroup~]an_act\nOutputList2SigType112=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=text-o61\nInputList3SigType72=Serial\nInputList3Cue73=text-o62\nInputList3SigType73=Serial\nInputList3Cue74=text-o63\nInputList3SigType74=Serial\nInputList3Cue75=text-o64\nInputList3SigType75=Serial\nInputList3Cue76=text-o65\nInputList3SigType76=Serial\nInputList3Cue77=text-o66\nInputList3SigType77=Serial\nInputList3Cue78=text-o67\nInputList3SigType78=Serial\nInputList3Cue79=text-o68\nInputList3SigType79=Serial\nInputList3Cue80=text-o69\nInputList3SigType80=Serial\nInputList3Cue81=text-o70\nInputList3SigType81=Serial\nInputList3Cue82=text-o71\nInputList3SigType82=Serial\nInputList3Cue83=text-o72\nInputList3SigType83=Serial\nInputList3Cue84=text-o73\nInputList3SigType84=Serial\nInputList3Cue85=text-o74\nInputList3SigType85=Serial\nInputList3Cue86=text-o75\nInputList3SigType86=Serial\nInputList3Cue87=text-o76\nInputList3SigType87=Serial\nInputList3Cue88=text-o77\nInputList3SigType88=Serial\nInputList3Cue89=text-o78\nInputList3SigType89=Serial\nInputList3Cue90=text-o79\nInputList3SigType90=Serial\nInputList3Cue91=text-o80\nInputList3SigType91=Serial\nInputList3Cue92=text-o81\nInputList3SigType92=Serial\nInputList3Cue93=text-o82\nInputList3SigType93=Serial\nInputList3Cue94=text-o83\nInputList3SigType94=Serial\nInputList3Cue95=text-o84\nInputList3SigType95=Serial\nInputList3Cue96=text-o85\nInputList3SigType96=Serial\nInputList3Cue97=text-o86\nInputList3SigType97=Serial\nInputList3Cue98=text-o87\nInputList3SigType98=Serial\nInputList3Cue99=text-o88\nInputList3SigType99=Serial\nInputList3Cue100=text-o89\nInputList3SigType100=Serial\nInputList3Cue101=text-o90\nInputList3SigType101=Serial\nInputList3Cue102=text-o91\nInputList3SigType102=Serial\nInputList3Cue103=text-o92\nInputList3SigType103=Serial\nInputList3Cue104=text-o93\nInputList3SigType104=Serial\nInputList3Cue105=text-o94\nInputList3SigType105=Serial\nInputList3Cue106=text-o95\nInputList3SigType106=Serial\nInputList3Cue107=text-o96\nInputList3SigType107=Serial\nInputList3Cue108=text-o97\nInputList3SigType108=Serial\nInputList3Cue109=text-o98\nInputList3SigType109=Serial\nInputList3Cue110=text-o99\nInputList3SigType110=Serial\nInputList3Cue111=text-o100\nInputList3SigType111=Serial\nInputList3Cue112=[~EndGroup~]text-o\nInputList3SigType112=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=text-i61\nOutputList3SigType72=Serial\nOutputList3Cue73=text-i62\nOutputList3SigType73=Serial\nOutputList3Cue74=text-i63\nOutputList3SigType74=Serial\nOutputList3Cue75=text-i64\nOutputList3SigType75=Serial\nOutputList3Cue76=text-i65\nOutputList3SigType76=Serial\nOutputList3Cue77=text-i66\nOutputList3SigType77=Serial\nOutputList3Cue78=text-i67\nOutputList3SigType78=Serial\nOutputList3Cue79=text-i68\nOutputList3SigType79=Serial\nOutputList3Cue80=text-i69\nOutputList3SigType80=Serial\nOutputList3Cue81=text-i70\nOutputList3SigType81=Serial\nOutputList3Cue82=text-i71\nOutputList3SigType82=Serial\nOutputList3Cue83=text-i72\nOutputList3SigType83=Serial\nOutputList3Cue84=text-i73\nOutputList3SigType84=Serial\nOutputList3Cue85=text-i74\nOutputList3SigType85=Serial\nOutputList3Cue86=text-i75\nOutputList3SigType86=Serial\nOutputList3Cue87=text-i76\nOutputList3SigType87=Serial\nOutputList3Cue88=text-i77\nOutputList3SigType88=Serial\nOutputList3Cue89=text-i78\nOutputList3SigType89=Serial\nOutputList3Cue90=text-i79\nOutputList3SigType90=Serial\nOutputList3Cue91=text-i80\nOutputList3SigType91=Serial\nOutputList3Cue92=text-i81\nOutputList3SigType92=Serial\nOutputList3Cue93=text-i82\nOutputList3SigType93=Serial\nOutputList3Cue94=text-i83\nOutputList3SigType94=Serial\nOutputList3Cue95=text-i84\nOutputList3SigType95=Serial\nOutputList3Cue96=text-i85\nOutputList3SigType96=Serial\nOutputList3Cue97=text-i86\nOutputList3SigType97=Serial\nOutputList3Cue98=text-i87\nOutputList3SigType98=Serial\nOutputList3Cue99=text-i88\nOutputList3SigType99=Serial\nOutputList3Cue100=text-i89\nOutputList3SigType100=Serial\nOutputList3Cue101=text-i90\nOutputList3SigType101=Serial\nOutputList3Cue102=text-i91\nOutputList3SigType102=Serial\nOutputList3Cue103=text-i92\nOutputList3SigType103=Serial\nOutputList3Cue104=text-i93\nOutputList3SigType104=Serial\nOutputList3Cue105=text-i94\nOutputList3SigType105=Serial\nOutputList3Cue106=text-i95\nOutputList3SigType106=Serial\nOutputList3Cue107=text-i96\nOutputList3SigType107=Serial\nOutputList3Cue108=text-i97\nOutputList3SigType108=Serial\nOutputList3Cue109=text-i98\nOutputList3SigType109=Serial\nOutputList3Cue110=text-i99\nOutputList3SigType110=Serial\nOutputList3Cue111=text-i100\nOutputList3SigType111=Serial\nOutputList3Cue112=[~EndGroup~]text-i\nOutputList3SigType112=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=16\nCedH=16\nSmartObjId=1001d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=16\nTp=1\nHD=TRUE\nDV=1001d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=16\nName=PepperDash Essentials iPad_[Z.Dialogs] Call List_Subpage Reference List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\nHint=Dynamic Button List Vertical (Smart Object ID=3901)\nCode=17\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical\nGUID=15B95903-FB30-472E-9D10-82A9C3A9D7C7\nSmplCName=PepperDash Essentials iPad_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=[~UNUSED2~]\nInputSigType22=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=[~UNUSED2~]\nInputSigType2024=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=[~UNUSED2~]\nInputSigType4026=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=[~UNUSED2~]\nOutputSigType22=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED2~]\nOutputSigType2024=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED2~]\nOutputSigType4026=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=[~UNUSED2~]\nInputList2SigType22=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED2~]\nOutputList2SigType22=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=[~UNUSED2~]\nInputList3SigType22=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=[~UNUSED2~]\nInputList3SigType2024=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED2~]\nOutputList3SigType22=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED2~]\nOutputList3SigType2024=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=17\nCedH=17\nSmartObjId=3901d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=17\nTp=1\nHD=TRUE\nDV=3901d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=17\nName=PepperDash Essentials iPad_[E.Technician Controls] Menu_Dynamic Button List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\nHint=Subpage Reference List Vertical (Smart Object ID=15023)\nCode=18\nSGControlType=Subpage Reference List Vertical\nSGControlName=Subpage Reference List Vertical\nGUID=EC6E6B35-F033-4F14-9465-FEEBE20455CC\nSmplCName=PepperDash Essentials iPad_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10125\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=4052\nMaxVariableInputs=4052\nMinVariableOutputs=4052\nMaxVariableOutputs=4052\nNumFixedParams=1\nMinVariableInputsList2=48\nMaxVariableInputsList2=48\nMinVariableOutputsList2=48\nMaxVariableOutputsList2=48\nMinVariableInputsList3=72\nMaxVariableInputsList3=72\nMinVariableOutputsList3=72\nMaxVariableOutputsList3=72\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=[~UNUSED3~]\nInputSigType1=Digital\nInputCue2=[~UNUSED2~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Enable\nInputSigType11=Digital\nInputCue12=Item 1 Enable\nInputSigType12=Digital\nInputCue13=Item 2 Enable\nInputSigType13=Digital\nInputCue14=Item 3 Enable\nInputSigType14=Digital\nInputCue15=Item 4 Enable\nInputSigType15=Digital\nInputCue16=Item 5 Enable\nInputSigType16=Digital\nInputCue17=Item 6 Enable\nInputSigType17=Digital\nInputCue18=Item 7 Enable\nInputSigType18=Digital\nInputCue19=Item 8 Enable\nInputSigType19=Digital\nInputCue20=Item 9 Enable\nInputSigType20=Digital\nInputCue21=Item 10 Enable\nInputSigType21=Digital\nInputCue22=Item 11 Enable\nInputSigType22=Digital\nInputCue23=Item 12 Enable\nInputSigType23=Digital\nInputCue24=[~UNUSED2~]\nInputSigType24=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Enable\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Visible\nInputSigType2013=Digital\nInputCue2014=Item 1 Visible\nInputSigType2014=Digital\nInputCue2015=Item 2 Visible\nInputSigType2015=Digital\nInputCue2016=Item 3 Visible\nInputSigType2016=Digital\nInputCue2017=Item 4 Visible\nInputSigType2017=Digital\nInputCue2018=Item 5 Visible\nInputSigType2018=Digital\nInputCue2019=Item 6 Visible\nInputSigType2019=Digital\nInputCue2020=Item 7 Visible\nInputSigType2020=Digital\nInputCue2021=Item 8 Visible\nInputSigType2021=Digital\nInputCue2022=Item 9 Visible\nInputSigType2022=Digital\nInputCue2023=Item 10 Visible\nInputSigType2023=Digital\nInputCue2024=Item 11 Visible\nInputSigType2024=Digital\nInputCue2025=Item 12 Visible\nInputSigType2025=Digital\nInputCue2026=[~UNUSED2~]\nInputSigType2026=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Visible\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]fb\nInputSigType4015=Digital\nInputCue4016=fb1\nInputSigType4016=Digital\nInputCue4017=fb2\nInputSigType4017=Digital\nInputCue4018=fb3\nInputSigType4018=Digital\nInputCue4019=fb4\nInputSigType4019=Digital\nInputCue4020=fb5\nInputSigType4020=Digital\nInputCue4021=fb6\nInputSigType4021=Digital\nInputCue4022=fb7\nInputSigType4022=Digital\nInputCue4023=fb8\nInputSigType4023=Digital\nInputCue4024=fb9\nInputSigType4024=Digital\nInputCue4025=fb10\nInputSigType4025=Digital\nInputCue4026=fb11\nInputSigType4026=Digital\nInputCue4027=fb12\nInputSigType4027=Digital\nInputCue4028=fb13\nInputSigType4028=Digital\nInputCue4029=fb14\nInputSigType4029=Digital\nInputCue4030=fb15\nInputSigType4030=Digital\nInputCue4031=fb16\nInputSigType4031=Digital\nInputCue4032=fb17\nInputSigType4032=Digital\nInputCue4033=fb18\nInputSigType4033=Digital\nInputCue4034=fb19\nInputSigType4034=Digital\nInputCue4035=fb20\nInputSigType4035=Digital\nInputCue4036=fb21\nInputSigType4036=Digital\nInputCue4037=fb22\nInputSigType4037=Digital\nInputCue4038=fb23\nInputSigType4038=Digital\nInputCue4039=fb24\nInputSigType4039=Digital\nInputCue4040=fb25\nInputSigType4040=Digital\nInputCue4041=fb26\nInputSigType4041=Digital\nInputCue4042=fb27\nInputSigType4042=Digital\nInputCue4043=fb28\nInputSigType4043=Digital\nInputCue4044=fb29\nInputSigType4044=Digital\nInputCue4045=fb30\nInputSigType4045=Digital\nInputCue4046=fb31\nInputSigType4046=Digital\nInputCue4047=fb32\nInputSigType4047=Digital\nInputCue4048=fb33\nInputSigType4048=Digital\nInputCue4049=fb34\nInputSigType4049=Digital\nInputCue4050=fb35\nInputSigType4050=Digital\nInputCue4051=fb36\nInputSigType4051=Digital\nInputCue4052=[~EndGroup~]fb\nInputSigType4052=Digital\nOutputCue1=Is Moving\nOutputSigType1=Digital\nOutputCue2=[~UNUSED2~]\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Enable\nOutputSigType11=Digital\nOutputCue12=[~UNUSED3~]\nOutputSigType12=Digital\nOutputCue13=[~UNUSED3~]\nOutputSigType13=Digital\nOutputCue14=[~UNUSED3~]\nOutputSigType14=Digital\nOutputCue15=[~UNUSED3~]\nOutputSigType15=Digital\nOutputCue16=[~UNUSED3~]\nOutputSigType16=Digital\nOutputCue17=[~UNUSED3~]\nOutputSigType17=Digital\nOutputCue18=[~UNUSED3~]\nOutputSigType18=Digital\nOutputCue19=[~UNUSED3~]\nOutputSigType19=Digital\nOutputCue20=[~UNUSED3~]\nOutputSigType20=Digital\nOutputCue21=[~UNUSED3~]\nOutputSigType21=Digital\nOutputCue22=[~UNUSED3~]\nOutputSigType22=Digital\nOutputCue23=[~UNUSED3~]\nOutputSigType23=Digital\nOutputCue24=[~UNUSED2~]\nOutputSigType24=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Enable\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Visible\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED2~]\nOutputSigType2026=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Visible\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Press\nOutputSigType4015=Digital\nOutputCue4016=press1\nOutputSigType4016=Digital\nOutputCue4017=press2\nOutputSigType4017=Digital\nOutputCue4018=press3\nOutputSigType4018=Digital\nOutputCue4019=press4\nOutputSigType4019=Digital\nOutputCue4020=press5\nOutputSigType4020=Digital\nOutputCue4021=press6\nOutputSigType4021=Digital\nOutputCue4022=press7\nOutputSigType4022=Digital\nOutputCue4023=press8\nOutputSigType4023=Digital\nOutputCue4024=press9\nOutputSigType4024=Digital\nOutputCue4025=press10\nOutputSigType4025=Digital\nOutputCue4026=press11\nOutputSigType4026=Digital\nOutputCue4027=press12\nOutputSigType4027=Digital\nOutputCue4028=press13\nOutputSigType4028=Digital\nOutputCue4029=press14\nOutputSigType4029=Digital\nOutputCue4030=press15\nOutputSigType4030=Digital\nOutputCue4031=press16\nOutputSigType4031=Digital\nOutputCue4032=press17\nOutputSigType4032=Digital\nOutputCue4033=press18\nOutputSigType4033=Digital\nOutputCue4034=press19\nOutputSigType4034=Digital\nOutputCue4035=press20\nOutputSigType4035=Digital\nOutputCue4036=press21\nOutputSigType4036=Digital\nOutputCue4037=press22\nOutputSigType4037=Digital\nOutputCue4038=press23\nOutputSigType4038=Digital\nOutputCue4039=press24\nOutputSigType4039=Digital\nOutputCue4040=press25\nOutputSigType4040=Digital\nOutputCue4041=press26\nOutputSigType4041=Digital\nOutputCue4042=press27\nOutputSigType4042=Digital\nOutputCue4043=press28\nOutputSigType4043=Digital\nOutputCue4044=press29\nOutputSigType4044=Digital\nOutputCue4045=press30\nOutputSigType4045=Digital\nOutputCue4046=press31\nOutputSigType4046=Digital\nOutputCue4047=press32\nOutputSigType4047=Digital\nOutputCue4048=press33\nOutputSigType4048=Digital\nOutputCue4049=press34\nOutputSigType4049=Digital\nOutputCue4050=press35\nOutputSigType4050=Digital\nOutputCue4051=press36\nOutputSigType4051=Digital\nOutputCue4052=[~EndGroup~]Press\nOutputSigType4052=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=Scroll To Item\nInputList2SigType2=Analog\nInputList2Cue3=Set Number of Items\nInputList2SigType3=Analog\nInputList2Cue4=[~UNUSED2~]\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]an_fb\nInputList2SigType11=Analog\nInputList2Cue12=an_fb1\nInputList2SigType12=Analog\nInputList2Cue13=an_fb2\nInputList2SigType13=Analog\nInputList2Cue14=an_fb3\nInputList2SigType14=Analog\nInputList2Cue15=an_fb4\nInputList2SigType15=Analog\nInputList2Cue16=an_fb5\nInputList2SigType16=Analog\nInputList2Cue17=an_fb6\nInputList2SigType17=Analog\nInputList2Cue18=an_fb7\nInputList2SigType18=Analog\nInputList2Cue19=an_fb8\nInputList2SigType19=Analog\nInputList2Cue20=an_fb9\nInputList2SigType20=Analog\nInputList2Cue21=an_fb10\nInputList2SigType21=Analog\nInputList2Cue22=an_fb11\nInputList2SigType22=Analog\nInputList2Cue23=an_fb12\nInputList2SigType23=Analog\nInputList2Cue24=an_fb13\nInputList2SigType24=Analog\nInputList2Cue25=an_fb14\nInputList2SigType25=Analog\nInputList2Cue26=an_fb15\nInputList2SigType26=Analog\nInputList2Cue27=an_fb16\nInputList2SigType27=Analog\nInputList2Cue28=an_fb17\nInputList2SigType28=Analog\nInputList2Cue29=an_fb18\nInputList2SigType29=Analog\nInputList2Cue30=an_fb19\nInputList2SigType30=Analog\nInputList2Cue31=an_fb20\nInputList2SigType31=Analog\nInputList2Cue32=an_fb21\nInputList2SigType32=Analog\nInputList2Cue33=an_fb22\nInputList2SigType33=Analog\nInputList2Cue34=an_fb23\nInputList2SigType34=Analog\nInputList2Cue35=an_fb24\nInputList2SigType35=Analog\nInputList2Cue36=an_fb25\nInputList2SigType36=Analog\nInputList2Cue37=an_fb26\nInputList2SigType37=Analog\nInputList2Cue38=an_fb27\nInputList2SigType38=Analog\nInputList2Cue39=an_fb28\nInputList2SigType39=Analog\nInputList2Cue40=an_fb29\nInputList2SigType40=Analog\nInputList2Cue41=an_fb30\nInputList2SigType41=Analog\nInputList2Cue42=an_fb31\nInputList2SigType42=Analog\nInputList2Cue43=an_fb32\nInputList2SigType43=Analog\nInputList2Cue44=an_fb33\nInputList2SigType44=Analog\nInputList2Cue45=an_fb34\nInputList2SigType45=Analog\nInputList2Cue46=an_fb35\nInputList2SigType46=Analog\nInputList2Cue47=an_fb36\nInputList2SigType47=Analog\nInputList2Cue48=[~EndGroup~]an_fb\nInputList2SigType48=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=[~UNUSED3~]\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED2~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]an_act\nOutputList2SigType11=Analog\nOutputList2Cue12=an_act1\nOutputList2SigType12=Analog\nOutputList2Cue13=an_act2\nOutputList2SigType13=Analog\nOutputList2Cue14=an_act3\nOutputList2SigType14=Analog\nOutputList2Cue15=an_act4\nOutputList2SigType15=Analog\nOutputList2Cue16=an_act5\nOutputList2SigType16=Analog\nOutputList2Cue17=an_act6\nOutputList2SigType17=Analog\nOutputList2Cue18=an_act7\nOutputList2SigType18=Analog\nOutputList2Cue19=an_act8\nOutputList2SigType19=Analog\nOutputList2Cue20=an_act9\nOutputList2SigType20=Analog\nOutputList2Cue21=an_act10\nOutputList2SigType21=Analog\nOutputList2Cue22=an_act11\nOutputList2SigType22=Analog\nOutputList2Cue23=an_act12\nOutputList2SigType23=Analog\nOutputList2Cue24=an_act13\nOutputList2SigType24=Analog\nOutputList2Cue25=an_act14\nOutputList2SigType25=Analog\nOutputList2Cue26=an_act15\nOutputList2SigType26=Analog\nOutputList2Cue27=an_act16\nOutputList2SigType27=Analog\nOutputList2Cue28=an_act17\nOutputList2SigType28=Analog\nOutputList2Cue29=an_act18\nOutputList2SigType29=Analog\nOutputList2Cue30=an_act19\nOutputList2SigType30=Analog\nOutputList2Cue31=an_act20\nOutputList2SigType31=Analog\nOutputList2Cue32=an_act21\nOutputList2SigType32=Analog\nOutputList2Cue33=an_act22\nOutputList2SigType33=Analog\nOutputList2Cue34=an_act23\nOutputList2SigType34=Analog\nOutputList2Cue35=an_act24\nOutputList2SigType35=Analog\nOutputList2Cue36=an_act25\nOutputList2SigType36=Analog\nOutputList2Cue37=an_act26\nOutputList2SigType37=Analog\nOutputList2Cue38=an_act27\nOutputList2SigType38=Analog\nOutputList2Cue39=an_act28\nOutputList2SigType39=Analog\nOutputList2Cue40=an_act29\nOutputList2SigType40=Analog\nOutputList2Cue41=an_act30\nOutputList2SigType41=Analog\nOutputList2Cue42=an_act31\nOutputList2SigType42=Analog\nOutputList2Cue43=an_act32\nOutputList2SigType43=Analog\nOutputList2Cue44=an_act33\nOutputList2SigType44=Analog\nOutputList2Cue45=an_act34\nOutputList2SigType45=Analog\nOutputList2Cue46=an_act35\nOutputList2SigType46=Analog\nOutputList2Cue47=an_act36\nOutputList2SigType47=Analog\nOutputList2Cue48=[~EndGroup~]an_act\nOutputList2SigType48=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]text-o\nInputList3SigType11=Serial\nInputList3Cue12=text-o1\nInputList3SigType12=Serial\nInputList3Cue13=text-o2\nInputList3SigType13=Serial\nInputList3Cue14=text-o3\nInputList3SigType14=Serial\nInputList3Cue15=text-o4\nInputList3SigType15=Serial\nInputList3Cue16=text-o5\nInputList3SigType16=Serial\nInputList3Cue17=text-o6\nInputList3SigType17=Serial\nInputList3Cue18=text-o7\nInputList3SigType18=Serial\nInputList3Cue19=text-o8\nInputList3SigType19=Serial\nInputList3Cue20=text-o9\nInputList3SigType20=Serial\nInputList3Cue21=text-o10\nInputList3SigType21=Serial\nInputList3Cue22=text-o11\nInputList3SigType22=Serial\nInputList3Cue23=text-o12\nInputList3SigType23=Serial\nInputList3Cue24=text-o13\nInputList3SigType24=Serial\nInputList3Cue25=text-o14\nInputList3SigType25=Serial\nInputList3Cue26=text-o15\nInputList3SigType26=Serial\nInputList3Cue27=text-o16\nInputList3SigType27=Serial\nInputList3Cue28=text-o17\nInputList3SigType28=Serial\nInputList3Cue29=text-o18\nInputList3SigType29=Serial\nInputList3Cue30=text-o19\nInputList3SigType30=Serial\nInputList3Cue31=text-o20\nInputList3SigType31=Serial\nInputList3Cue32=text-o21\nInputList3SigType32=Serial\nInputList3Cue33=text-o22\nInputList3SigType33=Serial\nInputList3Cue34=text-o23\nInputList3SigType34=Serial\nInputList3Cue35=text-o24\nInputList3SigType35=Serial\nInputList3Cue36=text-o25\nInputList3SigType36=Serial\nInputList3Cue37=text-o26\nInputList3SigType37=Serial\nInputList3Cue38=text-o27\nInputList3SigType38=Serial\nInputList3Cue39=text-o28\nInputList3SigType39=Serial\nInputList3Cue40=text-o29\nInputList3SigType40=Serial\nInputList3Cue41=text-o30\nInputList3SigType41=Serial\nInputList3Cue42=text-o31\nInputList3SigType42=Serial\nInputList3Cue43=text-o32\nInputList3SigType43=Serial\nInputList3Cue44=text-o33\nInputList3SigType44=Serial\nInputList3Cue45=text-o34\nInputList3SigType45=Serial\nInputList3Cue46=text-o35\nInputList3SigType46=Serial\nInputList3Cue47=text-o36\nInputList3SigType47=Serial\nInputList3Cue48=text-o37\nInputList3SigType48=Serial\nInputList3Cue49=text-o38\nInputList3SigType49=Serial\nInputList3Cue50=text-o39\nInputList3SigType50=Serial\nInputList3Cue51=text-o40\nInputList3SigType51=Serial\nInputList3Cue52=text-o41\nInputList3SigType52=Serial\nInputList3Cue53=text-o42\nInputList3SigType53=Serial\nInputList3Cue54=text-o43\nInputList3SigType54=Serial\nInputList3Cue55=text-o44\nInputList3SigType55=Serial\nInputList3Cue56=text-o45\nInputList3SigType56=Serial\nInputList3Cue57=text-o46\nInputList3SigType57=Serial\nInputList3Cue58=text-o47\nInputList3SigType58=Serial\nInputList3Cue59=text-o48\nInputList3SigType59=Serial\nInputList3Cue60=text-o49\nInputList3SigType60=Serial\nInputList3Cue61=text-o50\nInputList3SigType61=Serial\nInputList3Cue62=text-o51\nInputList3SigType62=Serial\nInputList3Cue63=text-o52\nInputList3SigType63=Serial\nInputList3Cue64=text-o53\nInputList3SigType64=Serial\nInputList3Cue65=text-o54\nInputList3SigType65=Serial\nInputList3Cue66=text-o55\nInputList3SigType66=Serial\nInputList3Cue67=text-o56\nInputList3SigType67=Serial\nInputList3Cue68=text-o57\nInputList3SigType68=Serial\nInputList3Cue69=text-o58\nInputList3SigType69=Serial\nInputList3Cue70=text-o59\nInputList3SigType70=Serial\nInputList3Cue71=text-o60\nInputList3SigType71=Serial\nInputList3Cue72=[~EndGroup~]text-o\nInputList3SigType72=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]text-i\nOutputList3SigType11=Serial\nOutputList3Cue12=text-i1\nOutputList3SigType12=Serial\nOutputList3Cue13=text-i2\nOutputList3SigType13=Serial\nOutputList3Cue14=text-i3\nOutputList3SigType14=Serial\nOutputList3Cue15=text-i4\nOutputList3SigType15=Serial\nOutputList3Cue16=text-i5\nOutputList3SigType16=Serial\nOutputList3Cue17=text-i6\nOutputList3SigType17=Serial\nOutputList3Cue18=text-i7\nOutputList3SigType18=Serial\nOutputList3Cue19=text-i8\nOutputList3SigType19=Serial\nOutputList3Cue20=text-i9\nOutputList3SigType20=Serial\nOutputList3Cue21=text-i10\nOutputList3SigType21=Serial\nOutputList3Cue22=text-i11\nOutputList3SigType22=Serial\nOutputList3Cue23=text-i12\nOutputList3SigType23=Serial\nOutputList3Cue24=text-i13\nOutputList3SigType24=Serial\nOutputList3Cue25=text-i14\nOutputList3SigType25=Serial\nOutputList3Cue26=text-i15\nOutputList3SigType26=Serial\nOutputList3Cue27=text-i16\nOutputList3SigType27=Serial\nOutputList3Cue28=text-i17\nOutputList3SigType28=Serial\nOutputList3Cue29=text-i18\nOutputList3SigType29=Serial\nOutputList3Cue30=text-i19\nOutputList3SigType30=Serial\nOutputList3Cue31=text-i20\nOutputList3SigType31=Serial\nOutputList3Cue32=text-i21\nOutputList3SigType32=Serial\nOutputList3Cue33=text-i22\nOutputList3SigType33=Serial\nOutputList3Cue34=text-i23\nOutputList3SigType34=Serial\nOutputList3Cue35=text-i24\nOutputList3SigType35=Serial\nOutputList3Cue36=text-i25\nOutputList3SigType36=Serial\nOutputList3Cue37=text-i26\nOutputList3SigType37=Serial\nOutputList3Cue38=text-i27\nOutputList3SigType38=Serial\nOutputList3Cue39=text-i28\nOutputList3SigType39=Serial\nOutputList3Cue40=text-i29\nOutputList3SigType40=Serial\nOutputList3Cue41=text-i30\nOutputList3SigType41=Serial\nOutputList3Cue42=text-i31\nOutputList3SigType42=Serial\nOutputList3Cue43=text-i32\nOutputList3SigType43=Serial\nOutputList3Cue44=text-i33\nOutputList3SigType44=Serial\nOutputList3Cue45=text-i34\nOutputList3SigType45=Serial\nOutputList3Cue46=text-i35\nOutputList3SigType46=Serial\nOutputList3Cue47=text-i36\nOutputList3SigType47=Serial\nOutputList3Cue48=text-i37\nOutputList3SigType48=Serial\nOutputList3Cue49=text-i38\nOutputList3SigType49=Serial\nOutputList3Cue50=text-i39\nOutputList3SigType50=Serial\nOutputList3Cue51=text-i40\nOutputList3SigType51=Serial\nOutputList3Cue52=text-i41\nOutputList3SigType52=Serial\nOutputList3Cue53=text-i42\nOutputList3SigType53=Serial\nOutputList3Cue54=text-i43\nOutputList3SigType54=Serial\nOutputList3Cue55=text-i44\nOutputList3SigType55=Serial\nOutputList3Cue56=text-i45\nOutputList3SigType56=Serial\nOutputList3Cue57=text-i46\nOutputList3SigType57=Serial\nOutputList3Cue58=text-i47\nOutputList3SigType58=Serial\nOutputList3Cue59=text-i48\nOutputList3SigType59=Serial\nOutputList3Cue60=text-i49\nOutputList3SigType60=Serial\nOutputList3Cue61=text-i50\nOutputList3SigType61=Serial\nOutputList3Cue62=text-i51\nOutputList3SigType62=Serial\nOutputList3Cue63=text-i52\nOutputList3SigType63=Serial\nOutputList3Cue64=text-i53\nOutputList3SigType64=Serial\nOutputList3Cue65=text-i54\nOutputList3SigType65=Serial\nOutputList3Cue66=text-i55\nOutputList3SigType66=Serial\nOutputList3Cue67=text-i56\nOutputList3SigType67=Serial\nOutputList3Cue68=text-i57\nOutputList3SigType68=Serial\nOutputList3Cue69=text-i58\nOutputList3SigType69=Serial\nOutputList3Cue70=text-i59\nOutputList3SigType70=Serial\nOutputList3Cue71=text-i60\nOutputList3SigType71=Serial\nOutputList3Cue72=[~EndGroup~]text-i\nOutputList3SigType72=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=18\nCedH=18\nSmartObjId=15023d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=18\nTp=1\nHD=TRUE\nDV=15023d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=18\nName=PepperDash Essentials iPad_[Z.Dialogs] Schedule List_Subpage Reference List Vertical.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n;================================================================================\n[\nObjTp=Symbol\nName=PepperDash Essentials iPad_[D.VC] Recents_Dynamic Button List Vertical_2.ced\nHint=Dynamic Button List Vertical_2 (Smart Object ID=1203)\nCode=19\nSGControlType=Dynamic Button List Vertical\nSGControlName=Dynamic Button List Vertical_2\nGUID=2C4B55E4-42AC-486D-BC6E-C7DD7D24C974\nSmplCName=PepperDash Essentials iPad_[D.VC] Recents_Dynamic Button List Vertical_2.ced\nSMWRev=4.02.20\nExpand=expand_random\nHelpID=10112\nRender=8\n;Define the number of inputs, outputs and parameters\nMinVariableInputs=6016\nMaxVariableInputs=6016\nMinVariableOutputs=6016\nMaxVariableOutputs=6016\nNumFixedParams=1\nMinVariableInputsList2=2012\nMaxVariableInputsList2=2012\nMinVariableOutputsList2=2012\nMaxVariableOutputsList2=2012\nMinVariableInputsList3=4014\nMaxVariableInputsList3=4014\nMinVariableOutputsList3=4014\nMaxVariableOutputsList3=4014\nInputSigType1=Digital\nOutputSigType1=Digital\nInputList2SigType1=Analog\nOutputList2SigType1=Analog\nInputList3SigType1=Serial\nOutputList3SigType1=Serial\n\n;Define the cues, and signal types  each input, output and parameter.\nInputCue1=Disable Redraw\nInputSigType1=Digital\nInputCue2=[~UNUSED3~]\nInputSigType2=Digital\nInputCue3=[~UNUSED2~]\nInputSigType3=Digital\nInputCue4=[~UNUSED2~]\nInputSigType4=Digital\nInputCue5=[~UNUSED2~]\nInputSigType5=Digital\nInputCue6=[~UNUSED2~]\nInputSigType6=Digital\nInputCue7=[~UNUSED2~]\nInputSigType7=Digital\nInputCue8=[~UNUSED2~]\nInputSigType8=Digital\nInputCue9=[~UNUSED2~]\nInputSigType9=Digital\nInputCue10=[~UNUSED2~]\nInputSigType10=Digital\nInputCue11=[~BeginGroup~]Press\nInputSigType11=Digital\nInputCue12=Item 1 Selected\nInputSigType12=Digital\nInputCue13=Item 2 Selected\nInputSigType13=Digital\nInputCue14=Item 3 Selected\nInputSigType14=Digital\nInputCue15=Item 4 Selected\nInputSigType15=Digital\nInputCue16=Item 5 Selected\nInputSigType16=Digital\nInputCue17=Item 6 Selected\nInputSigType17=Digital\nInputCue18=Item 7 Selected\nInputSigType18=Digital\nInputCue19=Item 8 Selected\nInputSigType19=Digital\nInputCue20=Item 9 Selected\nInputSigType20=Digital\nInputCue21=Item 10 Selected\nInputSigType21=Digital\nInputCue22=Item 11 Selected\nInputSigType22=Digital\nInputCue23=Item 12 Selected\nInputSigType23=Digital\nInputCue24=Item 13 Selected\nInputSigType24=Digital\nInputCue25=Item 14 Selected\nInputSigType25=Digital\nInputCue26=Item 15 Selected\nInputSigType26=Digital\nInputCue27=Item 16 Selected\nInputSigType27=Digital\nInputCue28=Item 17 Selected\nInputSigType28=Digital\nInputCue29=Item 18 Selected\nInputSigType29=Digital\nInputCue30=Item 19 Selected\nInputSigType30=Digital\nInputCue31=Item 20 Selected\nInputSigType31=Digital\nInputCue32=Item 21 Selected\nInputSigType32=Digital\nInputCue33=Item 22 Selected\nInputSigType33=Digital\nInputCue34=Item 23 Selected\nInputSigType34=Digital\nInputCue35=Item 24 Selected\nInputSigType35=Digital\nInputCue36=Item 25 Selected\nInputSigType36=Digital\nInputCue37=Item 26 Selected\nInputSigType37=Digital\nInputCue38=Item 27 Selected\nInputSigType38=Digital\nInputCue39=Item 28 Selected\nInputSigType39=Digital\nInputCue40=Item 29 Selected\nInputSigType40=Digital\nInputCue41=Item 30 Selected\nInputSigType41=Digital\nInputCue42=[~UNUSED2~]\nInputSigType42=Digital|Analog|Serial|String\nInputCue2012=[~EndGroup~]Press\nInputSigType2012=Digital\nInputCue2013=[~BeginGroup~]Enable\nInputSigType2013=Digital\nInputCue2014=Item 1 Enabled\nInputSigType2014=Digital\nInputCue2015=Item 2 Enabled\nInputSigType2015=Digital\nInputCue2016=Item 3 Enabled\nInputSigType2016=Digital\nInputCue2017=Item 4 Enabled\nInputSigType2017=Digital\nInputCue2018=Item 5 Enabled\nInputSigType2018=Digital\nInputCue2019=Item 6 Enabled\nInputSigType2019=Digital\nInputCue2020=Item 7 Enabled\nInputSigType2020=Digital\nInputCue2021=Item 8 Enabled\nInputSigType2021=Digital\nInputCue2022=Item 9 Enabled\nInputSigType2022=Digital\nInputCue2023=Item 10 Enabled\nInputSigType2023=Digital\nInputCue2024=Item 11 Enabled\nInputSigType2024=Digital\nInputCue2025=Item 12 Enabled\nInputSigType2025=Digital\nInputCue2026=Item 13 Enabled\nInputSigType2026=Digital\nInputCue2027=Item 14 Enabled\nInputSigType2027=Digital\nInputCue2028=Item 15 Enabled\nInputSigType2028=Digital\nInputCue2029=Item 16 Enabled\nInputSigType2029=Digital\nInputCue2030=Item 17 Enabled\nInputSigType2030=Digital\nInputCue2031=Item 18 Enabled\nInputSigType2031=Digital\nInputCue2032=Item 19 Enabled\nInputSigType2032=Digital\nInputCue2033=Item 20 Enabled\nInputSigType2033=Digital\nInputCue2034=Item 21 Enabled\nInputSigType2034=Digital\nInputCue2035=Item 22 Enabled\nInputSigType2035=Digital\nInputCue2036=Item 23 Enabled\nInputSigType2036=Digital\nInputCue2037=Item 24 Enabled\nInputSigType2037=Digital\nInputCue2038=Item 25 Enabled\nInputSigType2038=Digital\nInputCue2039=Item 26 Enabled\nInputSigType2039=Digital\nInputCue2040=Item 27 Enabled\nInputSigType2040=Digital\nInputCue2041=Item 28 Enabled\nInputSigType2041=Digital\nInputCue2042=Item 29 Enabled\nInputSigType2042=Digital\nInputCue2043=Item 30 Enabled\nInputSigType2043=Digital\nInputCue2044=[~UNUSED2~]\nInputSigType2044=Digital|Analog|Serial|String\nInputCue4014=[~EndGroup~]Enable\nInputSigType4014=Digital\nInputCue4015=[~BeginGroup~]Visible\nInputSigType4015=Digital\nInputCue4016=Item 1 Visible\nInputSigType4016=Digital\nInputCue4017=Item 2 Visible\nInputSigType4017=Digital\nInputCue4018=Item 3 Visible\nInputSigType4018=Digital\nInputCue4019=Item 4 Visible\nInputSigType4019=Digital\nInputCue4020=Item 5 Visible\nInputSigType4020=Digital\nInputCue4021=Item 6 Visible\nInputSigType4021=Digital\nInputCue4022=Item 7 Visible\nInputSigType4022=Digital\nInputCue4023=Item 8 Visible\nInputSigType4023=Digital\nInputCue4024=Item 9 Visible\nInputSigType4024=Digital\nInputCue4025=Item 10 Visible\nInputSigType4025=Digital\nInputCue4026=Item 11 Visible\nInputSigType4026=Digital\nInputCue4027=Item 12 Visible\nInputSigType4027=Digital\nInputCue4028=Item 13 Visible\nInputSigType4028=Digital\nInputCue4029=Item 14 Visible\nInputSigType4029=Digital\nInputCue4030=Item 15 Visible\nInputSigType4030=Digital\nInputCue4031=Item 16 Visible\nInputSigType4031=Digital\nInputCue4032=Item 17 Visible\nInputSigType4032=Digital\nInputCue4033=Item 18 Visible\nInputSigType4033=Digital\nInputCue4034=Item 19 Visible\nInputSigType4034=Digital\nInputCue4035=Item 20 Visible\nInputSigType4035=Digital\nInputCue4036=Item 21 Visible\nInputSigType4036=Digital\nInputCue4037=Item 22 Visible\nInputSigType4037=Digital\nInputCue4038=Item 23 Visible\nInputSigType4038=Digital\nInputCue4039=Item 24 Visible\nInputSigType4039=Digital\nInputCue4040=Item 25 Visible\nInputSigType4040=Digital\nInputCue4041=Item 26 Visible\nInputSigType4041=Digital\nInputCue4042=Item 27 Visible\nInputSigType4042=Digital\nInputCue4043=Item 28 Visible\nInputSigType4043=Digital\nInputCue4044=Item 29 Visible\nInputSigType4044=Digital\nInputCue4045=Item 30 Visible\nInputSigType4045=Digital\nInputCue4046=[~UNUSED2~]\nInputSigType4046=Digital|Analog|Serial|String\nInputCue6016=[~EndGroup~]Visible\nInputSigType6016=Digital\nOutputCue1=[~UNUSED3~]\nOutputSigType1=Digital\nOutputCue2=Is Moving\nOutputSigType2=Digital\nOutputCue3=[~UNUSED2~]\nOutputSigType3=Digital\nOutputCue4=[~UNUSED2~]\nOutputSigType4=Digital\nOutputCue5=[~UNUSED2~]\nOutputSigType5=Digital\nOutputCue6=[~UNUSED2~]\nOutputSigType6=Digital\nOutputCue7=[~UNUSED2~]\nOutputSigType7=Digital\nOutputCue8=[~UNUSED2~]\nOutputSigType8=Digital\nOutputCue9=[~UNUSED2~]\nOutputSigType9=Digital\nOutputCue10=[~UNUSED2~]\nOutputSigType10=Digital\nOutputCue11=[~BeginGroup~]Press\nOutputSigType11=Digital\nOutputCue12=Item 1 Pressed\nOutputSigType12=Digital\nOutputCue13=Item 2 Pressed\nOutputSigType13=Digital\nOutputCue14=Item 3 Pressed\nOutputSigType14=Digital\nOutputCue15=Item 4 Pressed\nOutputSigType15=Digital\nOutputCue16=Item 5 Pressed\nOutputSigType16=Digital\nOutputCue17=Item 6 Pressed\nOutputSigType17=Digital\nOutputCue18=Item 7 Pressed\nOutputSigType18=Digital\nOutputCue19=Item 8 Pressed\nOutputSigType19=Digital\nOutputCue20=Item 9 Pressed\nOutputSigType20=Digital\nOutputCue21=Item 10 Pressed\nOutputSigType21=Digital\nOutputCue22=Item 11 Pressed\nOutputSigType22=Digital\nOutputCue23=Item 12 Pressed\nOutputSigType23=Digital\nOutputCue24=Item 13 Pressed\nOutputSigType24=Digital\nOutputCue25=Item 14 Pressed\nOutputSigType25=Digital\nOutputCue26=Item 15 Pressed\nOutputSigType26=Digital\nOutputCue27=Item 16 Pressed\nOutputSigType27=Digital\nOutputCue28=Item 17 Pressed\nOutputSigType28=Digital\nOutputCue29=Item 18 Pressed\nOutputSigType29=Digital\nOutputCue30=Item 19 Pressed\nOutputSigType30=Digital\nOutputCue31=Item 20 Pressed\nOutputSigType31=Digital\nOutputCue32=Item 21 Pressed\nOutputSigType32=Digital\nOutputCue33=Item 22 Pressed\nOutputSigType33=Digital\nOutputCue34=Item 23 Pressed\nOutputSigType34=Digital\nOutputCue35=Item 24 Pressed\nOutputSigType35=Digital\nOutputCue36=Item 25 Pressed\nOutputSigType36=Digital\nOutputCue37=Item 26 Pressed\nOutputSigType37=Digital\nOutputCue38=Item 27 Pressed\nOutputSigType38=Digital\nOutputCue39=Item 28 Pressed\nOutputSigType39=Digital\nOutputCue40=Item 29 Pressed\nOutputSigType40=Digital\nOutputCue41=Item 30 Pressed\nOutputSigType41=Digital\nOutputCue42=[~UNUSED2~]\nOutputSigType42=Digital|Analog|Serial|String\nOutputCue2012=[~EndGroup~]Press\nOutputSigType2012=Digital\nOutputCue2013=[~BeginGroup~]Enable\nOutputSigType2013=Digital\nOutputCue2014=[~UNUSED3~]\nOutputSigType2014=Digital\nOutputCue2015=[~UNUSED3~]\nOutputSigType2015=Digital\nOutputCue2016=[~UNUSED3~]\nOutputSigType2016=Digital\nOutputCue2017=[~UNUSED3~]\nOutputSigType2017=Digital\nOutputCue2018=[~UNUSED3~]\nOutputSigType2018=Digital\nOutputCue2019=[~UNUSED3~]\nOutputSigType2019=Digital\nOutputCue2020=[~UNUSED3~]\nOutputSigType2020=Digital\nOutputCue2021=[~UNUSED3~]\nOutputSigType2021=Digital\nOutputCue2022=[~UNUSED3~]\nOutputSigType2022=Digital\nOutputCue2023=[~UNUSED3~]\nOutputSigType2023=Digital\nOutputCue2024=[~UNUSED3~]\nOutputSigType2024=Digital\nOutputCue2025=[~UNUSED3~]\nOutputSigType2025=Digital\nOutputCue2026=[~UNUSED3~]\nOutputSigType2026=Digital\nOutputCue2027=[~UNUSED3~]\nOutputSigType2027=Digital\nOutputCue2028=[~UNUSED3~]\nOutputSigType2028=Digital\nOutputCue2029=[~UNUSED3~]\nOutputSigType2029=Digital\nOutputCue2030=[~UNUSED3~]\nOutputSigType2030=Digital\nOutputCue2031=[~UNUSED3~]\nOutputSigType2031=Digital\nOutputCue2032=[~UNUSED3~]\nOutputSigType2032=Digital\nOutputCue2033=[~UNUSED3~]\nOutputSigType2033=Digital\nOutputCue2034=[~UNUSED3~]\nOutputSigType2034=Digital\nOutputCue2035=[~UNUSED3~]\nOutputSigType2035=Digital\nOutputCue2036=[~UNUSED3~]\nOutputSigType2036=Digital\nOutputCue2037=[~UNUSED3~]\nOutputSigType2037=Digital\nOutputCue2038=[~UNUSED3~]\nOutputSigType2038=Digital\nOutputCue2039=[~UNUSED3~]\nOutputSigType2039=Digital\nOutputCue2040=[~UNUSED3~]\nOutputSigType2040=Digital\nOutputCue2041=[~UNUSED3~]\nOutputSigType2041=Digital\nOutputCue2042=[~UNUSED3~]\nOutputSigType2042=Digital\nOutputCue2043=[~UNUSED3~]\nOutputSigType2043=Digital\nOutputCue2044=[~UNUSED2~]\nOutputSigType2044=Digital|Analog|Serial|String\nOutputCue4014=[~EndGroup~]Enable\nOutputSigType4014=Digital\nOutputCue4015=[~BeginGroup~]Visible\nOutputSigType4015=Digital\nOutputCue4016=[~UNUSED3~]\nOutputSigType4016=Digital\nOutputCue4017=[~UNUSED3~]\nOutputSigType4017=Digital\nOutputCue4018=[~UNUSED3~]\nOutputSigType4018=Digital\nOutputCue4019=[~UNUSED3~]\nOutputSigType4019=Digital\nOutputCue4020=[~UNUSED3~]\nOutputSigType4020=Digital\nOutputCue4021=[~UNUSED3~]\nOutputSigType4021=Digital\nOutputCue4022=[~UNUSED3~]\nOutputSigType4022=Digital\nOutputCue4023=[~UNUSED3~]\nOutputSigType4023=Digital\nOutputCue4024=[~UNUSED3~]\nOutputSigType4024=Digital\nOutputCue4025=[~UNUSED3~]\nOutputSigType4025=Digital\nOutputCue4026=[~UNUSED3~]\nOutputSigType4026=Digital\nOutputCue4027=[~UNUSED3~]\nOutputSigType4027=Digital\nOutputCue4028=[~UNUSED3~]\nOutputSigType4028=Digital\nOutputCue4029=[~UNUSED3~]\nOutputSigType4029=Digital\nOutputCue4030=[~UNUSED3~]\nOutputSigType4030=Digital\nOutputCue4031=[~UNUSED3~]\nOutputSigType4031=Digital\nOutputCue4032=[~UNUSED3~]\nOutputSigType4032=Digital\nOutputCue4033=[~UNUSED3~]\nOutputSigType4033=Digital\nOutputCue4034=[~UNUSED3~]\nOutputSigType4034=Digital\nOutputCue4035=[~UNUSED3~]\nOutputSigType4035=Digital\nOutputCue4036=[~UNUSED3~]\nOutputSigType4036=Digital\nOutputCue4037=[~UNUSED3~]\nOutputSigType4037=Digital\nOutputCue4038=[~UNUSED3~]\nOutputSigType4038=Digital\nOutputCue4039=[~UNUSED3~]\nOutputSigType4039=Digital\nOutputCue4040=[~UNUSED3~]\nOutputSigType4040=Digital\nOutputCue4041=[~UNUSED3~]\nOutputSigType4041=Digital\nOutputCue4042=[~UNUSED3~]\nOutputSigType4042=Digital\nOutputCue4043=[~UNUSED3~]\nOutputSigType4043=Digital\nOutputCue4044=[~UNUSED3~]\nOutputSigType4044=Digital\nOutputCue4045=[~UNUSED3~]\nOutputSigType4045=Digital\nOutputCue4046=[~UNUSED2~]\nOutputSigType4046=Digital|Analog|Serial|String\nOutputCue6016=[~EndGroup~]Visible\nOutputSigType6016=Digital\nInputList2Cue1=[~UNUSED3~]\nInputList2SigType1=Analog\nInputList2Cue2=[~UNUSED3~]\nInputList2SigType2=Analog\nInputList2Cue3=Scroll To Item\nInputList2SigType3=Analog\nInputList2Cue4=Set Number of Items\nInputList2SigType4=Analog\nInputList2Cue5=[~UNUSED2~]\nInputList2SigType5=Analog\nInputList2Cue6=[~UNUSED2~]\nInputList2SigType6=Analog\nInputList2Cue7=[~UNUSED2~]\nInputList2SigType7=Analog\nInputList2Cue8=[~UNUSED2~]\nInputList2SigType8=Analog\nInputList2Cue9=[~UNUSED2~]\nInputList2SigType9=Analog\nInputList2Cue10=[~UNUSED2~]\nInputList2SigType10=Analog\nInputList2Cue11=[~BeginGroup~]Item Icons\nInputList2SigType11=Analog\nInputList2Cue12=Set Item 1 Icon Analog\nInputList2SigType12=Analog\nInputList2Cue13=Set Item 2 Icon Analog\nInputList2SigType13=Analog\nInputList2Cue14=Set Item 3 Icon Analog\nInputList2SigType14=Analog\nInputList2Cue15=Set Item 4 Icon Analog\nInputList2SigType15=Analog\nInputList2Cue16=Set Item 5 Icon Analog\nInputList2SigType16=Analog\nInputList2Cue17=Set Item 6 Icon Analog\nInputList2SigType17=Analog\nInputList2Cue18=Set Item 7 Icon Analog\nInputList2SigType18=Analog\nInputList2Cue19=Set Item 8 Icon Analog\nInputList2SigType19=Analog\nInputList2Cue20=Set Item 9 Icon Analog\nInputList2SigType20=Analog\nInputList2Cue21=Set Item 10 Icon Analog\nInputList2SigType21=Analog\nInputList2Cue22=Set Item 11 Icon Analog\nInputList2SigType22=Analog\nInputList2Cue23=Set Item 12 Icon Analog\nInputList2SigType23=Analog\nInputList2Cue24=Set Item 13 Icon Analog\nInputList2SigType24=Analog\nInputList2Cue25=Set Item 14 Icon Analog\nInputList2SigType25=Analog\nInputList2Cue26=Set Item 15 Icon Analog\nInputList2SigType26=Analog\nInputList2Cue27=Set Item 16 Icon Analog\nInputList2SigType27=Analog\nInputList2Cue28=Set Item 17 Icon Analog\nInputList2SigType28=Analog\nInputList2Cue29=Set Item 18 Icon Analog\nInputList2SigType29=Analog\nInputList2Cue30=Set Item 19 Icon Analog\nInputList2SigType30=Analog\nInputList2Cue31=Set Item 20 Icon Analog\nInputList2SigType31=Analog\nInputList2Cue32=Set Item 21 Icon Analog\nInputList2SigType32=Analog\nInputList2Cue33=Set Item 22 Icon Analog\nInputList2SigType33=Analog\nInputList2Cue34=Set Item 23 Icon Analog\nInputList2SigType34=Analog\nInputList2Cue35=Set Item 24 Icon Analog\nInputList2SigType35=Analog\nInputList2Cue36=Set Item 25 Icon Analog\nInputList2SigType36=Analog\nInputList2Cue37=Set Item 26 Icon Analog\nInputList2SigType37=Analog\nInputList2Cue38=Set Item 27 Icon Analog\nInputList2SigType38=Analog\nInputList2Cue39=Set Item 28 Icon Analog\nInputList2SigType39=Analog\nInputList2Cue40=Set Item 29 Icon Analog\nInputList2SigType40=Analog\nInputList2Cue41=Set Item 30 Icon Analog\nInputList2SigType41=Analog\nInputList2Cue42=[~UNUSED2~]\nInputList2SigType42=Digital|Analog|Serial|String\nInputList2Cue2012=[~EndGroup~]Item Icons\nInputList2SigType2012=Analog\nOutputList2Cue1=Item Clicked\nOutputList2SigType1=Analog\nOutputList2Cue2=Item Held\nOutputList2SigType2=Analog\nOutputList2Cue3=[~UNUSED3~]\nOutputList2SigType3=Analog\nOutputList2Cue4=[~UNUSED3~]\nOutputList2SigType4=Analog\nOutputList2Cue5=[~UNUSED2~]\nOutputList2SigType5=Analog\nOutputList2Cue6=[~UNUSED2~]\nOutputList2SigType6=Analog\nOutputList2Cue7=[~UNUSED2~]\nOutputList2SigType7=Analog\nOutputList2Cue8=[~UNUSED2~]\nOutputList2SigType8=Analog\nOutputList2Cue9=[~UNUSED2~]\nOutputList2SigType9=Analog\nOutputList2Cue10=[~UNUSED2~]\nOutputList2SigType10=Analog\nOutputList2Cue11=[~BeginGroup~]Item Icons\nOutputList2SigType11=Analog\nOutputList2Cue12=[~UNUSED3~]\nOutputList2SigType12=Analog\nOutputList2Cue13=[~UNUSED3~]\nOutputList2SigType13=Analog\nOutputList2Cue14=[~UNUSED3~]\nOutputList2SigType14=Analog\nOutputList2Cue15=[~UNUSED3~]\nOutputList2SigType15=Analog\nOutputList2Cue16=[~UNUSED3~]\nOutputList2SigType16=Analog\nOutputList2Cue17=[~UNUSED3~]\nOutputList2SigType17=Analog\nOutputList2Cue18=[~UNUSED3~]\nOutputList2SigType18=Analog\nOutputList2Cue19=[~UNUSED3~]\nOutputList2SigType19=Analog\nOutputList2Cue20=[~UNUSED3~]\nOutputList2SigType20=Analog\nOutputList2Cue21=[~UNUSED3~]\nOutputList2SigType21=Analog\nOutputList2Cue22=[~UNUSED3~]\nOutputList2SigType22=Analog\nOutputList2Cue23=[~UNUSED3~]\nOutputList2SigType23=Analog\nOutputList2Cue24=[~UNUSED3~]\nOutputList2SigType24=Analog\nOutputList2Cue25=[~UNUSED3~]\nOutputList2SigType25=Analog\nOutputList2Cue26=[~UNUSED3~]\nOutputList2SigType26=Analog\nOutputList2Cue27=[~UNUSED3~]\nOutputList2SigType27=Analog\nOutputList2Cue28=[~UNUSED3~]\nOutputList2SigType28=Analog\nOutputList2Cue29=[~UNUSED3~]\nOutputList2SigType29=Analog\nOutputList2Cue30=[~UNUSED3~]\nOutputList2SigType30=Analog\nOutputList2Cue31=[~UNUSED3~]\nOutputList2SigType31=Analog\nOutputList2Cue32=[~UNUSED3~]\nOutputList2SigType32=Analog\nOutputList2Cue33=[~UNUSED3~]\nOutputList2SigType33=Analog\nOutputList2Cue34=[~UNUSED3~]\nOutputList2SigType34=Analog\nOutputList2Cue35=[~UNUSED3~]\nOutputList2SigType35=Analog\nOutputList2Cue36=[~UNUSED3~]\nOutputList2SigType36=Analog\nOutputList2Cue37=[~UNUSED3~]\nOutputList2SigType37=Analog\nOutputList2Cue38=[~UNUSED3~]\nOutputList2SigType38=Analog\nOutputList2Cue39=[~UNUSED3~]\nOutputList2SigType39=Analog\nOutputList2Cue40=[~UNUSED3~]\nOutputList2SigType40=Analog\nOutputList2Cue41=[~UNUSED3~]\nOutputList2SigType41=Analog\nOutputList2Cue42=[~UNUSED2~]\nOutputList2SigType42=Digital|Analog|Serial|String\nOutputList2Cue2012=[~EndGroup~]Item Icons\nOutputList2SigType2012=Analog\nInputList3Cue1=[~UNUSED2~]\nInputList3SigType1=Serial\nInputList3Cue2=[~UNUSED2~]\nInputList3SigType2=Serial\nInputList3Cue3=[~UNUSED2~]\nInputList3SigType3=Serial\nInputList3Cue4=[~UNUSED2~]\nInputList3SigType4=Serial\nInputList3Cue5=[~UNUSED2~]\nInputList3SigType5=Serial\nInputList3Cue6=[~UNUSED2~]\nInputList3SigType6=Serial\nInputList3Cue7=[~UNUSED2~]\nInputList3SigType7=Serial\nInputList3Cue8=[~UNUSED2~]\nInputList3SigType8=Serial\nInputList3Cue9=[~UNUSED2~]\nInputList3SigType9=Serial\nInputList3Cue10=[~UNUSED2~]\nInputList3SigType10=Serial\nInputList3Cue11=[~BeginGroup~]Item Texts\nInputList3SigType11=Serial\nInputList3Cue12=Set Item 1 Text\nInputList3SigType12=Serial\nInputList3Cue13=Set Item 2 Text\nInputList3SigType13=Serial\nInputList3Cue14=Set Item 3 Text\nInputList3SigType14=Serial\nInputList3Cue15=Set Item 4 Text\nInputList3SigType15=Serial\nInputList3Cue16=Set Item 5 Text\nInputList3SigType16=Serial\nInputList3Cue17=Set Item 6 Text\nInputList3SigType17=Serial\nInputList3Cue18=Set Item 7 Text\nInputList3SigType18=Serial\nInputList3Cue19=Set Item 8 Text\nInputList3SigType19=Serial\nInputList3Cue20=Set Item 9 Text\nInputList3SigType20=Serial\nInputList3Cue21=Set Item 10 Text\nInputList3SigType21=Serial\nInputList3Cue22=Set Item 11 Text\nInputList3SigType22=Serial\nInputList3Cue23=Set Item 12 Text\nInputList3SigType23=Serial\nInputList3Cue24=Set Item 13 Text\nInputList3SigType24=Serial\nInputList3Cue25=Set Item 14 Text\nInputList3SigType25=Serial\nInputList3Cue26=Set Item 15 Text\nInputList3SigType26=Serial\nInputList3Cue27=Set Item 16 Text\nInputList3SigType27=Serial\nInputList3Cue28=Set Item 17 Text\nInputList3SigType28=Serial\nInputList3Cue29=Set Item 18 Text\nInputList3SigType29=Serial\nInputList3Cue30=Set Item 19 Text\nInputList3SigType30=Serial\nInputList3Cue31=Set Item 20 Text\nInputList3SigType31=Serial\nInputList3Cue32=Set Item 21 Text\nInputList3SigType32=Serial\nInputList3Cue33=Set Item 22 Text\nInputList3SigType33=Serial\nInputList3Cue34=Set Item 23 Text\nInputList3SigType34=Serial\nInputList3Cue35=Set Item 24 Text\nInputList3SigType35=Serial\nInputList3Cue36=Set Item 25 Text\nInputList3SigType36=Serial\nInputList3Cue37=Set Item 26 Text\nInputList3SigType37=Serial\nInputList3Cue38=Set Item 27 Text\nInputList3SigType38=Serial\nInputList3Cue39=Set Item 28 Text\nInputList3SigType39=Serial\nInputList3Cue40=Set Item 29 Text\nInputList3SigType40=Serial\nInputList3Cue41=Set Item 30 Text\nInputList3SigType41=Serial\nInputList3Cue42=[~UNUSED2~]\nInputList3SigType42=Digital|Analog|Serial|String\nInputList3Cue2012=[~EndGroup~]Item Texts\nInputList3SigType2012=Serial\nInputList3Cue2013=[~BeginGroup~]Item Icons\nInputList3SigType2013=Serial\nInputList3Cue2014=Set Item 1 Icon Serial\nInputList3SigType2014=Serial\nInputList3Cue2015=Set Item 2 Icon Serial\nInputList3SigType2015=Serial\nInputList3Cue2016=Set Item 3 Icon Serial\nInputList3SigType2016=Serial\nInputList3Cue2017=Set Item 4 Icon Serial\nInputList3SigType2017=Serial\nInputList3Cue2018=Set Item 5 Icon Serial\nInputList3SigType2018=Serial\nInputList3Cue2019=Set Item 6 Icon Serial\nInputList3SigType2019=Serial\nInputList3Cue2020=Set Item 7 Icon Serial\nInputList3SigType2020=Serial\nInputList3Cue2021=Set Item 8 Icon Serial\nInputList3SigType2021=Serial\nInputList3Cue2022=Set Item 9 Icon Serial\nInputList3SigType2022=Serial\nInputList3Cue2023=Set Item 10 Icon Serial\nInputList3SigType2023=Serial\nInputList3Cue2024=Set Item 11 Icon Serial\nInputList3SigType2024=Serial\nInputList3Cue2025=Set Item 12 Icon Serial\nInputList3SigType2025=Serial\nInputList3Cue2026=Set Item 13 Icon Serial\nInputList3SigType2026=Serial\nInputList3Cue2027=Set Item 14 Icon Serial\nInputList3SigType2027=Serial\nInputList3Cue2028=Set Item 15 Icon Serial\nInputList3SigType2028=Serial\nInputList3Cue2029=Set Item 16 Icon Serial\nInputList3SigType2029=Serial\nInputList3Cue2030=Set Item 17 Icon Serial\nInputList3SigType2030=Serial\nInputList3Cue2031=Set Item 18 Icon Serial\nInputList3SigType2031=Serial\nInputList3Cue2032=Set Item 19 Icon Serial\nInputList3SigType2032=Serial\nInputList3Cue2033=Set Item 20 Icon Serial\nInputList3SigType2033=Serial\nInputList3Cue2034=Set Item 21 Icon Serial\nInputList3SigType2034=Serial\nInputList3Cue2035=Set Item 22 Icon Serial\nInputList3SigType2035=Serial\nInputList3Cue2036=Set Item 23 Icon Serial\nInputList3SigType2036=Serial\nInputList3Cue2037=Set Item 24 Icon Serial\nInputList3SigType2037=Serial\nInputList3Cue2038=Set Item 25 Icon Serial\nInputList3SigType2038=Serial\nInputList3Cue2039=Set Item 26 Icon Serial\nInputList3SigType2039=Serial\nInputList3Cue2040=Set Item 27 Icon Serial\nInputList3SigType2040=Serial\nInputList3Cue2041=Set Item 28 Icon Serial\nInputList3SigType2041=Serial\nInputList3Cue2042=Set Item 29 Icon Serial\nInputList3SigType2042=Serial\nInputList3Cue2043=Set Item 30 Icon Serial\nInputList3SigType2043=Serial\nInputList3Cue2044=[~UNUSED2~]\nInputList3SigType2044=Digital|Analog|Serial|String\nInputList3Cue4014=[~EndGroup~]Item Icons\nInputList3SigType4014=Serial\nOutputList3Cue1=[~UNUSED2~]\nOutputList3SigType1=Serial\nOutputList3Cue2=[~UNUSED2~]\nOutputList3SigType2=Serial\nOutputList3Cue3=[~UNUSED2~]\nOutputList3SigType3=Serial\nOutputList3Cue4=[~UNUSED2~]\nOutputList3SigType4=Serial\nOutputList3Cue5=[~UNUSED2~]\nOutputList3SigType5=Serial\nOutputList3Cue6=[~UNUSED2~]\nOutputList3SigType6=Serial\nOutputList3Cue7=[~UNUSED2~]\nOutputList3SigType7=Serial\nOutputList3Cue8=[~UNUSED2~]\nOutputList3SigType8=Serial\nOutputList3Cue9=[~UNUSED2~]\nOutputList3SigType9=Serial\nOutputList3Cue10=[~UNUSED2~]\nOutputList3SigType10=Serial\nOutputList3Cue11=[~BeginGroup~]Item Texts\nOutputList3SigType11=Serial\nOutputList3Cue12=[~UNUSED3~]\nOutputList3SigType12=Serial\nOutputList3Cue13=[~UNUSED3~]\nOutputList3SigType13=Serial\nOutputList3Cue14=[~UNUSED3~]\nOutputList3SigType14=Serial\nOutputList3Cue15=[~UNUSED3~]\nOutputList3SigType15=Serial\nOutputList3Cue16=[~UNUSED3~]\nOutputList3SigType16=Serial\nOutputList3Cue17=[~UNUSED3~]\nOutputList3SigType17=Serial\nOutputList3Cue18=[~UNUSED3~]\nOutputList3SigType18=Serial\nOutputList3Cue19=[~UNUSED3~]\nOutputList3SigType19=Serial\nOutputList3Cue20=[~UNUSED3~]\nOutputList3SigType20=Serial\nOutputList3Cue21=[~UNUSED3~]\nOutputList3SigType21=Serial\nOutputList3Cue22=[~UNUSED3~]\nOutputList3SigType22=Serial\nOutputList3Cue23=[~UNUSED3~]\nOutputList3SigType23=Serial\nOutputList3Cue24=[~UNUSED3~]\nOutputList3SigType24=Serial\nOutputList3Cue25=[~UNUSED3~]\nOutputList3SigType25=Serial\nOutputList3Cue26=[~UNUSED3~]\nOutputList3SigType26=Serial\nOutputList3Cue27=[~UNUSED3~]\nOutputList3SigType27=Serial\nOutputList3Cue28=[~UNUSED3~]\nOutputList3SigType28=Serial\nOutputList3Cue29=[~UNUSED3~]\nOutputList3SigType29=Serial\nOutputList3Cue30=[~UNUSED3~]\nOutputList3SigType30=Serial\nOutputList3Cue31=[~UNUSED3~]\nOutputList3SigType31=Serial\nOutputList3Cue32=[~UNUSED3~]\nOutputList3SigType32=Serial\nOutputList3Cue33=[~UNUSED3~]\nOutputList3SigType33=Serial\nOutputList3Cue34=[~UNUSED3~]\nOutputList3SigType34=Serial\nOutputList3Cue35=[~UNUSED3~]\nOutputList3SigType35=Serial\nOutputList3Cue36=[~UNUSED3~]\nOutputList3SigType36=Serial\nOutputList3Cue37=[~UNUSED3~]\nOutputList3SigType37=Serial\nOutputList3Cue38=[~UNUSED3~]\nOutputList3SigType38=Serial\nOutputList3Cue39=[~UNUSED3~]\nOutputList3SigType39=Serial\nOutputList3Cue40=[~UNUSED3~]\nOutputList3SigType40=Serial\nOutputList3Cue41=[~UNUSED3~]\nOutputList3SigType41=Serial\nOutputList3Cue42=[~UNUSED2~]\nOutputList3SigType42=Digital|Analog|Serial|String\nOutputList3Cue2012=[~EndGroup~]Item Texts\nOutputList3SigType2012=Serial\nOutputList3Cue2013=[~BeginGroup~]Item Icons\nOutputList3SigType2013=Serial\nOutputList3Cue2014=[~UNUSED3~]\nOutputList3SigType2014=Serial\nOutputList3Cue2015=[~UNUSED3~]\nOutputList3SigType2015=Serial\nOutputList3Cue2016=[~UNUSED3~]\nOutputList3SigType2016=Serial\nOutputList3Cue2017=[~UNUSED3~]\nOutputList3SigType2017=Serial\nOutputList3Cue2018=[~UNUSED3~]\nOutputList3SigType2018=Serial\nOutputList3Cue2019=[~UNUSED3~]\nOutputList3SigType2019=Serial\nOutputList3Cue2020=[~UNUSED3~]\nOutputList3SigType2020=Serial\nOutputList3Cue2021=[~UNUSED3~]\nOutputList3SigType2021=Serial\nOutputList3Cue2022=[~UNUSED3~]\nOutputList3SigType2022=Serial\nOutputList3Cue2023=[~UNUSED3~]\nOutputList3SigType2023=Serial\nOutputList3Cue2024=[~UNUSED3~]\nOutputList3SigType2024=Serial\nOutputList3Cue2025=[~UNUSED3~]\nOutputList3SigType2025=Serial\nOutputList3Cue2026=[~UNUSED3~]\nOutputList3SigType2026=Serial\nOutputList3Cue2027=[~UNUSED3~]\nOutputList3SigType2027=Serial\nOutputList3Cue2028=[~UNUSED3~]\nOutputList3SigType2028=Serial\nOutputList3Cue2029=[~UNUSED3~]\nOutputList3SigType2029=Serial\nOutputList3Cue2030=[~UNUSED3~]\nOutputList3SigType2030=Serial\nOutputList3Cue2031=[~UNUSED3~]\nOutputList3SigType2031=Serial\nOutputList3Cue2032=[~UNUSED3~]\nOutputList3SigType2032=Serial\nOutputList3Cue2033=[~UNUSED3~]\nOutputList3SigType2033=Serial\nOutputList3Cue2034=[~UNUSED3~]\nOutputList3SigType2034=Serial\nOutputList3Cue2035=[~UNUSED3~]\nOutputList3SigType2035=Serial\nOutputList3Cue2036=[~UNUSED3~]\nOutputList3SigType2036=Serial\nOutputList3Cue2037=[~UNUSED3~]\nOutputList3SigType2037=Serial\nOutputList3Cue2038=[~UNUSED3~]\nOutputList3SigType2038=Serial\nOutputList3Cue2039=[~UNUSED3~]\nOutputList3SigType2039=Serial\nOutputList3Cue2040=[~UNUSED3~]\nOutputList3SigType2040=Serial\nOutputList3Cue2041=[~UNUSED3~]\nOutputList3SigType2041=Serial\nOutputList3Cue2042=[~UNUSED3~]\nOutputList3SigType2042=Serial\nOutputList3Cue2043=[~UNUSED3~]\nOutputList3SigType2043=Serial\nOutputList3Cue2044=[~UNUSED2~]\nOutputList3SigType2044=Digital|Analog|Serial|String\nOutputList3Cue4014=[~EndGroup~]Item Icons\nOutputList3SigType4014=Serial\nParamCue1=SmartObjectId\nParamSigType1=Constant\nMPp=1\nPp1=19\nCedH=19\nSmartObjId=1203d\n]\n; Parameter Properties for Smart Object ID\n[\nObjTp=Dp\nH=19\nTp=1\nHD=TRUE\nDV=1203d\nNF=1\nDNF=1\nEncFmt=0\nDVLF=1\nSgn=0\n]\n; Smart Objects Definition section\n[\nObjTp=CED\nH=19\nName=PepperDash Essentials iPad_[D.VC] Recents_Dynamic Button List Vertical_2.ced\n;Cedver is the version of the Smart Graphics control, not the CED file format.\n;If the control definition changes, increment this.\nCedVer=1\n]\n"
  },
  {
    "path": "src/PepperDash.Essentials/app.config",
    "content": "﻿<?xml version=\"1.0\"?>\n<configuration>\n\t<runtime>\n\t\t<assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n\t\t\t<dependentAssembly>\n\t\t\t\t<assemblyIdentity name=\"SimplSharpPro\" publicKeyToken=\"1099C178B3B54C3B\" culture=\"neutral\"/>\n\t\t\t\t<bindingRedirect oldVersion=\"0.0.0.0-1.5.2.1\" newVersion=\"1.5.2.1\"/>\n\t\t\t</dependentAssembly>\n\t\t</assemblyBinding>\n\t</runtime>\n</configuration>\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Crestron.SimplSharpPro.EthernetCommunication;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Base class for bridge API variants\n    /// </summary>\n    [Obsolete(\"Will be removed in v3.0.0\")]\n    public abstract class BridgeApi : EssentialsDevice\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">Device key</param>\n        protected BridgeApi(string key) :\n            base(key)\n        {\n\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"name\"></param>\n        protected BridgeApi(string key, string name) :\n            base(key, name)\n        {\n\n        }\n    }\n\n    /// <summary>\n    /// Class to link devices and rooms to an EISC Instance\n    /// </summary>\n    public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor\n    {\n        /// <summary>\n        /// Gets the PropertiesConfig\n        /// </summary>\n        public EiscApiPropertiesConfig PropertiesConfig { get; private set; }\n\n        /// <summary>\n        /// Gets the JoinMaps dictionary\n        /// </summary>\n        public Dictionary<string, JoinMapBaseAdvanced> JoinMaps { get; private set; }\n\n        /// <summary>\n        /// Gets the EISC instance\n        /// </summary>\n        public BasicTriList Eisc { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"dc\">Device configuration</param>\n        /// <param name=\"eisc\">EISC instance</param>\n        public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) :\n            base(dc.Key, dc.Name)\n        {\n            JoinMaps = new Dictionary<string, JoinMapBaseAdvanced>();\n\n            PropertiesConfig = dc.Properties.ToObject<EiscApiPropertiesConfig>();\n\n            Eisc = eisc;\n\n            Eisc.SigChange += Eisc_SigChange;\n\n            CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, Eisc, 120000, 300000);\n\n            AddPostActivationAction(LinkDevices);\n            AddPostActivationAction(LinkRooms);\n            AddPostActivationAction(RegisterEisc);\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>        \n        public override bool CustomActivate()\n        {\n            CommunicationMonitor.Start();\n            return base.CustomActivate();\n        }\n\n        /// <summary>\n        /// Deactivate method\n        /// </summary>\n        public override bool Deactivate()\n        {\n            CommunicationMonitor.Stop();\n            return base.Deactivate();\n        }\n\n        private void LinkDevices()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Linking Devices...\");\n\n            if (PropertiesConfig.Devices == null)\n            {\n                this.LogDebug(\"No devices linked to this bridge\");\n                return;\n            }\n\n            foreach (var d in PropertiesConfig.Devices)\n            {\n                var device = DeviceManager.GetDeviceForKey(d.DeviceKey);\n\n                if (device == null)\n                {\n                    continue;\n                }\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Linking Device: '{0}'\", device.Key);\n\n                if (device is IBridgeAdvanced bridge)\n                {\n                    bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);\n                    continue;\n                }\n\n                this.LogWarning(\"{deviceKey} is not compatible with this bridge type. Please update the device.\", device.Key);\n            }\n        }\n\n        private void RegisterEisc()\n        {\n            if (Eisc.Registered)\n            {\n                return;\n            }\n\n            var registerResult = Eisc.Register();\n\n            if (registerResult != eDeviceRegistrationUnRegistrationResponse.Success)\n            {\n                this.LogVerbose(\"Registration result: {registerResult}\", registerResult);\n                return;\n            }\n\n            this.LogDebug(\"EISC registration successful\");\n        }\n\n        /// <summary>\n        /// Link rooms to this EISC. Rooms MUST implement IBridgeAdvanced\n        /// </summary>\n        public void LinkRooms()\n        {\n            this.LogDebug(\"Linking Rooms...\");\n\n            if (PropertiesConfig.Rooms == null)\n            {\n                this.LogDebug(\"No rooms linked to this bridge.\");\n                return;\n            }\n\n            foreach (var room in PropertiesConfig.Rooms)\n            {\n                if (!(DeviceManager.GetDeviceForKey(room.RoomKey) is IBridgeAdvanced rm))\n                {\n                    this.LogDebug(\"Room {roomKey} does not implement IBridgeAdvanced. Skipping...\", room.RoomKey);\n                    continue;\n                }\n\n                rm.LinkToApi(Eisc, room.JoinStart, room.JoinMapKey, this);\n            }\n        }\n\n        /// <summary>\n        /// Adds a join map\n        /// </summary>\n        /// <param name=\"deviceKey\">The key of the device to add the join map for</param>\n        /// <param name=\"joinMap\">The join map to add</param>        \n        public void AddJoinMap(string deviceKey, JoinMapBaseAdvanced joinMap)\n        {\n            if (!JoinMaps.ContainsKey(deviceKey))\n            {\n                JoinMaps.Add(deviceKey, joinMap);\n            }\n            else\n            {\n                this.LogWarning(\"Unable to add join map with key '{deviceKey}'.  Key already exists in JoinMaps dictionary\", deviceKey);\n            }\n        }\n\n        /// <summary>\n        /// PrintJoinMaps method\n        /// </summary>        \n        public virtual void PrintJoinMaps()\n        {\n            CrestronConsole.ConsoleCommandResponse(\"Join Maps for EISC IPID: {0}\\r\\n\", Eisc.ID.ToString(\"X\"));\n\n            foreach (var joinMap in JoinMaps)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Join map for device '{0}':\", joinMap.Key);\n                joinMap.Value.PrintJoinMapInfo();\n            }\n        }\n\n        /// <summary>\n        /// MarkdownForBridge method\n        /// </summary>        \n        public virtual void MarkdownForBridge(string bridgeKey)\n        {\n            this.LogInformation(\"Writing Joinmaps to files for EISC IPID: {eiscId}\", Eisc.ID.ToString(\"X\"));\n\n            foreach (var joinMap in JoinMaps)\n            {\n                this.LogInformation(\"Generating markdown for device '{deviceKey}':\", joinMap.Key);\n                joinMap.Value.MarkdownJoinMapInfo(joinMap.Key, bridgeKey);\n            }\n        }\n\n        /// <summary>\n        /// Prints the join map for a device by key\n        /// </summary>\n        /// <param name=\"deviceKey\">The key of the device to print the join map for</param>        \n        public void PrintJoinMapForDevice(string deviceKey)\n        {\n            var joinMap = JoinMaps[deviceKey];\n\n            if (joinMap == null)\n            {\n                this.LogInformation(\"Unable to find joinMap for device with key: '{deviceKey}'\", deviceKey);\n                return;\n            }\n\n            this.LogInformation(\"Join map for device '{deviceKey}' on EISC '{eiscKey}':\", deviceKey, Key);\n            joinMap.PrintJoinMapInfo();\n        }\n        /// <summary>\n        /// Prints the join map for a device by key in Markdown format\n        /// </summary>\n        /// <param name=\"deviceKey\">The key of the device to print the join map for</param>\n        /// <param name=\"bridgeKey\">The key of the bridge to use for the Markdown output</param>        \n        public void MarkdownJoinMapForDevice(string deviceKey, string bridgeKey)\n        {\n            var joinMap = JoinMaps[deviceKey];\n\n            if (joinMap == null)\n            {\n                this.LogInformation(\"Unable to find joinMap for device with key: '{deviceKey}'\", deviceKey);\n                return;\n            }\n\n            this.LogInformation(\"Join map for device '{deviceKey}' on EISC '{eiscKey}':\", deviceKey, Key);\n            joinMap.MarkdownJoinMapInfo(deviceKey, bridgeKey);\n        }\n\n        /// <summary>\n        /// Used for debugging to trigger an action based on a join number and type\n        /// </summary>\n        /// <param name=\"join\">The join number to execute the action for</param>\n        /// <param name=\"type\">The type of join (digital, analog, serial)</param>\n        /// <param name=\"state\">The state to pass to the action</param>        \n        public void ExecuteJoinAction(uint join, string type, object state)\n        {\n            try\n            {\n                switch (type.ToLower())\n                {\n                    case \"digital\":\n                        {\n                            if (Eisc.BooleanOutput[join].UserObject is Action<bool> userObject)\n                            {\n                                this.LogVerbose(\"Executing Boolean Action\");\n                                userObject(Convert.ToBoolean(state));\n                            }\n                            else\n                                this.LogVerbose(\"User Object is null.  Nothing to Execute\");\n                            break;\n                        }\n                    case \"analog\":\n                        {\n                            if (Eisc.UShortOutput[join].UserObject is Action<ushort> userObject)\n                            {\n                                this.LogVerbose(\"Executing Analog Action\");\n                                userObject(Convert.ToUInt16(state));\n                            }\n                            else\n                                this.LogVerbose(\"User Object is null.  Nothing to Execute\");\n                            break;\n                        }\n                    case \"serial\":\n                        {\n                            if (Eisc.StringOutput[join].UserObject is Action<string> userObject)\n                            {\n                                this.LogVerbose(\"Executing Serial Action\");\n                                userObject(Convert.ToString(state));\n                            }\n                            else\n                                this.LogVerbose(\"User Object is null.  Nothing to Execute\");\n                            break;\n                        }\n                    default:\n                        {\n                            this.LogVerbose(\"Unknown join type.  Use digital/serial/analog\");\n                            break;\n                        }\n                }\n            }\n            catch (Exception e)\n            {\n                this.LogError(\"ExecuteJoinAction error: {message}\", e.Message);\n                this.LogDebug(e, \"Stack Trace: \");\n            }\n\n        }\n\n        /// <summary>\n        /// Handle incoming sig changes\n        /// </summary>\n        /// <param name=\"currentDevice\">BasicTriList device that triggered the event</param>\n        /// <param name=\"args\">Event arguments containing the signal information</param>\n        protected void Eisc_SigChange(object currentDevice, SigEventArgs args)\n        {\n            try\n            {\n                this.LogVerbose(\"EiscApiAdvanced change: {type} {number}={value}\", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);\n                var userObject = args.Sig.UserObject;\n\n                if (userObject == null) return;\n\n\n                if (userObject is Action<bool>)\n                {\n                    this.LogDebug(\"Executing Boolean Action\");\n                    (userObject as Action<bool>)(args.Sig.BoolValue);\n                }\n                else if (userObject is Action<ushort>)\n                {\n                    this.LogDebug(\"Executing Analog Action\");\n                    (userObject as Action<ushort>)(args.Sig.UShortValue);\n                }\n                else if (userObject is Action<string>)\n                {\n                    this.LogDebug(\"Executing Serial Action\");\n                    (userObject as Action<string>)(args.Sig.StringValue);\n                }\n            }\n            catch (Exception e)\n            {\n                this.LogError(\"Eisc_SigChange handler error: {message}\", e.Message);\n                this.LogDebug(e, \"Stack Trace: \");\n            }\n        }\n\n        #region Implementation of ICommunicationMonitor\n\n        /// <summary>\n        /// Gets or sets the CommunicationMonitor\n        /// </summary>\n        public StatusMonitorBase CommunicationMonitor { get; private set; }\n\n        #endregion\n    }\n\n    /// <summary>\n    /// Represents a EiscApiPropertiesConfig\n    /// </summary>\n    public class EiscApiPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Control\n        /// </summary>\n        [JsonProperty(\"control\")]\n        public EssentialsControlPropertiesConfig Control { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Devices\n        /// </summary>\n        [JsonProperty(\"devices\")]\n        public List<ApiDevicePropertiesConfig> Devices { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Rooms\n        /// </summary>\n        [JsonProperty(\"rooms\")]\n        public List<ApiRoomPropertiesConfig> Rooms { get; set; }\n\n\n        /// <summary>\n        /// Represents a ApiDevicePropertiesConfig\n        /// </summary>\n        public class ApiDevicePropertiesConfig\n        {\n            /// <summary>\n            /// Gets or sets the DeviceKey\n            /// </summary>\n            [JsonProperty(\"deviceKey\")]\n            public string DeviceKey { get; set; }\n\n            /// <summary>\n            /// Gets or sets the JoinStart\n            /// </summary>\n            [JsonProperty(\"joinStart\")]\n            public uint JoinStart { get; set; }\n\n            /// <summary>\n            /// Gets or sets the JoinMapKey\n            /// </summary>\n            [JsonProperty(\"joinMapKey\")]\n            public string JoinMapKey { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a ApiRoomPropertiesConfig\n        /// </summary>\n        public class ApiRoomPropertiesConfig\n        {\n            /// <summary>\n            /// Gets or sets the RoomKey\n            /// </summary>\n            [JsonProperty(\"roomKey\")]\n            public string RoomKey { get; set; }\n\n            /// <summary>\n            /// Gets or sets the JoinStart\n            /// </summary>\n            [JsonProperty(\"joinStart\")]\n            public uint JoinStart { get; set; }\n\n            /// <summary>\n            /// Gets or sets the JoinMapKey\n            /// </summary>\n            [JsonProperty(\"joinMapKey\")]\n            public string JoinMapKey { get; set; }\n        }\n\n    }\n\n    /// <summary>\n    /// Factory class for EiscApiAdvanced devices\n    /// </summary>\n    /// <remarks>\n    /// Supported types:\n    /// eiscapiadv - Create a standard EISC client over TCP/IP\n    /// eiscapiadvanced - Create a standard EISC client over TCP/IP\n    /// eiscapiadvancedserver - Create an EISC server\n    /// eiscapiadvancedclient - Create an EISC client\n    /// vceiscapiadv - Create a VC-4 EISC client\n    /// vceiscapiadvanced - Create a VC-4 EISC client\n    /// eiscapiadvudp - Create a standard EISC client over UDP\n    /// eiscapiadvancedudp - Create a standard EISC client over UDP\n    /// </remarks>\n    public class EiscApiAdvancedFactory : EssentialsDeviceFactory<EiscApiAdvanced>\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public EiscApiAdvancedFactory()\n        {\n            TypeNames = new List<string> { \"eiscapiadv\", \"eiscapiadvanced\", \"eiscapiadvancedserver\", \"eiscapiadvancedclient\", \"vceiscapiadv\", \"vceiscapiadvanced\", \"eiscapiadvudp\", \"eiscapiadvancedudp\" };\n        }\n\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogDebug(\"Attempting to create new EiscApiAdvanced Device\");\n\n            var controlProperties = CommFactory.GetControlPropertiesConfig(dc);\n\n            BasicTriList eisc;\n\n            switch (dc.Type.ToLower())\n            {\n                case \"eiscapiadvudp\":\n                case \"eiscapiadvancedudp\":\n                    {\n                        eisc = new EthernetIntersystemCommunications(controlProperties.IpIdInt,\n                            controlProperties.TcpSshProperties.Address, Global.ControlSystem);\n                        break;\n                    }\n                case \"eiscapiadv\":\n                case \"eiscapiadvanced\":\n                    {\n                        eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(controlProperties.IpIdInt,\n                            controlProperties.TcpSshProperties.Address, Global.ControlSystem);\n                        break;\n                    }\n                case \"eiscapiadvancedserver\":\n                    {\n                        eisc = new EISCServer(controlProperties.IpIdInt, Global.ControlSystem);\n                        break;\n                    }\n                case \"eiscapiadvancedclient\":\n                    {\n                        eisc = new EISCClient(controlProperties.IpIdInt, controlProperties.TcpSshProperties.Address, Global.ControlSystem);\n                        break;\n                    }\n                case \"vceiscapiadv\":\n                case \"vceiscapiadvanced\":\n                    {\n                        if (string.IsNullOrEmpty(controlProperties.RoomId))\n                        {\n                            Debug.LogInformation(\"Unable to build VC-4 EISC Client for device {deviceKey}. Room ID is missing or empty\", dc.Key);\n                            eisc = null;\n                            break;\n                        }\n                        eisc = new VirtualControlEISCClient(controlProperties.IpIdInt, controlProperties.RoomId,\n                            Global.ControlSystem);\n                        break;\n                    }\n                default:\n                    eisc = null;\n                    break;\n            }\n\n            if (eisc == null)\n            {\n                return null;\n            }\n\n            return new EiscApiAdvanced(dc, eisc);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/BridgeHelper.cs",
    "content": "﻿using PepperDash.Core;\nusing Serilog.Events;\n\n//using PepperDash.Essentials.Devices.Common.Cameras;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Helper methods for bridges\n    /// </summary>\n    public static class BridgeHelper\n    {\n        /// <summary>\n        /// PrintJoinMp method\n        /// </summary>\n        /// <param name=\"command\">target bridgekey to print join map for</param>\n        public static void PrintJoinMap(string command)\n        {\n            var targets = command.Split(' ');\n\n            var bridgeKey = targets[0].Trim();\n\n            if (!(DeviceManager.GetDeviceForKey(bridgeKey) is EiscApiAdvanced bridge))\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Unable to find advanced bridge with key: '{0}'\", bridgeKey);\n                return;\n            }\n\n            if (targets.Length > 1)\n            {\n                var deviceKey = targets[1].Trim();\n\n                if (string.IsNullOrEmpty(deviceKey)) return;\n                bridge.PrintJoinMapForDevice(deviceKey);\n            }\n            else\n            {\n                bridge.PrintJoinMaps();\n            }\n        }\n        /// <summary>\n        /// JoinmapMarkdown method\n        /// </summary>\n        public static void JoinmapMarkdown(string command)\n        {\n            var targets = command.Split(' ');\n\n            var bridgeKey = targets[0].Trim();\n\n            var bridge = DeviceManager.GetDeviceForKey(bridgeKey) as EiscApiAdvanced;\n\n            if (bridge == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Unable to find advanced bridge with key: '{0}'\", bridgeKey);\n                return;\n            }\n\n            if (targets.Length > 1)\n            {\n                var deviceKey = targets[1].Trim();\n\n                if (string.IsNullOrEmpty(deviceKey)) return;\n                bridge.MarkdownJoinMapForDevice(deviceKey, bridgeKey);\n            }\n            else\n            {\n                bridge.MarkdownForBridge(bridgeKey);\n\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/IBridge.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Defines the contract for IBridgeAdvanced\n    /// </summary>\n    public interface IBridgeAdvanced\n    {\n        /// <summary>\n        /// Links the bridge to the API using the provided trilist, join start, join map key, and bridge.\n        /// </summary>\n        /// <param name=\"trilist\">The trilist to link to.</param>\n        /// <param name=\"joinStart\">The starting join number.</param>\n        /// <param name=\"joinMapKey\">The key for the join map.</param>\n        /// <param name=\"bridge\">The EISC API bridge.</param>\n        void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/AirMediaControllerJoinMap.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a AirMediaControllerJoinMap\n    /// </summary>\n    public class AirMediaControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Air Media Online status\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Air Media In Sharing Session status\n        /// </summary>\n        [JoinName(\"IsInSession\")]\n        public JoinDataComplete IsInSession = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media In Sharing Session\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Air Media Has HDMI Video Sync status\n        /// </summary>\n        [JoinName(\"HdmiVideoSync\")]\n        public JoinDataComplete HdmiVideoSync = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Has HDMI Video Sync\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Air Media Automatic Input Routing Enable(d)\n        /// </summary>\n        [JoinName(\"AutomaticInputRoutingEnabled\")]\n        public JoinDataComplete AutomaticInputRoutingEnabled = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Automatic Input Routing Enable(d)\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Air Media Video Route Select / Feedback\n        /// </summary>\n        [JoinName(\"VideoOut\")]\n        public JoinDataComplete VideoOut = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Video Route Select / Feedback\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Air Media Error Status Feedback\n        /// </summary>\n        [JoinName(\"ErrorFB\")]\n        public JoinDataComplete ErrorFB = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Error Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Air Media Number of Users Connected Feedback\n        /// </summary>\n        [JoinName(\"NumberOfUsersConnectedFB\")]\n        public JoinDataComplete NumberOfUsersConnectedFB = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Number of Users Connected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Air Media Login Code Set / Get\n        /// </summary>\n        [JoinName(\"LoginCode\")]\n        public JoinDataComplete LoginCode = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Login Code Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Air Media Device Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Device Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Air Media IP Address Feedback\n        /// </summary>\n        [JoinName(\"ConnectionAddressFB\")]\n        public JoinDataComplete ConnectionAddressFB = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media IP Address\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Air Media Hostname Feedback\n        /// </summary>\n        [JoinName(\"HostnameFB\")]\n        public JoinDataComplete HostnameFB = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Hostname\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Air Media Serial Number Feedback\n        /// </summary>\n        [JoinName(\"SerialNumberFeedback\")]\n        public JoinDataComplete SerialNumberFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Air Media Serial Number\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public AirMediaControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(AirMediaControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected AirMediaControllerJoinMap(uint joinStart, Type type) : base(joinStart, type){}\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/AppleTvJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a AppleTvJoinMap\n    /// </summary>\n    public class AppleTvJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// AppleTv Nav Up\n        /// </summary>\n        [JoinName(\"UpArrow\")]\n        public JoinDataComplete UpArrow = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"AppleTv Nav Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// AppleTv Nav Down\n        /// </summary>\n        [JoinName(\"DnArrow\")]\n        public JoinDataComplete DnArrow = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"AppleTv Nav Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// AppleTv Nav Left\n        /// </summary>\n        [JoinName(\"LeftArrow\")]\n        public JoinDataComplete LeftArrow = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"AppleTv Nav Left\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// AppleTv Nav Right\n        /// </summary>\n        [JoinName(\"RightArrow\")]\n        public JoinDataComplete RightArrow = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"AppleTv Nav Right\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// AppleTv Menu\n        /// </summary>\n        [JoinName(\"Menu\")]\n        public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"AppleTv Menu\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// AppleTv Select\n        /// </summary>\n        [JoinName(\"Select\")]\n        public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"AppleTv Select\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// AppleTv Play/Pause\n        /// </summary>\n        [JoinName(\"PlayPause\")]\n        public JoinDataComplete PlayPause = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"AppleTv Play/Pause\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public AppleTvJoinMap(uint joinStart)\n            : base(joinStart, typeof(AppleTvJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        public AppleTvJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/C2nRthsControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a C2nRthsControllerJoinMap\n    /// </summary>\n    public class C2nRthsControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// C2nRthsController Online status\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Temp Sensor Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Temperature Format (C/F)\n        /// </summary>\n        [JoinName(\"TemperatureFormat\")]\n        public JoinDataComplete TemperatureFormat = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Temp Sensor Unit Format\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Temperature Sensor Feedbacks\n        /// </summary>\n        [JoinName(\"Temperature\")]\n        public JoinDataComplete Temperature = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Temp Sensor Temperature Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Humidity Sensor Feedbacks\n        /// </summary>\n        [JoinName(\"Humidity\")]\n        public JoinDataComplete Humidity = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Temp Sensor Humidity Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Temp Sensor Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Temp Sensor Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public C2nRthsControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(C2nRthsControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected C2nRthsControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/CameraControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a CameraControllerJoinMap\n    /// </summary>\n    public class CameraControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Tilt Up\n        /// </summary>\n        [JoinName(\"TiltUp\")]\n        public JoinDataComplete TiltUp = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, new JoinMetadata { Description = \"Tilt Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Tilt Down\n        /// </summary>\n        [JoinName(\"TiltDown\")]\n        public JoinDataComplete TiltDown = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, new JoinMetadata { Description = \"Tilt Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Pan Left\n        /// </summary>\n        [JoinName(\"PanLeft\")]\n        public JoinDataComplete PanLeft = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 }, new JoinMetadata { Description = \"Pan Left\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Pan Right\n        /// </summary>\n        [JoinName(\"PanRight\")]\n        public JoinDataComplete PanRight = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 }, new JoinMetadata { Description = \"Pan Right\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Zoom In\n        /// </summary>\n        [JoinName(\"ZoomIn\")]\n        public JoinDataComplete ZoomIn = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 }, new JoinMetadata { Description = \"Zoom In\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Zoom Out\n        /// </summary>\n        [JoinName(\"ZoomOut\")]\n        public JoinDataComplete ZoomOut = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 }, new JoinMetadata { Description = \"Zoom Out\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Is Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 }, new JoinMetadata { Description = \"Is Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Power On\n        /// </summary>\n        [JoinName(\"PowerOn\")]\n        public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 }, new JoinMetadata { Description = \"Power On\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Power Off\n        /// </summary>\n        [JoinName(\"PowerOff\")]\n        public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 }, new JoinMetadata { Description = \"Power Off\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Number Of Presets\n        /// </summary>\n        [JoinName(\"NumberOfPresets\")]\n        public JoinDataComplete NumberOfPresets = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 }, new JoinMetadata { Description = \"Tells Essentials the number of defined presets\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Preset Recall Start\n        /// </summary>\n        [JoinName(\"PresetRecallStart\")]\n        public JoinDataComplete PresetRecallStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 20 }, new JoinMetadata { Description = \"Preset Recall Start\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Preset Label Start\n        /// </summary>\n        [JoinName(\"PresetLabelStart\")]\n        public JoinDataComplete PresetLabelStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 20 }, new JoinMetadata { Description = \"Preset Label Start\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// Preset Save Start\n        /// </summary>\n        [JoinName(\"PresetSaveStart\")]\n        public JoinDataComplete PresetSaveStart = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 20 }, new JoinMetadata { Description = \"Preset Save Start\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Camera Mode Auto\n        /// </summary>\n        [JoinName(\"CameraModeAuto\")]\n        public JoinDataComplete CameraModeAuto = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 }, new JoinMetadata { Description = \"Camera Mode Auto\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Camera Mode Manual\n        /// </summary>\n        [JoinName(\"CameraModeManual\")]\n        public JoinDataComplete CameraModeManual = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 }, new JoinMetadata { Description = \"Camera Mode Manual\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Camera Mode Off\n        /// </summary>\n        [JoinName(\"CameraModeOff\")]\n        public JoinDataComplete CameraModeOff = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 }, new JoinMetadata { Description = \"Camera Mode Off\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Supports Camera Mode Manual\n        /// </summary>\n        [JoinName(\"SupportsCameraModeAuto\")]\n        public JoinDataComplete SupportsCameraModeAuto = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1 }, new JoinMetadata { Description = \"Supports Camera Mode Auto\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Supports Camera Mode Off\n        /// </summary>\n        [JoinName(\"SupportsCameraModeOff\")]\n        public JoinDataComplete SupportsCameraModeOff = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1 }, new JoinMetadata { Description = \"Supports Camera Mode Off\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Supports Presets\n        /// </summary>\n        [JoinName(\"SupportsPresets\")]\n        public JoinDataComplete SupportsPresets = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1 }, new JoinMetadata { Description = \"Supports Presets\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public CameraControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(CameraControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected CameraControllerJoinMap(uint joinStart, Type type) : base(joinStart, type){}\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a CenOdtOccupancySensorBaseJoinMap\n    /// </summary>\n    public class CenOdtOccupancySensorBaseJoinMap : JoinMapBaseAdvanced\n    {\n        #region Digitals\n\n        /// <summary>\n        /// Online\n        /// </summary>\n        [JoinName(\"Online\")]\n        public JoinDataComplete Online = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Force Occupied\n        /// </summary>\n        [JoinName(\"ForceOccupied\")]\n        public JoinDataComplete ForceOccupied = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Force Occupied\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Force Vacant\n        /// </summary>\n        [JoinName(\"ForceVacant\")]\n        public JoinDataComplete ForceVacant = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Force Vacant\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Enable Raw States\n        /// </summary>\n        [JoinName(\"EnableRawStates\")]\n        public JoinDataComplete EnableRawStates = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enable Raw States\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Disable Raw States\n        /// </summary>\n        [JoinName(\"RoomOccupiedFeedback\")]\n        public JoinDataComplete RoomOccupiedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Room Occupied Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Grace Occupancy Detected Feedback\n        /// </summary>\n        [JoinName(\"GraceOccupancyDetectedFeedback\")]\n        public JoinDataComplete GraceOccupancyDetectedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Grace Occupancy Detected Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Room Vacant Feedback\n        /// </summary>\n        [JoinName(\"RoomVacantFeedback\")]\n        public JoinDataComplete RoomVacantFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Room Vacant Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Raw Occupancy Feedback\n        /// </summary>\n        [JoinName(\"RawOccupancyFeedback\")]\n        public JoinDataComplete RawOccupancyFeedback = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Raw Occupancy Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Raw Occupancy Pir Feedback\n        /// </summary>\n        [JoinName(\"RawOccupancyPirFeedback\")]\n        public JoinDataComplete RawOccupancyPirFeedback = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Raw Occupancy Pir Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Raw Occupancy Us Feedback\n        /// </summary>\n        [JoinName(\"RawOccupancyUsFeedback\")]\n        public JoinDataComplete RawOccupancyUsFeedback = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Raw Occupancy Us Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Identity Mode On\n        /// </summary>\n        [JoinName(\"IdentityModeOn\")]\n        public JoinDataComplete IdentityMode = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enable Identity Mode\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Identity Mode Off\n        /// </summary>\n        [JoinName(\"IdentityModeFeedback\")]\n        public JoinDataComplete IdentityModeFeedback = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Identity Mode Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Enable Led Flash\n        /// </summary>\n        [JoinName(\"EnableLedFlash\")]\n        public JoinDataComplete EnableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enable Led Flash\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Disable Led Flash\n        /// </summary>\n        [JoinName(\"DisableLedFlash\")]\n        public JoinDataComplete DisableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Disable Led Flash\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Enable Short Timeout\n        /// </summary>\n        [JoinName(\"EnableShortTimeout\")]\n        public JoinDataComplete EnableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enable Short Timeout\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Disable Short Timeout\n        /// </summary>\n        [JoinName(\"DisableShortTimeout\")]\n        public JoinDataComplete DisableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Disable Short Timeout\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Or When Vacated\n        /// </summary>\n        [JoinName(\"OrWhenVacated\")]\n        public JoinDataComplete OrWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Or When Vacated\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// And When Vacated\n        /// </summary>\n        [JoinName(\"AndWhenVacated\")]\n        public JoinDataComplete AndWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },\n            new JoinMetadata { Description = \"AndWhenVacated\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Enable Us A\n        /// </summary>\n        [JoinName(\"EnableUsA\")]\n        public JoinDataComplete EnableUsA = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enable Us A\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Disable Us A\n        /// </summary>\n        [JoinName(\"DisableUsA\")]\n        public JoinDataComplete DisableUsA = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Disable Us A\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Enable Us B\n        /// </summary>\n        [JoinName(\"EnableUsB\")]\n        public JoinDataComplete EnableUsB = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enable Us B\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Disable Us B\n        /// </summary>\n        [JoinName(\"DisableUsB\")]\n        public JoinDataComplete DisableUsB = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Disable Us B\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Enable Pir\n        /// </summary>\n        [JoinName(\"EnablePir\")]\n        public JoinDataComplete EnablePir = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enable Pir\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Disable Pir\n        /// </summary>\n        [JoinName(\"DisablePir\")]\n        public JoinDataComplete DisablePir = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Disable Pir\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Increment Us In Occupied State\n        /// </summary>\n        [JoinName(\"IncrementUsInOccupiedState\")]\n        public JoinDataComplete IncrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Increment Us In OccupiedState\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Decrement Us In Occupied State\n        /// </summary>\n        [JoinName(\"DecrementUsInOccupiedState\")]\n        public JoinDataComplete DecrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Dencrement Us In Occupied State\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Increment Us In Vacant State\n        /// </summary>\n        [JoinName(\"IncrementUsInVacantState\")]\n        public JoinDataComplete IncrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Increment Us In VacantState\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Decrement Us In Vacant State\n        /// </summary>\n        [JoinName(\"DecrementUsInVacantState\")]\n        public JoinDataComplete DecrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Decrement Us In VacantState\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Increment Pir In Occupied State\n        /// </summary>\n        [JoinName(\"IncrementPirInOccupiedState\")]\n        public JoinDataComplete IncrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Increment Pir In Occupied State\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Decrement Pir In Occupied State\n        /// </summary>\n        [JoinName(\"DecrementPirInOccupiedState\")]\n        public JoinDataComplete DecrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Decrement Pir In OccupiedState\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Increment Pir In Vacant State\n        /// </summary>\n        [JoinName(\"IncrementPirInVacantState\")]\n        public JoinDataComplete IncrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Increment Pir In Vacant State\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Decrement Pir In Vacant State\n        /// </summary>\n        [JoinName(\"DecrementPirInVacantState\")]\n        public JoinDataComplete DecrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Decrement Pir In Vacant State\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        #endregion\n\n        #region Analog\n        /// <summary>\n        /// Timeout\n        /// </summary>\n        [JoinName(\"Timeout\")]\n        public JoinDataComplete Timeout = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Timeout\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Timeout Local Feedback\n        /// </summary>\n        [JoinName(\"TimeoutLocalFeedback\")]\n        public JoinDataComplete TimeoutLocalFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Timeout Local Feedback\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Internal PhotoSensor Value\n        /// </summary>\n        [JoinName(\"InternalPhotoSensorValue\")]\n        public JoinDataComplete InternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Internal PhotoSensor Value\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// External PhotoSensor Value\n        /// </summary>\n        [JoinName(\"UsSensitivityInOccupiedState\")]\n        public JoinDataComplete UsSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Us Sensitivity In Occupied State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Us Sensitivity In Vacant State\n        /// </summary>\n        [JoinName(\"UsSensitivityInVacantState\")]\n        public JoinDataComplete UsSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Us Sensitivity In Vacant State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Pir Sensitivity In Occupied State\n        /// </summary>\n        [JoinName(\"PirSensitivityInOccupiedState\")]\n        public JoinDataComplete PirSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Pir Sensitivity In Occupied State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Pir Sensitivity In Vacant State\n        /// </summary>\n        [JoinName(\"PirSensitivityInVacantState\")]\n        public JoinDataComplete PirSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Pir Sensitivity In Vacant State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        #endregion\n\n        #region Serial\n\n        /// <summary>\n        /// Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        #endregion\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public CenOdtOccupancySensorBaseJoinMap(uint joinStart)\n            : this(joinStart, typeof(CenOdtOccupancySensorBaseJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected CenOdtOccupancySensorBaseJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/DisplayControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a DisplayControllerJoinMap\n    /// </summary>\n    public class DisplayControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// Power Off\n        /// </summary>\n        [JoinName(\"PowerOff\")]\n        public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Power Off\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Power On\n        /// </summary>\n        [JoinName(\"PowerOn\")]\n        public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Power On\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Is Two Way Display\n        /// </summary>\n        [JoinName(\"IsTwoWayDisplay\")]\n        public JoinDataComplete IsTwoWayDisplay = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Is Two Way Display\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Volume Up\n        /// </summary>\n        [JoinName(\"VolumeUp\")]\n        public JoinDataComplete VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Volume Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Volume Level\n        /// </summary>\n        [JoinName(\"VolumeLevel\")]\n        public JoinDataComplete VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Volume Level\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Volume Down\n        /// </summary>\n        [JoinName(\"VolumeDown\")]\n        public JoinDataComplete VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Volume Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Volume Mute\n        /// </summary>\n        [JoinName(\"VolumeMute\")]\n        public JoinDataComplete VolumeMute = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Volume Mute\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Volume Mute On\n        /// </summary>\n        [JoinName(\"VolumeMuteOn\")]\n        public JoinDataComplete VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Volume Mute On\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Volume Mute Off\n        /// </summary>\n        [JoinName(\"VolumeMuteOff\")]\n        public JoinDataComplete VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Volume Mute Off\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Input Select Offset\n        /// </summary>\n        [JoinName(\"InputSelectOffset\")]\n        public JoinDataComplete InputSelectOffset = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 }, \n            new JoinMetadata { Description = \"Input Select\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Input Names Offset\n        /// </summary>\n        [JoinName(\"InputNamesOffset\")]\n        public JoinDataComplete InputNamesOffset = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 }, \n            new JoinMetadata { Description = \"Input Names Offset\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// Input Select\n        /// </summary>\n        [JoinName(\"InputSelect\")]\n        public JoinDataComplete InputSelect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Input Select\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Button Visibility Offset\n        /// </summary>\n        [JoinName(\"ButtonVisibilityOffset\")]\n        public JoinDataComplete ButtonVisibilityOffset = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 10 }, \n            new JoinMetadata { Description = \"Button Visibility Offset\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalSerial });\n        \n        /// <summary>\n        /// Is Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 }, \n            new JoinMetadata { Description = \"Is Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public DisplayControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(DisplayControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected DisplayControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmBladeChassisControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges {\n    /// <summary>\n    /// Represents a DmBladeChassisControllerJoinMap\n    /// </summary>\n    public class DmBladeChassisControllerJoinMap : JoinMapBaseAdvanced {\n\n        /// <summary>\n        /// DM Blade Chassis Online status\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM Blade Chassis Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Blade Input Video Sync\n        /// </summary>\n        [JoinName(\"VideoSyncStatus\")]\n        public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Input Video Sync\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Blade Chassis Input Endpoint Online\n        /// </summary>\n        [JoinName(\"InputEndpointOnline\")]\n        public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Input Endpoint Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Blade Chassis Output Endpoint Online\n        /// </summary>\n        [JoinName(\"OutputEndpointOnline\")]\n        public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Output Endpoint Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Blade Chassis Tx Advanced Is Present\n        /// </summary>\n        [JoinName(\"TxAdvancedIsPresent\")]\n        public JoinDataComplete TxAdvancedIsPresent = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Tx Advanced Is Present\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Blade Chassis Rx Advanced Is Present\n        /// </summary>\n        [JoinName(\"OutputVideo\")]\n        public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Output Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Blade Chassis Input HDCP Support State\n        /// </summary>\n        [JoinName(\"HdcpSupportState\")]\n        public JoinDataComplete HdcpSupportState = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Input HDCP Support State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Blade Chassis Input HDCP Support Capability\n        /// </summary>\n        [JoinName(\"HdcpSupportCapability\")]\n        public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Input HDCP Support Capability\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Blade Chassis Input Names\n        /// </summary>\n        [JoinName(\"InputNames\")]\n        public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Blade Chassis Output Names\n        /// </summary>\n        [JoinName(\"OutputNames\")]\n        public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Output Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Blade Chassis Video Output Currently Routed Video Input Name\n        /// </summary>\n        [JoinName(\"OutputCurrentVideoInputNames\")]\n        public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Video Output Currently Routed Video Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Blade Chassis Input Current Resolution\n        /// </summary>\n        [JoinName(\"InputCurrentResolution\")]\n        public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 128 },\n            new JoinMetadata { Description = \"DM Blade Chassis Input Current Resolution\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public DmBladeChassisControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(DmBladeChassisControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected DmBladeChassisControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmChassisControllerJoinMap.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a DmChassisControllerJoinMap\n    /// </summary>\n    public class DmChassisControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// DM Chassis enable audio breakaway routing\n        /// </summary>\n        [JoinName(\"EnableAudioBreakaway\")]\n        public JoinDataComplete EnableAudioBreakaway = new JoinDataComplete(\n            new JoinData {JoinNumber = 4, JoinSpan = 1},\n            new JoinMetadata\n            {\n                Description = \"DM Chassis enable audio breakaway routing\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n        \n        /// <summary>\n        /// DM Chassis enable USB breakaway routing\n        /// </summary>\n        [JoinName(\"EnableUsbBreakaway\")]\n        public JoinDataComplete EnableUsbBreakaway = new JoinDataComplete(\n            new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"DM Chassis enable USB breakaway routing\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n        \n        /// <summary>\n        /// DM Chassis Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM Chassis Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Chassis SystemId Get/Set/Trigger\n        /// </summary>\n        [JoinName(\"SystemId\")]\n        public JoinDataComplete SystemId = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM Chassis SystemId Get/Set/Trigger/\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalAnalog });\n        \n        /// <summary>\n        /// DM Chassis Online status\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM Chassis Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Input Video Sync\n        /// </summary>\n        [JoinName(\"VideoSyncStatus\")]\n        public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Input Video Sync\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Chassis Input Endpoint Online\n        /// </summary>\n        [JoinName(\"InputEndpointOnline\")]\n        public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input Endpoint Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Chassis Output Endpoint Online\n        /// </summary>\n        [JoinName(\"OutputEndpointOnline\")]\n        public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Endpoint Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Chassis Tx Advanced Is Present\n        /// </summary>\n        [JoinName(\"TxAdvancedIsPresent\")]\n        public JoinDataComplete TxAdvancedIsPresent = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Tx Advanced Is Present\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Chassis Rx Advanced Is Present\n        /// </summary>\n        [JoinName(\"OutputDisabledByHdcp\")]\n        public JoinDataComplete OutputDisabledByHdcp = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Disabled by HDCP\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Chassis Output Set / Get\n        /// </summary>\n        [JoinName(\"OutputVideo\")]\n        public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Video Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Output Audio Set / Get\n        /// </summary>\n        [JoinName(\"OutputAudio\")]\n        public JoinDataComplete OutputAudio = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Audio Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Input Set / Get\n        /// </summary>\n        [JoinName(\"OutputUsb\")]\n        public JoinDataComplete OutputUsb = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output USB Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Input Set / Get\n        /// </summary>\n        [JoinName(\"InputUsb\")]\n        public JoinDataComplete InputUsb = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input Usb Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Input HDCP Support State\n        /// </summary>\n        [JoinName(\"HdcpSupportState\")]\n        public JoinDataComplete HdcpSupportState = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input HDCP Support State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Input HDCP Support Capability\n        /// </summary>\n        [JoinName(\"HdcpSupportCapability\")]\n        public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input HDCP Support Capability\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback\n        /// </summary>\n        [JoinName(\"InputStreamCardState\")]\n        public JoinDataComplete InputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1501, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback\n        /// </summary>\n        [JoinName(\"OutputStreamCardState\")]\n        public JoinDataComplete OutputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1601, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis No Route Name\n        /// </summary>\n        [JoinName(\"NoRouteName\")]\n        public JoinDataComplete NoRouteName = new JoinDataComplete(new JoinData { JoinNumber = 100, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM Chassis Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Chassis Input Names\n        /// </summary>\n        [JoinName(\"InputNames\")]\n        public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Chassis Output Names\n        /// </summary>\n        [JoinName(\"OutputNames\")]\n        public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Chassis Video Input Names\n        /// </summary>\n        [JoinName(\"InputVideoNames\")] public JoinDataComplete InputVideoNames =\n            new JoinDataComplete(new JoinData {JoinNumber = 501, JoinSpan = 200},\n                new JoinMetadata\n                {\n                    Description = \"DM Chassis Video Input Names\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n        \n        /// <summary>\n        /// DM Chassis Audio Input Names\n        /// </summary>\n        [JoinName(\"InputAudioNames\")]\n        public JoinDataComplete InputAudioNames =\n            new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 200 },\n            new JoinMetadata\n            {\n                Description = \"DM Chassis Audio Input Names\",\n                JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n        \n        /// <summary>\n        /// DM Chassis Video Output Names\n        /// </summary>\n        [JoinName(\"OutputVideoNames\")]\n        public JoinDataComplete OutputVideoNames =\n            new JoinDataComplete(new JoinData { JoinNumber = 901, JoinSpan = 200 },\n            new JoinMetadata\n            {\n                Description = \"DM Chassis Video Output Names\",\n                JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n        \n        /// <summary>\n        /// DM Chassis Audio Output Names\n        /// </summary>\n        [JoinName(\"OutputAudioNames\")]\n        public JoinDataComplete OutputAudioNames =\n            new JoinDataComplete(new JoinData { JoinNumber = 1101, JoinSpan = 200 },\n            new JoinMetadata\n            {\n                Description = \"DM Chassis Audio Output Names\",\n                JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n        \n        /// <summary>\n        /// DM Chassis Video Output Currently Routed Video Input Name\n        /// </summary>\n        [JoinName(\"OutputCurrentVideoInputNames\")]\n        public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Video Output Currently Routed Video Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Chassis Audio Output Currently Routed Audio Input Name\n        /// </summary>\n        [JoinName(\"OutputCurrentAudioInputNames\")]\n        public JoinDataComplete OutputCurrentAudioInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2201, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Audio Output Currently Routed Video Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Chassis Input Current Resolution\n        /// </summary>\n        [JoinName(\"InputCurrentResolution\")]\n        public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input Current Resolution\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public DmChassisControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(DmChassisControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected DmChassisControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmRmcControllerJoinMap.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a DmRmcControllerJoinMap\n    /// </summary>\n    public class DmRmcControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// DM RMC Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM RMC Mute Video\n        /// </summary>\n        [JoinName(\"VideoMuteOn\")]\n        public JoinDataComplete VideoMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC Mute Video\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM RMC UnMute Video\n        /// </summary>\n        [JoinName(\"VideoMuteOff\")]\n        public JoinDataComplete VideoMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC UnMute Video\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM RMC Mute Video Toggle\n        /// </summary>\n        [JoinName(\"VideoMuteToggle\")]\n        public JoinDataComplete VideoMuteToggle = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC Mute Video Toggle\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM RMC Current Output Resolution\n        /// </summary>\n        [JoinName(\"CurrentOutputResolution\")]\n        public JoinDataComplete CurrentOutputResolution = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC Current Output Resolution\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM RMC EDID Manufacturer\n        /// </summary>\n        [JoinName(\"EdidManufacturer\")]\n        public JoinDataComplete EdidManufacturer = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC EDID Manufacturer\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM RMC EDID Name\n        /// </summary>\n        [JoinName(\"EdidName\")]\n        public JoinDataComplete EdidName = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC EDID Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM RMC EDID Preferred Timing\n        /// </summary>\n        [JoinName(\"EdidPrefferedTiming\")]\n        public JoinDataComplete EdidPrefferedTiming = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC EDID Preferred Timing\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM RMC EDID Serial Number\n        /// </summary>\n        [JoinName(\"EdidSerialNumber\")]\n        public JoinDataComplete EdidSerialNumber = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC EDID Serial Number\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM RMC Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM RMC Audio Video Source Set / Get\n        /// </summary>\n        [JoinName(\"AudioVideoSource\")]\n        public JoinDataComplete AudioVideoSource = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC Audio Video Source Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM RMC HDCP Support Capability\n        /// </summary>\n        [JoinName(\"HdcpSupportCapability\")]\n        public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC HDCP Support Capability\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM RMC Port 1 (DM) HDCP State Set / Get\n        /// </summary>\n        [JoinName(\"Port1HdcpState\")]\n        public JoinDataComplete Port1HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC Port 1 (DM) HDCP State Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM RMC Port 2 (HDMI) HDCP State Set / Get\n        /// </summary>\n        [JoinName(\"Port2HdcpState\")]\n        public JoinDataComplete Port2HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC Port 2 (HDMI) HDCP State Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM RMC HDMI Input Sync\n        /// </summary>\n        [JoinName(\"HdmiInputSync\")]\n        public JoinDataComplete HdmiInputSync = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM RMC HDMI Input Sync\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM RMC Number of Input Ports that support HDCP\n        /// </summary>\n        [JoinName(\"HdcpInputPortCount\")]\n        public JoinDataComplete HdcpInputPortCount = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Number of Input Ports that support HDCP\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public DmRmcControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(DmRmcControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected DmRmcControllerJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmTxControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a DmTxControllerJoinMap\n    /// </summary>\n    public class DmTxControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// DM TX Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM TX Video Sync\n        /// </summary>\n        [JoinName(\"VideoSyncStatus\")]\n        public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Video Sync\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM TX Enable Free Run Set / Get\n        /// </summary>\n        [JoinName(\"FreeRunEnabled\")]\n        public JoinDataComplete FreeRunEnabled = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Enable Free Run Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Input 1 Video Sync Status\n        /// </summary>\n        [JoinName(\"Input1VideoSyncStatus\")]\n        public JoinDataComplete Input1VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Input 1 Video Sync Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Input 2 Video Sync Status\n        /// </summary>\n        [JoinName(\"Input2VideoSyncStatus\")]\n        public JoinDataComplete Input2VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Input 2 Video Sync Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Input 3 Video Sync Status\n        /// </summary>\n        [JoinName(\"Input3VideoSyncStatus\")]\n        public JoinDataComplete Input3VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Input 3 Video Sync Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM TX Current Input Resolution\n        /// </summary>\n        [JoinName(\"CurrentInputResolution\")]\n        public JoinDataComplete CurrentInputResolution = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Current Input Resolution\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM TX Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM TX Video Input Set / Get\n        /// </summary>\n        [JoinName(\"VideoInput\")]\n        public JoinDataComplete VideoInput = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Video Input Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM TX Audio Input Set / Get\n        /// </summary>\n        [JoinName(\"AudioInput\")]\n        public JoinDataComplete AudioInput = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Audio Input Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM TX HDCP Support Capability\n        /// </summary>\n        [JoinName(\"HdcpSupportCapability\")]\n        public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX HDCP Support Capability\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM TX Port 1 HDCP State Set / Get\n        /// </summary>\n        [JoinName(\"Port1HdcpState\")]\n        public JoinDataComplete Port1HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Port 1 HDCP State Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM TX Port 2 HDCP State Set / Get\n        /// </summary>\n        [JoinName(\"Port2HdcpState\")]\n        public JoinDataComplete Port2HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Port 2 HDCP State Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM TX VGA Brightness\n        /// </summary>\n        [JoinName(\"VgaBrightness\")]\n        public JoinDataComplete VgaBrightness = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX VGA Brightness\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM TX VGA Contrast\n        /// </summary>\n        [JoinName(\"VgaContrast\")]\n        public JoinDataComplete VgaContrast = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Online\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM TX Port 3 HDCP State Set / Get\n        /// </summary>\n        [JoinName(\"Port3HdcpState\")]\n        public JoinDataComplete Port3HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DM TX Port 3 HDCP State Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM TX Number of Input Ports that support HDCP\n        /// </summary>\n        [JoinName(\"HdcpInputPortCount\")]\n        public JoinDataComplete HdcpInputPortCount = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Number of Input Ports that support HDCP\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public DmTxControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(DmTxControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected DmTxControllerJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a DmpsAudioOutputControllerJoinMap\n    /// </summary>\n    public class DmpsAudioOutputControllerJoinMap : JoinMapBaseAdvanced\n    {\n\n        /// <summary>\n        /// Master Volume Level Signed dB Set / Get\n        /// </summary>\n        [JoinName(\"MasterVolumeLevel\")]\n        public JoinDataComplete MasterVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Master Volume Signed dB Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Master Volume 16bit Scaled Set / Get\n        /// </summary>\n        [JoinName(\"MasterVolumeLevelScaled\")]\n        public JoinDataComplete MasterVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Master Volume 16bit Scaled Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Mixer Preset Recall Set\n        /// </summary>\n        [JoinName(\"MixerPresetRecall\")]\n        public JoinDataComplete MixerPresetRecall = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mixer Preset Recall Set\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Mixer Eq Preset Recall Set\n        /// </summary>\n        [JoinName(\"MixerEqPresetRecall\")]\n        public JoinDataComplete MixerEqPresetRecall = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mixer Eq Preset Recall Set\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Master Volume Mute On Set / Get\n        /// </summary>\n        [JoinName(\"MasterVolumeMuteOn\")]\n        public JoinDataComplete MasterVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Master Volume Mute On Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Master Volume Mute Off Set / Get\n        /// </summary>\n        [JoinName(\"MasterVolumeMuteOff\")]\n        public JoinDataComplete MasterVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Master Volume Mute Off Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Master Volume Level Up\n        /// </summary>\n        [JoinName(\"MasterVolumeUp\")]\n        public JoinDataComplete MasterVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Master Volume Level Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Master Volume Level Down\n        /// </summary>\n        [JoinName(\"MasterVolumeDown\")]\n        public JoinDataComplete MasterVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Master Volume Level Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Master Volume Scaled Send Enable/Disable\n        /// </summary>\n        [JoinName(\"MasterVolumeLevelScaledSend\")]\n        public JoinDataComplete MasterVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Master Volume Scaled Send Enable/Disable\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Source Volume Signed dB Set / Get\n        /// </summary>\n        [JoinName(\"SourceVolumeLevel\")]\n        public JoinDataComplete SourceVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Source Volume Signed dB Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Source Volume 16bit Scaled Set / Get\n        /// </summary>\n        [JoinName(\"SourceVolumeLevelScaled\")]\n        public JoinDataComplete SourceVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Source Volume 16bit Scaled Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Source Volume Mute On Set / Get\n        /// </summary>\n        [JoinName(\"SourceVolumeMuteOn\")]\n        public JoinDataComplete SourceVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Source Volume Mute On Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Source Volume Mute Off Set / Get\n        /// </summary>\n        [JoinName(\"SourceVolumeMuteOff\")]\n        public JoinDataComplete SourceVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Source Volume Mute Off Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Source Volume Level Up\n        /// </summary>\n        [JoinName(\"SourceVolumeUp\")]\n        public JoinDataComplete SourceVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Source Volume Level Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Source Volume Level Down\n        /// </summary>\n        [JoinName(\"SourceVolumeDown\")]\n        public JoinDataComplete SourceVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Source Volume Level Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Source Volume Scaled Send Enable/Disable\n        /// </summary>\n        [JoinName(\"SourceVolumeLevelScaledSend\")]\n        public JoinDataComplete SourceVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Source Volume Scaled Send Enable/Disable\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec1 Volume Signed dB Set / Get\n        /// </summary>\n        [JoinName(\"Codec1VolumeLevel\")]\n        public JoinDataComplete Codec1VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec1 Volume Signed dB Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Codec1 Volume 16bit Scaled Set / Get\n        /// </summary>\n        [JoinName(\"Codec1VolumeLevelScaled\")]\n        public JoinDataComplete Codec1VolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec1 Volume 16bit Scaled Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Codec1 Volume Mute On Set / Get\n        /// </summary>\n        [JoinName(\"Codec1VolumeMuteOn\")]\n        public JoinDataComplete Codec1VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec1 Volume Mute On Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec1 Volume Mute Off Set / Get\n        /// </summary>\n        [JoinName(\"Codec1VolumeMuteOff\")]\n        public JoinDataComplete Codec1VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec1 Volume Mute Off Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec1 Volume Level Up\n        /// </summary>\n        [JoinName(\"Codec1VolumeUp\")]\n        public JoinDataComplete Codec1VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec1 Volume Level Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec1 Volume Level Down\n        /// </summary>\n        [JoinName(\"Codec1VolumeDown\")]\n        public JoinDataComplete Codec1VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec1 Volume Level Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec1 Volume Scaled Send Enable/Disable\n        /// </summary>\n        [JoinName(\"Codec1VolumeLevelScaledSend\")]\n        public JoinDataComplete Codec1VolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec1 Volume Scaled Send Enable/Disable\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec2 Volume Signed dB Set / Get\n        /// </summary>\n        [JoinName(\"Codec2VolumeLevel\")]\n        public JoinDataComplete Codec2VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec2 Volume Signed dB Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Codec2 Volume 16bit Scaled Set / Get\n        /// </summary>\n        [JoinName(\"Codec2VolumeLevelScaled\")]\n        public JoinDataComplete Codec2VolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec2 Volume 16bit Scaled Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Codec2 Volume Mute On Set / Get\n        /// </summary>\n        [JoinName(\"Codec2VolumeMuteOn\")]\n        public JoinDataComplete Codec2VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec2 Volume Mute On Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec2 Volume Mute Off Set / Get\n        /// </summary>\n        [JoinName(\"Codec2VolumeMuteOff\")]\n        public JoinDataComplete Codec2VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec2 Volume Mute Off Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec2 Volume Level Up\n        /// </summary>\n        [JoinName(\"Codec2VolumeUp\")]\n        public JoinDataComplete Codec2VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec2 Volume Level Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec2 Volume Level Down\n        /// </summary>\n        [JoinName(\"Codec2VolumeDown\")]\n        public JoinDataComplete Codec2VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec2 Volume Level Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Codec2 Volume Scaled Send Enable/Disable\n        /// </summary>\n        [JoinName(\"Codec2VolumeLevelScaledSend\")]\n        public JoinDataComplete Codec2VolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Codec2 Volume Scaled Send Enable/Disable\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// MicsMaster Volume Signed dB Set / Get\n        /// </summary>\n        [JoinName(\"MicsMasterVolumeLevel\")]\n        public JoinDataComplete MicsMasterVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },\n            new JoinMetadata { Description = \"MicsMaster Volume Signed dB Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// MicsMaster Volume 16bit Scaled Set / Get\n        /// </summary>\n        [JoinName(\"MicsMasterVolumeLevelScaled\")]\n        public JoinDataComplete MicsMasterVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },\n            new JoinMetadata { Description = \"MicsMaster Volume 16bit Scaled Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// MicsMaster Volume Mute On Set / Get\n        /// </summary>\n        [JoinName(\"MicsMasterVolumeMuteOn\")]\n        public JoinDataComplete MicsMasterVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },\n            new JoinMetadata { Description = \"MicsMaster Volume Mute On Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// MicsMaster Volume Mute Off Set / Get\n        /// </summary>\n        [JoinName(\"MicsMasterVolumeMuteOff\")]\n        public JoinDataComplete MicsMasterVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },\n            new JoinMetadata { Description = \"MicsMaster Volume Mute Off Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// MicsMaster Volume Level Up\n        /// </summary>\n        [JoinName(\"MicsMasterVolumeUp\")]\n        public JoinDataComplete MicsMasterVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },\n            new JoinMetadata { Description = \"MicsMaster Volume Level Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// MicsMaster Volume Level Down\n        /// </summary>\n        [JoinName(\"MicsMasterVolumeDown\")]\n        public JoinDataComplete MicsMasterVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 44, JoinSpan = 1 },\n            new JoinMetadata { Description = \"MicsMaster Volume Level Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// MicsMaster Volume Scaled Send Enable/Disable\n        /// </summary>\n        [JoinName(\"MicsMasterVolumeLevelScaledSend\")]\n        public JoinDataComplete MicsMasterVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 45, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mics Master Volume Scaled Send Enable/Disable\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public DmpsAudioOutputControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(DmpsAudioOutputControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected DmpsAudioOutputControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsMicrophoneControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a DmpsMicrophoneControllerJoinMap\n    /// </summary>\n    public class DmpsMicrophoneControllerJoinMap : JoinMapBaseAdvanced\n    {\n\n        /// <summary>\n        /// Mic Gain dB Set / Get\n        /// </summary>\n        [JoinName(\"MicGain\")]\n        public JoinDataComplete MicGain = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mic Gain dB Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Mic Gain 16bit Scaled Set / Get\n        /// </summary>\n        [JoinName(\"MicGainScaled\")]\n        public JoinDataComplete MicGainScaled = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mic Gain 16bit Scaled Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Mic Mute On Set / Get\n        /// </summary>\n        [JoinName(\"MicMuteOn\")]\n        public JoinDataComplete MicMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mic Mute On Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Mic Mute Off Set / Get\n        /// </summary>\n        [JoinName(\"MicMuteOff\")]\n        public JoinDataComplete MicMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mic Mute Off Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Mic Gain Scaled Send Enable/Disable\n        /// </summary>\n        [JoinName(\"MicGainScaledSend\")]\n        public JoinDataComplete MicGainScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mic Gain Scaled Send Enable/Disable\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Mic Name Get\n        /// </summary>\n        [JoinName(\"MicName\")]\n        public JoinDataComplete MicName = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mic Name Get\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public DmpsMicrophoneControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(DmpsMicrophoneControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected DmpsMicrophoneControllerJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a DmpsRoutingControllerJoinMap\n    /// </summary>\n    public class DmpsRoutingControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// DMPS Enable Audio and Video Routing\n        /// </summary>\n        [JoinName(\"EnableRouting\")]\n        public JoinDataComplete EnableRouting = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DMPS Enable Audio and Video Routing\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DMPS Disable Audio and Video Routing\n        /// </summary>\n        [JoinName(\"SystemPowerOn\")]\n        public JoinDataComplete SystemPowerOn = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DMPS System Power On Get/Set\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DMPS Disable Audio and Video Routing\n        /// </summary>\n        [JoinName(\"SystemPowerOff\")]\n        public JoinDataComplete SystemPowerOff = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DMPS System Power Off Get/Set\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DMPS Front Panel Lock On Get/Set\n        /// </summary>\n        [JoinName(\"FrontPanelLockOn\")]\n        public JoinDataComplete FrontPanelLockOn = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DMPS Front Panel Lock On Get/Set\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DMPS Front Panel Lock  Off Get/Set\n        /// </summary>\n        [JoinName(\"FrontPanelLockOff\")]\n        public JoinDataComplete FrontPanelLockOff = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DMPS Front Panel Lock  Off Get/Set\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Input Video Sync\n        /// </summary>\n        [JoinName(\"VideoSyncStatus\")]\n        public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Input Video Sync\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Chassis Input Endpoint Online\n        /// </summary>\n        [JoinName(\"InputEndpointOnline\")]\n        public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input Endpoint Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Chassis Output Endpoint Online\n        /// </summary>\n        [JoinName(\"OutputEndpointOnline\")]\n        public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Endpoint Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// DM Chassis Input Video Set / Get\n        /// </summary>\n        [JoinName(\"OutputVideo\")]\n        public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Video Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Input Audio Set / Get\n        /// </summary>\n        [JoinName(\"OutputAudio\")]\n        public JoinDataComplete OutputAudio = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Audio Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// DM Chassis Input Name\n        /// </summary>\n        [JoinName(\"InputNames\")]\n        public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Chassis Output Name\n        /// </summary>\n        [JoinName(\"OutputNames\")]\n        public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Output Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n        \n        /// <summary>\n        /// DM Chassis Video Input Name\n        /// </summary>\n        [JoinName(\"InputVideoNames\")]\n        public JoinDataComplete InputVideoNames =\n            new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },\n            new JoinMetadata\n            {\n                Description = \"DM Chassis Video Input Name\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n       \n        /// <summary>\n        /// DM Chassis Audio Input Name\n        /// </summary>\n        [JoinName(\"InputAudioNames\")]\n        public JoinDataComplete InputAudioNames =\n            new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },\n            new JoinMetadata\n            {\n                Description = \"Audio Input Name\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n       \n        /// <summary>\n        /// DM Chassis Video Output Name\n        /// </summary>\n        [JoinName(\"OutputVideoNames\")]\n        public JoinDataComplete OutputVideoNames =\n            new JoinDataComplete(new JoinData { JoinNumber = 901, JoinSpan = 32 },\n            new JoinMetadata\n            {\n                Description = \"Video Output Name\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n       \n        /// <summary>\n        /// DM Chassis Audio Output Name\n        /// </summary>    \n        [JoinName(\"OutputAudioNames\")]\n        public JoinDataComplete OutputAudioNames =\n            new JoinDataComplete(new JoinData { JoinNumber = 1101, JoinSpan = 32 },\n            new JoinMetadata\n            {\n                Description = \"Audio Output Name\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n       \n        /// <summary>\n        /// DM Chassis Video Output Currently Routed Video Input Name\n        /// </summary>\n        [JoinName(\"OutputCurrentVideoInputNames\")]\n        public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Video Output Currently Routed Video Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n       \n        /// <summary>\n        /// DM Chassis Audio Output Currently Routed Video Input Name\n        /// </summary>\n        [JoinName(\"OutputCurrentAudioInputNames\")]\n        public JoinDataComplete OutputCurrentAudioInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2201, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Audio Output Currently Routed Video Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n       \n        /// <summary>\n        /// DM Chassis Input Current Resolution\n        /// </summary>\n        [JoinName(\"InputCurrentResolution\")]\n        public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 32 },\n            new JoinMetadata { Description = \"DM Chassis Input Current Resolution\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public DmpsRoutingControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(DmpsRoutingControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected DmpsRoutingControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.Bridges.JoinMaps\n{\n\t\t/// <summary>\n\t\t/// Represents a GenericIrControllerJoinMap\n\t\t/// </summary>\n\t\tpublic sealed class GenericIrControllerJoinMap : JoinMapBaseAdvanced\n\t\t{\n\t\t\t/// <summary>\n\t\t\t/// PLAY\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"PLAY\")]\n\t\t\tpublic JoinDataComplete Play = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 1,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"PLAY\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// STOP\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"STOP\")]\n\t\t\tpublic JoinDataComplete Stop = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 2,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"STOP\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// PAUSE\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"PAUSE\")]\n\t\t\tpublic JoinDataComplete Pause = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 3,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"PAUSE\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\n\t\t\t/// <summary>\n\t\t\t/// FSCAN\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"FSCAN\")]\n\t\t\tpublic JoinDataComplete ForwardScan = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 4,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"FSCAN\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// RSCAN\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"RSCAN\")]\n\t\t\tpublic JoinDataComplete ReverseScan = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 5,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"RSCAN\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// F_SKIP\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"F_SKIP\")]\n\t\t\tpublic JoinDataComplete ForwardSkip = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 6,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"F_SKIP\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// R_SKIP\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"R_SKIP\")]\n\t\t\tpublic JoinDataComplete ReverseSkip = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 7,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"R_SKIP\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// RECORD\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"RECORD\")]\n\t\t\tpublic JoinDataComplete Record = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 8,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"RECORD\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// POWER\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"POWER\")]\n\t\t\tpublic JoinDataComplete Power = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 9,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"POWER\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 0\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"0\")]\n\t\t\tpublic JoinDataComplete Kp0 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 10,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"0\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 1\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"1\")]\n\t\t\tpublic JoinDataComplete Kp1 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 11,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"1\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 2\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"2\")]\n\t\t\tpublic JoinDataComplete Kp2 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 12,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"2\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 3\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"3\")]\n\t\t\tpublic JoinDataComplete Kp3 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 13,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"3\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 4\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"4\")]\n\t\t\tpublic JoinDataComplete Kp4 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 14,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"4\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 5\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"5\")]\n\t\t\tpublic JoinDataComplete Kp5 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 15,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"5\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 6\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"6\")]\n\t\t\tpublic JoinDataComplete Kp6 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 16,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"6\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 7\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"7\")]\n\t\t\tpublic JoinDataComplete Kp7 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 17,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"7\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 8\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"8\")]\n\t\t\tpublic JoinDataComplete Kp8 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 18,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"8\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// 9\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"9\")]\n\t\t\tpublic JoinDataComplete Kp9 = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 19,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"9\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\n\t\t\t// [JoinName(\"+10\")]\n\t\t\t// public JoinDataComplete Kp9 = new JoinDataComplete(\n\t\t\t// \tnew JoinData\n\t\t\t// \t{\n\t\t\t// \t\tJoinNumber = 20,\n\t\t\t// \t\tJoinSpan = 1\n\t\t\t// \t},\n\t\t\t// \tnew JoinMetadata\n\t\t\t// \t{\n\t\t\t// \t\tDescription = \"+10\",\n\t\t\t// \t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t// \t\tJoinType = eJoinType.Digital\n\t\t\t// \t});\n\n\t\t\t/// <summary>\n\t\t\t/// ENTER\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"ENTER\")]\n\t\t\tpublic JoinDataComplete Enter = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 21,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"ENTER\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// CH+\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"CH+\")]\n\t\t\tpublic JoinDataComplete ChannelUp = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 22,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"CH+\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// CH-\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"CH-\")]\n\t\t\tpublic JoinDataComplete ChannelDown = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 23,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"CH-\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\n\t\t\t/// <summary>\n\t\t\t/// *\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"*\")]\n\t\t\tpublic JoinDataComplete KpStar = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 24,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"*\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// #\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"#\")]\n\t\t\tpublic JoinDataComplete KpPound = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 25,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"#\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\n\t\t\t// [JoinName(\".\")]\n\t\t\t// public JoinDataComplete KpPound = new JoinDataComplete(\n\t\t\t// \tnew JoinData\n\t\t\t// \t{\n\t\t\t// \t\tJoinNumber = 26,\n\t\t\t// \t\tJoinSpan = 1\n\t\t\t// \t},\n\t\t\t// \tnew JoinMetadata\n\t\t\t// \t{\n\t\t\t// \t\tDescription = \".\",\n\t\t\t// \t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t// \t\tJoinType = eJoinType.Digital\n\t\t\t// \t});\n\n\t\t\t/// <summary>\n\t\t\t/// POWER_ON\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"POWER_ON\")]\n\t\t\tpublic JoinDataComplete PowerOn = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 27,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"POWER_ON\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// POWER_OFF\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"POWER_OFF\")]\n\t\t\tpublic JoinDataComplete PowerOff = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 28,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"POWER_OFF\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// PLAY_PAUSE\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"PLAY_PAUSE\")]\n\t\t\tpublic JoinDataComplete PlayPause = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 29,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"PLAY_PAUSE\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// LAST\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"LAST\")]\n\t\t\tpublic JoinDataComplete Last = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 30,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"LAST\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// HOME\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"HOME\")]\n\t\t\tpublic JoinDataComplete Home = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 40,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"HOME\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// BACK\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"BACK\")]\n\t\t\tpublic JoinDataComplete Back = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 41,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"BACK\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\n\t\t\t/// <summary>\n\t\t\t/// GUIDE\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"GUIDE\")]\n\t\t\tpublic JoinDataComplete Guide = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 42,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"GUIDE\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// INFO\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"INFO\")]\n\t\t\tpublic JoinDataComplete Info = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 43,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"INFO\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// MENU\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"MENU\")]\n\t\t\tpublic JoinDataComplete Menu = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 44,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"MENU\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// UP_ARROW\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"UP_ARROW\")]\n\t\t\tpublic JoinDataComplete DpadUp = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 45,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"UP_ARROW\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// DN_ARROW\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"DN_ARROW\")]\n\t\t\tpublic JoinDataComplete DpadDown = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 46,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"DN_ARROW\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// LEFT_ARROW\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"LEFT_ARROW\")]\n\t\t\tpublic JoinDataComplete DpadLeft = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 47,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"LEFT_ARROW\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// RIGHT_ARROW\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"RIGHT_ARROW\")]\n\t\t\tpublic JoinDataComplete DpadRight = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 48,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"RIGHT_ARROW\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// SELECT\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"SELECT\")]\n\t\t\tpublic JoinDataComplete DpadSelect = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 49,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"SELECT\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// OPTIONS\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"OPTIONS\")]\n\t\t\tpublic JoinDataComplete Options = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 50,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"OPTIONS\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// RETURN\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"RETURN\")]\n\t\t\tpublic JoinDataComplete Return = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 51,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"RETURN\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// DVR\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"DVR\")]\n\t\t\tpublic JoinDataComplete Dvr = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 52,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"DVR\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// ON_DEMAND\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"ON_DEMAND\")]\n\t\t\tpublic JoinDataComplete OnDemand = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 53,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"ON_DEMAND\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// PAGE_UP\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"PAGE_UP\")]\n\t\t\tpublic JoinDataComplete PageUp = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 54,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"PAGE_UP\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// PAGE_DOWN\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"PAGE_DOWN\")]\n\t\t\tpublic JoinDataComplete PageDown = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 55,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"PAGE_DOWN\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// F_SRCH\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"F_SRCH\")]\n\t\t\tpublic JoinDataComplete ForwardSearch = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 56,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"F_SRCH\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// R_SRCH\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"R_SRCH\")]\n\t\t\tpublic JoinDataComplete ReverseSearch = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 57,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"R_SRCH\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// TRACK+\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"TRACK+\")]\n\t\t\tpublic JoinDataComplete TrackPlus = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 58,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"TRACK+\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// TRACK-\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"TRACK-\")]\n\t\t\tpublic JoinDataComplete TrackMinus = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 59,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"TRACK-\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// A\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"A\")]\n\t\t\tpublic JoinDataComplete KpA = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 61,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"A\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// B\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"B\")]\n\t\t\tpublic JoinDataComplete KpB = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 62,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"B\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// C\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"C\")]\n\t\t\tpublic JoinDataComplete KpC = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 63,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"C\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// D\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"D\")]\n\t\t\tpublic JoinDataComplete KpD = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 64,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"D\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// RED\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"RED\")]\n\t\t\tpublic JoinDataComplete KpRed = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 65,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"RED\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// GREEN\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"GREEN\")]\n\t\t\tpublic JoinDataComplete KpGreen = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 66,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"GREEN\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// YELLOW\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"YELLOW\")]\n\t\t\tpublic JoinDataComplete KpYellow = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 67,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"YELLOW\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// BLUE\n\t\t\t/// </summary>\n\t\t\t[JoinName(\"BLUE\")]\n\t\t\tpublic JoinDataComplete KpBlue = new JoinDataComplete(\n\t\t\t\tnew JoinData\n\t\t\t\t{\n\t\t\t\t\tJoinNumber = 68,\n\t\t\t\t\tJoinSpan = 1\n\t\t\t\t},\n\t\t\t\tnew JoinMetadata\n\t\t\t\t{\n\t\t\t\t\tDescription = \"BLUE\",\n\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t});\n\n\t\t\t/// <summary>\n\t\t\t/// Constructor\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"joinStart\">Join this join map will start at</param>\n\t\t\tpublic GenericIrControllerJoinMap(uint joinStart)\n\t\t\t\t: base(joinStart, typeof(GenericIrControllerJoinMap))\n\t\t\t{\n\t\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericLightingJoinMap.cs",
    "content": "﻿using System;\n\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a GenericLightingJoinMap\n    /// </summary>\n    public class GenericLightingJoinMap : JoinMapBaseAdvanced\n    {\n\n        /// <summary>\n        /// Lighting Controller Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Lighting Controller Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Select Scene By Index\n        /// </summary>\n        [JoinName(\"SelectScene\")]\n        public JoinDataComplete SelectScene = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Lighting Controller Select Scene By Index\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Select Scene Direct\n        /// </summary>\n        [JoinName(\"SelectSceneDirect\")]\n        public JoinDataComplete SelectSceneDirect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 },\n            new JoinMetadata { Description = \"Lighting Controller Select Scene\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalSerial });\n        \n        /// <summary>\n        /// Button Visibility\n        /// </summary>\n        [JoinName(\"ButtonVisibility\")]\n        public JoinDataComplete ButtonVisibility = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 10 },\n            new JoinMetadata { Description = \"Lighting Controller Button Visibility\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Set Integration Id\n        /// </summary>\n        [JoinName(\"IntegrationIdSet\")]\n        public JoinDataComplete IntegrationIdSet = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Lighting Controller Set Integration Id\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public GenericLightingJoinMap(uint joinStart)\n            : this(joinStart, typeof(GenericLightingJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected GenericLightingJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericRelayControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a GenericRelayControllerJoinMap\n    /// </summary>\n    public class GenericRelayControllerJoinMap : JoinMapBaseAdvanced\n    {\n\n        /// <summary>\n        /// Device Relay State Set / Get\n        /// </summary>\n        [JoinName(\"Relay\")]\n        public JoinDataComplete Relay = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Relay State Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public GenericRelayControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(GenericRelayControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected GenericRelayControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n            \n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsOccupancySensorBaseJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a GlsOccupancySensorBaseJoinMap\n    /// </summary>\n    public class GlsOccupancySensorBaseJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Occ Sensor Is Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Is Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Set to Occupied\n        /// </summary>\n        [JoinName(\"ForceOccupied\")]\n        public JoinDataComplete ForceOccupied = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Set to Occupied\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Set to Vacant\n        /// </summary>\n        [JoinName(\"ForceVacant\")]\n        public JoinDataComplete ForceVacant = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Set to Vacant\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Enable Raw\n        /// </summary>\n        [JoinName(\"EnableRawStates\")]\n        public JoinDataComplete EnableRawStates = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Enable Raw\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Disable Raw\n        /// </summary>\n        [JoinName(\"RoomOccupiedFeedback\")]\n        public JoinDataComplete RoomOccupiedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Room Is Occupied\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Grace Occupancy Detected\n        /// </summary>\n        [JoinName(\"GraceOccupancyDetectedFeedback\")]\n        public JoinDataComplete GraceOccupancyDetectedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Grace Occupancy Detected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Room Is Vacant\n        /// </summary>\n        [JoinName(\"RoomVacantFeedback\")]\n        public JoinDataComplete RoomVacantFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Room Is Vacant\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Raw Occupancy Detected\n        /// </summary>\n        [JoinName(\"RawOccupancyFeedback\")]\n        public JoinDataComplete RawOccupancyFeedback = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Raw Occupancy Detected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Raw PIR Occupancy Detected\n        /// </summary>\n        [JoinName(\"RawOccupancyPirFeedback\")]\n        public JoinDataComplete RawOccupancyPirFeedback = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Raw PIR Occupancy Detected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Raw US Occupancy Detected\n        /// </summary>\n        [JoinName(\"RawOccupancyUsFeedback\")]\n        public JoinDataComplete RawOccupancyUsFeedback = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Raw US Occupancy Detected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Enable LED Flash\n        /// </summary>\n        [JoinName(\"EnableLedFlash\")]\n        public JoinDataComplete EnableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Enable LED Flash\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Disable LED Flash\n        /// </summary>\n        [JoinName(\"DisableLedFlash\")]\n        public JoinDataComplete DisableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Disable LED Flash\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Enable Short Timeout\n        /// </summary>\n        [JoinName(\"EnableShortTimeout\")]\n        public JoinDataComplete EnableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Enable Short Timeout\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Disable Short Timeout\n        /// </summary>\n        [JoinName(\"DisableShortTimeout\")]\n        public JoinDataComplete DisableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Disable Short Timeout\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Set To Vacant when Either Sensor is Vacant\n        /// </summary>\n        [JoinName(\"OrWhenVacated\")]\n        public JoinDataComplete OrWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Set To Vacant when Either Sensor is Vacant\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Set To Vacant when Both Sensors are Vacant\n        /// </summary>\n        [JoinName(\"AndWhenVacated\")]\n        public JoinDataComplete AndWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Set To Vacant when Both Sensors are Vacant\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Enable Ultrasonic Sensor A\n        /// </summary>\n        [JoinName(\"EnableUsA\")]\n        public JoinDataComplete EnableUsA = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Enable Ultrasonic Sensor A\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Disable Ultrasonic Sensor A\n        /// </summary>\n        [JoinName(\"DisableUsA\")]\n        public JoinDataComplete DisableUsA = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Disable Ultrasonic Sensor A\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Enable Ultrasonic Sensor B\n        /// </summary>\n        [JoinName(\"EnableUsB\")]\n        public JoinDataComplete EnableUsB = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Enable Ultrasonic Sensor B\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Disable Ultrasonic Sensor B\n        /// </summary>\n        [JoinName(\"DisableUsB\")]\n        public JoinDataComplete DisableUsB = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Disable Ultrasonic Sensor B\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Enable IR Sensor\n        /// </summary>\n        [JoinName(\"EnablePir\")]\n        public JoinDataComplete EnablePir = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Enable IR Sensor\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Disable IR Sensor\n        /// </summary>\n        [JoinName(\"DisablePir\")]\n        public JoinDataComplete DisablePir = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Disable IR Sensor\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Increment US Occupied State Sensitivity\n        /// </summary>\n        [JoinName(\"IncrementUsInOccupiedState\")]\n        public JoinDataComplete IncrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Increment US Occupied State Sensitivity\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Decrement US Occupied State Sensitivity\n        /// </summary>\n        [JoinName(\"DecrementUsInOccupiedState\")]\n        public JoinDataComplete DecrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Decrement US Occupied State Sensitivity\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Increment US Vacant State Sensitivity\n        /// </summary>\n        [JoinName(\"IncrementUsInVacantState\")]\n        public JoinDataComplete IncrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Increment US Vacant State Sensitivity\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Decrement US Vacant State Sensitivity\n        /// </summary>\n        [JoinName(\"DecrementUsInVacantState\")]\n        public JoinDataComplete DecrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Decrement US Vacant State Sensitivity\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Increment IR Occupied State Sensitivity\n        /// </summary>\n        [JoinName(\"IncrementPirInOccupiedState\")]\n        public JoinDataComplete IncrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Increment IR Occupied State Sensitivity\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Decrement IR Occupied State Sensitivity\n        /// </summary>\n        [JoinName(\"DecrementPirInOccupiedState\")]\n        public JoinDataComplete DecrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Decrement IR Occupied State Sensitivity\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Increment IR Vacant State Sensitivity\n        /// </summary>\n        [JoinName(\"IncrementPirInVacantState\")]\n        public JoinDataComplete IncrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Increment IR Vacant State Sensitivity\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Decrement IR Vacant State Sensitivity\n        /// </summary>\n        [JoinName(\"DecrementPirInVacantState\")]\n        public JoinDataComplete DecrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Decrement IR Vacant State Sensitivity\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Occ Sensor Timeout Value\n        /// </summary>\n        [JoinName(\"Timeout\")]\n        public JoinDataComplete Timeout = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Timeout Value\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Occ Sensor Local Timeout Value\n        /// </summary>\n        [JoinName(\"TimeoutLocalFeedback\")]\n        public JoinDataComplete TimeoutLocalFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Local Timeout Value\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Occ Sensor Internal PhotoSensor Value\n        /// </summary>\n        [JoinName(\"InternalPhotoSensorValue\")]\n        public JoinDataComplete InternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Internal PhotoSensor Value\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Occ Sensor External PhotoSensor Value\n        /// </summary>\n        [JoinName(\"ExternalPhotoSensorValue\")]\n        public JoinDataComplete ExternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor External PhotoSensor Value\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Occ Sensor Ultrasonic Sensitivity in Occupied State\n        /// </summary>\n        [JoinName(\"UsSensitivityInOccupiedState\")]\n        public JoinDataComplete UsSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Ultrasonic Sensitivity in Occupied State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Occ Sensor Ultrasonic Sensitivity in Vacant State\n        /// </summary>\n        [JoinName(\"UsSensitivityInVacantState\")]\n        public JoinDataComplete UsSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Ultrasonic Sensitivity in Vacant State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Occ Sensor PIR Sensitivity in Occupied State\n        /// </summary>\n        [JoinName(\"PirSensitivityInOccupiedState\")]\n        public JoinDataComplete PirSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor PIR Sensitivity in Occupied State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Occ Sensor PIR Sensitivity in Vacant State\n        /// </summary>\n        [JoinName(\"PirSensitivityInVacantState\")]\n        public JoinDataComplete PirSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor PIR Sensitivity in Vacant State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Occ Sensor Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Occ Sensor Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public GlsOccupancySensorBaseJoinMap(uint joinStart)\n            : this(joinStart, typeof(GlsOccupancySensorBaseJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected GlsOccupancySensorBaseJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n\n    }\n}   \n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs",
    "content": "﻿using System;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.Bridges.JoinMaps\n{\n    /// <summary>\n    /// Represents a GlsPartitionSensorJoinMap\n    /// </summary>\n    public class GlsPartitionSensorJoinMap : JoinMapBaseAdvanced\n    {\n\n        #region Digital\n\n        /// <summary>\n        /// Sensor Is Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 1,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sensor Is Online\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        /// <summary>\n        /// Sensor Enable\n        /// </summary>\n        [JoinName(\"Enable\")]\n        public JoinDataComplete Enable = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 2,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sensor Enable\",\n                JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        /// <summary>\n        /// Sensor Partition Sensed\n        /// </summary>\n        [JoinName(\"PartitionSensed\")]\n        public JoinDataComplete PartitionSensed = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 3,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sensor Partition Sensed\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        /// <summary>\n        /// Sensor Partition Not Sensed\n        /// </summary>\n        [JoinName(\"PartitionNotSensed\")]\n        public JoinDataComplete PartitionNotSensed = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 4,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sensor Partition Not Sensed\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        /// <summary>\n        /// Sensor Increase Sensitivity\n        /// </summary>\n        [JoinName(\"IncreaseSensitivity\")]\n        public JoinDataComplete IncreaseSensitivity = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 6,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sensor Increase Sensitivity\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        /// <summary>\n        /// Sensor Decrease Sensitivity\n        /// </summary>\n        [JoinName(\"DecreaseSensitivity\")]\n        public JoinDataComplete DecreaseSensitivity = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 7,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sensor Decrease Sensitivity\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });        \n\n        #endregion\n\n        #region Analog\n\n        /// <summary>\n        /// Sensor Sensitivity\n        /// </summary>\n        [JoinName(\"Sensitivity\")]\n        public JoinDataComplete Sensitivity = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 2,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sensor Sensitivity\",\n                JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n        #endregion\n\n\n        #region Serial\n\n        /// <summary>\n        /// Sensor Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 1,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sensor Name\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n        #endregion\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public GlsPartitionSensorJoinMap(uint joinStart)\n            : this(joinStart, typeof(GlsPartitionSensorJoinMap))\n        {\n\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected GlsPartitionSensorJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdNxM4kEControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a HdMdNxM4kEControllerJoinMap\n    /// </summary>\n    public class HdMdNxM4kEControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Device Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Enable Automatic Routing on 4x1 Switchers\n        /// </summary>\n        [JoinName(\"EnableAutoRoute\")]\n        public JoinDataComplete EnableAutoRoute = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enable Automatic Routing on 4x1 Switchers\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Device Input Name\n        /// </summary>\n        [JoinName(\"InputName\")]\n        public JoinDataComplete InputName = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 8 },\n            new JoinMetadata { Description = \"Device Input Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Device Input Sync\n        /// </summary>\n        [JoinName(\"InputSync\")]\n        public JoinDataComplete InputSync = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 8 },\n            new JoinMetadata { Description = \"Device Input Sync\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Device Output Name\n        /// </summary>\n        [JoinName(\"OutputName\")]\n        public JoinDataComplete OutputName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 2 },\n            new JoinMetadata { Description = \"Device Output Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Device Output Route Set/Get\n        /// </summary>\n        [JoinName(\"OutputRoute\")]\n        public JoinDataComplete OutputRoute = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 2 },\n            new JoinMetadata { Description = \"Device Output Route Set/Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Device Output Route Name\n        /// </summary>\n        [JoinName(\"OutputRoutedName\")]\n        public JoinDataComplete OutputRoutedName = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 2 },\n            new JoinMetadata { Description = \"Device Output Route Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Device Enable Input Hdcp\n        /// </summary>\n        [JoinName(\"EnableInputHdcp\")]\n        public JoinDataComplete EnableInputHdcp = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 8 },\n            new JoinMetadata { Description = \"Device Enable Input Hdcp\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Device Disable Input Hdcp\n        /// </summary>\n        [JoinName(\"DisableInputHdcp\")]\n        public JoinDataComplete DisableInputHdcp = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 8 },\n            new JoinMetadata { Description = \"Device Disnable Input Hdcp\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Device Online Status\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Onlne\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public HdMdNxM4kEControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(HdMdNxM4kEControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected HdMdNxM4kEControllerJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdxxxCEControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a HdMdxxxCEControllerJoinMap\n    /// </summary>\n    public class HdMdxxxCEControllerJoinMap : JoinMapBaseAdvanced\n    {\n\n        /// <summary>\n        /// Device Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Remote End Detected\n        /// </summary>\n        [JoinName(\"RemoteEndDetected\")]\n        public JoinDataComplete RemoteEndDetected = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Remote End Detected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Auto Route On\n        /// </summary>\n        [JoinName(\"AutoRouteOn\")]\n        public JoinDataComplete AutoRouteOn = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Auto Route On\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Auto Route Off\n        /// </summary>\n        [JoinName(\"AutoRouteOff\")]\n        public JoinDataComplete AutoRouteOff = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Auto Route Off\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Priority Routing On\n        /// </summary>\n        [JoinName(\"PriorityRoutingOn\")]\n        public JoinDataComplete PriorityRoutingOn = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Priority Routing On\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Priority Routing Off\n        /// </summary>\n        [JoinName(\"PriorityRoutingOff\")]\n        public JoinDataComplete PriorityRoutingOff = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Priority Routing Off\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Input On Screen Display Enabled\n        /// </summary>\n        [JoinName(\"InputOnScreenDisplayEnabled\")]\n        public JoinDataComplete InputOnScreenDisplayEnabled = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Input OSD Enabled\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Input On Screen Display Disabled\n        /// </summary>\n        [JoinName(\"InputOnScreenDisplayDisabled\")]\n        public JoinDataComplete InputOnScreenDisplayDisabled = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Device Input OSD Disabled\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Sync Detected\n        /// </summary>\n        [JoinName(\"SyncDetected\")]\n        public JoinDataComplete SyncDetected = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 5 },\n            new JoinMetadata { Description = \"Device Sync Detected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Video Source\n        /// </summary>\n        [JoinName(\"VideoSource\")]\n        public JoinDataComplete VideoSource = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 5 },\n            new JoinMetadata { Description = \"Device Video Source Set / Get\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Source Count\n        /// </summary>\n        [JoinName(\"SourceCount\")]\n        public JoinDataComplete SourceCount = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 5 },\n            new JoinMetadata { Description = \"Device Video Source Count\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        /// <summary>\n        /// Source Names\n        /// </summary>\n        [JoinName(\"SourceNames\")]\n        public JoinDataComplete SourceNames = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 5 },\n            new JoinMetadata { Description = \"Device Video Source Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public HdMdxxxCEControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(HdMdxxxCEControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected HdMdxxxCEControllerJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs",
    "content": "﻿using System;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n /// <summary>\n /// Represents a HdPsXxxControllerJoinMap\n /// </summary>\n\tpublic class HdPsXxxControllerJoinMap : JoinMapBaseAdvanced\n\t{\n\n\t\t#region Digital\n\n\t\t/// <summary>\n\t\t/// Enable Automatic Routing on Xx1 Switchers\n\t\t/// </summary>\n\t\t[JoinName(\"EnableAutoRoute\")]\n\t\tpublic JoinDataComplete EnableAutoRoute = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 1,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Enable Automatic Routing on Xx1 Switchers\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Device Input Sync\n\t\t/// </summary>\n\t\t[JoinName(\"InputSync\")]\n\t\tpublic JoinDataComplete InputSync = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 2,\n\t\t\t\tJoinSpan = 8\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Input Sync\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Device Enable Input Hdcp\n\t\t/// </summary>\n\t\t[JoinName(\"EnableInputHdcp\")]\n\t\tpublic JoinDataComplete EnableInputHdcp = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 11,\n\t\t\t\tJoinSpan = 8\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Enable Input Hdcp\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Device Disnable Input Hdcp\n\t\t/// </summary>\n\t\t[JoinName(\"DisableInputHdcp\")]\n\t\tpublic JoinDataComplete DisableInputHdcp = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 21,\n\t\t\t\tJoinSpan = 8\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Disnable Input Hdcp\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Device Onlne\n\t\t/// </summary>\n\t\t[JoinName(\"IsOnline\")]\n\t\tpublic JoinDataComplete IsOnline = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 30,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Onlne\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t#endregion\n\n\n\t\t#region Analog\n\n\t\t/// <summary>\n\t\t/// Device Input Route Set/Get\n\t\t/// </summary>\n\t\t[JoinName(\"OutputRoute\")]\n\t\tpublic JoinDataComplete OutputRoute = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 11,\n\t\t\t\tJoinSpan = 2\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Output Route Set/Get\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t#endregion\n\n\n\t\t#region Serial\n\n\t\t/// <summary>\n\t\t/// Device Name\n\t\t/// </summary>\n\t\t[JoinName(\"Name\")]\n\t\tpublic JoinDataComplete Name = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 1,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Name\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Device Input Name\n\t\t/// </summary>\n\t\t[JoinName(\"InputName\")]\n\t\tpublic JoinDataComplete InputName = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 2,\n\t\t\t\tJoinSpan = 8\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Input Name\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Device Output Name\n\t\t/// </summary>\n\t\t[JoinName(\"OutputName\")]\n\t\tpublic JoinDataComplete OutputName = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 11,\n\t\t\t\tJoinSpan = 2\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Output Name\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Device Output Route Name\n\t\t/// </summary>\n\t\t[JoinName(\"OutputRoutedName\")]\n\t\tpublic JoinDataComplete OutputRoutedName = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 16,\n\t\t\t\tJoinSpan = 2\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device Output Route Name\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\n\t\t#endregion\n\n\t\t/// <summary>\n\t\t/// Constructor to use when instantiating this join map without inheriting from it\n\t\t/// </summary>\n\t\t/// <param name=\"joinStart\">Join this join map will start at</param>\n\t\tpublic HdPsXxxControllerJoinMap(uint joinStart)\n\t\t\t: this(joinStart, typeof(HdPsXxxControllerJoinMap))\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor to use when extending this Join map\n\t\t/// </summary>\n\t\t/// <param name=\"joinStart\">Join this join map will start at</param>\n\t\t/// <param name=\"type\">Type of the child join map</param>\n\t\tprotected HdPsXxxControllerJoinMap(uint joinStart, Type type)\n\t\t\t: base(joinStart, type)\n\t\t{\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/Hrxxx0WirelessRemoteControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a Hrxxx0WirelessRemoteControllerJoinMap\n    /// </summary>\n    public class Hrxxx0WirelessRemoteControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Power\n        /// </summary>\n        [JoinName(\"Power\")]\n        public JoinDataComplete Power = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Power\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Menu\n        /// </summary>\n        [JoinName(\"Menu\")]\n        public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Menu\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Guide\n        /// </summary>\n        [JoinName(\"Guide\")]\n        public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Guide\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Info\n        /// </summary>\n        [JoinName(\"Info\")]\n        public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Info\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// VolumeUp\n        /// </summary>\n        [JoinName(\"VolumeUp\")]\n        public JoinDataComplete VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"VolumeUp\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// VolumeDown\n        /// </summary>\n        [JoinName(\"VolumeDown\")]\n        public JoinDataComplete VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"VolumeDown\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// DialPadUp\n        /// </summary>\n        [JoinName(\"DialPadUp\")]\n        public JoinDataComplete DialPadUp = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DialPadUp\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// DialPadDown\n        /// </summary>\n        [JoinName(\"DialPadDown\")]\n        public JoinDataComplete DialPadDown = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DialPadDown\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// DialPadLeft\n        /// </summary>\n        [JoinName(\"DialPadLeft\")]\n        public JoinDataComplete DialPadLeft = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DialPadLeft\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// DialPadRight\n        /// </summary>\n        [JoinName(\"DialPadRight\")]\n        public JoinDataComplete DialPadRight = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DialPadRight\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// DialPadSelect\n        /// </summary>\n        [JoinName(\"DialPadSelect\")]\n        public JoinDataComplete DialPadSelect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"DialPadSelect\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// ChannelUp\n        /// </summary>\n        [JoinName(\"ChannelUp\")]\n        public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"ChannelUp\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// ChannelDown\n        /// </summary>\n        [JoinName(\"ChannelDown\")]\n        public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"ChannelDown\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Mute\n        /// </summary>\n        [JoinName(\"Mute\")]\n        public JoinDataComplete Mute = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Mute\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Exit\n        /// </summary>\n        [JoinName(\"Exit\")]\n        public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Exit\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Last\n        /// </summary>\n        [JoinName(\"Last\")]\n        public JoinDataComplete Last = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Last\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Play\n        /// </summary>\n        [JoinName(\"Play\")]\n        public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Play\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Pause\n        /// </summary>\n        [JoinName(\"Pause\")]\n        public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Pause\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Rewind\n        /// </summary>\n        [JoinName(\"Rewind\")]\n        public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Rewind\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// FastForward\n        /// </summary>\n        [JoinName(\"FastForward\")]\n        public JoinDataComplete FastForward = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },\n            new JoinMetadata { Description = \"FastForward\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// PreviousTrack\n        /// </summary>\n        [JoinName(\"PreviousTrack\")]\n        public JoinDataComplete PreviousTrack = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },\n            new JoinMetadata { Description = \"PreviousTrack\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// NextTrack\n        /// </summary>\n        [JoinName(\"NextTrack\")]\n        public JoinDataComplete NextTrack = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },\n            new JoinMetadata { Description = \"NextTrack\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Stop\n        /// </summary>\n        [JoinName(\"Stop\")]\n        public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Stop\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Record\n        /// </summary>\n        [JoinName(\"Record\")]\n        public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Record\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Dvr\n        /// </summary>\n        [JoinName(\"Dvr\")]\n        public JoinDataComplete Dvr = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Dvr\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad1\n        /// </summary>\n        [JoinName(\"Keypad1\")]\n        public JoinDataComplete Keypad1 = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad1\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad2Abc\n        /// </summary>\n        [JoinName(\"Keypad2Abc\")]\n        public JoinDataComplete Keypad2 = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad2Abc\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad3Def\n        /// </summary>\n        [JoinName(\"Keypad3Def\")]\n        public JoinDataComplete Keypad3Def = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad3Def\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad4Ghi\n        /// </summary>\n        [JoinName(\"Keypad4Ghi\")]\n        public JoinDataComplete Keypad4Ghi = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad4Ghi\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad5Jkl\n        /// </summary>\n        [JoinName(\"Keypad5Jkl\")]\n        public JoinDataComplete Keypad5Jkl = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad5Jkl\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad6Mno\n        /// </summary>\n        [JoinName(\"Keypad6Mno\")]\n        public JoinDataComplete Keypad6Mno = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad6Mno\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad7Pqrs\n        /// </summary>\n        [JoinName(\"Keypad7Pqrs\")]\n        public JoinDataComplete Keypad7Pqrs = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad7Pqrs\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad8Tuv\n        /// </summary>\n        [JoinName(\"Keypad8Tuv\")]\n        public JoinDataComplete Keypad8Tuv = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad8Tuv\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad9Wxyz\n        /// </summary>\n        [JoinName(\"Keypad9Wxyz\")]\n        public JoinDataComplete Keypad9Wxyz = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad9Wxyz\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad0\n        /// </summary>\n        [JoinName(\"Keypad0\")]\n        public JoinDataComplete Keypad0 = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad0\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Clear\n        /// </summary>\n        [JoinName(\"Clear\")]\n        public JoinDataComplete Clear = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Clear\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Enter\n        /// </summary>\n        [JoinName(\"Enter\")]\n        public JoinDataComplete Enter = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Enter\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Red\n        /// </summary>\n        [JoinName(\"Red\")]\n        public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Red\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Green\n        /// </summary>\n        [JoinName(\"Green\")]\n        public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Green\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Yellow\n        /// </summary>\n        [JoinName(\"Yellow\")]\n        public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Yellow\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Blue\n        /// </summary>\n        [JoinName(\"Blue\")]\n        public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Blue\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom1\n        /// </summary>\n        [JoinName(\"Custom1\")]\n        public JoinDataComplete Custom1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom1\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom2\n        /// </summary>\n        [JoinName(\"Custom2\")]\n        public JoinDataComplete Custom2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom2\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom3\n        /// </summary>\n        [JoinName(\"Custom3\")]\n        public JoinDataComplete Custom3 = new JoinDataComplete(new JoinData { JoinNumber = 44, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom3\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom4\n        /// </summary>\n        [JoinName(\"Custom4\")]\n        public JoinDataComplete Custom4 = new JoinDataComplete(new JoinData { JoinNumber = 45, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom4\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom5\n        /// </summary>\n        [JoinName(\"Custom5\")]\n        public JoinDataComplete Custom5 = new JoinDataComplete(new JoinData { JoinNumber = 46, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom5\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom6\n        /// </summary>\n        [JoinName(\"Custom6\")]\n        public JoinDataComplete Custom6 = new JoinDataComplete(new JoinData { JoinNumber = 47, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom6\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom7\n        /// </summary>\n        [JoinName(\"Custom7\")]\n        public JoinDataComplete Custom7 = new JoinDataComplete(new JoinData { JoinNumber = 48, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom7\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom8\n        /// </summary>\n        [JoinName(\"Custom8\")]\n        public JoinDataComplete Custom8 = new JoinDataComplete(new JoinData { JoinNumber = 49, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom8\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Custom9\n        /// </summary>\n        [JoinName(\"Custom9\")]\n        public JoinDataComplete Custom9 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Custom9\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Fav\n        /// </summary>\n        [JoinName(\"Fav\")]\n        public JoinDataComplete Fav = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Fav\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Home\n        /// </summary>\n        [JoinName(\"Home\")]\n        public JoinDataComplete Home = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Home\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// BatteryLow\n        /// </summary>\n        [JoinName(\"BatteryLow\")]\n        public JoinDataComplete BatteryLow = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 },\n            new JoinMetadata { Description = \"BatteryLow\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// BatteryCritical\n        /// </summary>\n        [JoinName(\"BatteryCritical\")]\n        public JoinDataComplete BatteryCritical = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1 },\n            new JoinMetadata { Description = \"BatteryCritical\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// BatteryVoltage\n        /// </summary>\n        [JoinName(\"BatteryVoltage\")]\n        public JoinDataComplete BatteryVoltage = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"BatteryVoltage\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public Hrxxx0WirelessRemoteControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(Hrxxx0WirelessRemoteControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected Hrxxx0WirelessRemoteControllerJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/IAnalogInputJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a IAnalogInputJoinMap\n    /// </summary>\n    public class IAnalogInputJoinMap : JoinMapBaseAdvanced\n    {\n\n        /// <summary>\n        /// Input Value\n        /// </summary>\n        [JoinName(\"InputValue\")]\n        public JoinDataComplete InputValue = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Input Value\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n        \n        /// <summary>\n        /// Minimum Change\n        /// </summary>\n        [JoinName(\"MinimumChange\")]\n        public JoinDataComplete MinimumChange = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Minimum voltage change required to reflect a change\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public IAnalogInputJoinMap(uint joinStart)\n            : this(joinStart, typeof(IAnalogInputJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected IAnalogInputJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/IBasicCommunicationJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a IBasicCommunicationJoinMap\n    /// </summary>\n    public class IBasicCommunicationJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Text Received From Remote Device\n        /// </summary>\n        [JoinName(\"TextReceived\")]\n        public JoinDataComplete TextReceived = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Text Received From Remote Device\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Text Sent To Remote Device\n        /// </summary>\n        [JoinName(\"SendText\")]\n        public JoinDataComplete SendText = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Text Sent To Remote Device\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Set Port Config\n        /// </summary>\n        [JoinName(\"SetPortConfig\")]\n        public JoinDataComplete SetPortConfig = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Set Port Config\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Connect\n        /// </summary>\n        [JoinName(\"Connect\")]\n        public JoinDataComplete Connect = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Connect\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Disconnect\n        /// </summary>\n        [JoinName(\"Connected\")]\n        public JoinDataComplete Connected = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Connected\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Status\n        /// </summary>\n        [JoinName(\"Status\")]\n        public JoinDataComplete Status = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public IBasicCommunicationJoinMap(uint joinStart)\n            : this(joinStart, typeof(IBasicCommunicationJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected IBasicCommunicationJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalInputJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a IDigitalInputJoinMap\n    /// </summary>\n    public class IDigitalInputJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Input State\n        /// </summary>\n        [JoinName(\"InputState\")]\n        public JoinDataComplete InputState = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Input State\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public IDigitalInputJoinMap(uint joinStart)\n            : this(joinStart, typeof(IDigitalInputJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected IDigitalInputJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalOutputJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a IDigitalOutputJoinMap\n    /// </summary>\n    public class IDigitalOutputJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Output State\n        /// </summary>\n        [JoinName(\"OutputState\")]\n        public JoinDataComplete OutputState = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Get / Set state of Digital Input\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public IDigitalOutputJoinMap(uint joinStart)\n            : this(joinStart, typeof(IDigitalOutputJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected IDigitalOutputJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/IRBlurayBaseJoinMap.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Join map for IRBlurayBase devices\n    /// </summary>\n    public class IRBlurayBaseJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Power On\n        /// </summary>\n        [JoinName(\"PowerOn\")]\n        public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Power On\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Power Off\n        /// </summary>\n        [JoinName(\"PowerOff\")]\n        public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Power Off\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Power Toggle\n        /// </summary>\n        [JoinName(\"PowerToggle\")]\n        public JoinDataComplete PowerToggle = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Power Toggle\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Nav Up\n        /// </summary>\n        [JoinName(\"Up\")]\n        public JoinDataComplete Up = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Nav Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Nav Down\n        /// </summary>\n        [JoinName(\"Down\")]\n        public JoinDataComplete Down = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Nav Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Nav Left\n        /// </summary>\n        [JoinName(\"Left\")]\n        public JoinDataComplete Left = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Nav Left\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Nav Right\n        /// </summary>\n        [JoinName(\"Right\")]\n        public JoinDataComplete Right = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Nav Right\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Select\n        /// </summary>\n        [JoinName(\"Select\")]\n        public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Select\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Menu\n        /// </summary>\n        [JoinName(\"Menu\")]\n        public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Menu\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Exit\n        /// </summary>\n        [JoinName(\"Exit\")]\n        public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Exit\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 0\n        /// </summary>\n        [JoinName(\"Digit0\")]\n        public JoinDataComplete Digit0 = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 0\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 1\n        /// </summary>\n        [JoinName(\"Digit1\")]\n        public JoinDataComplete Digit1 = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 1\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 2\n        /// </summary>\n        [JoinName(\"Digit2\")]\n        public JoinDataComplete Digit2 = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 2\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 3\n        /// </summary>\n        [JoinName(\"Digit3\")]\n        public JoinDataComplete Digit3 = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 3\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 4\n        /// </summary>\n        [JoinName(\"Digit4\")]\n        public JoinDataComplete Digit4 = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 4\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 5\n        /// </summary>\n        [JoinName(\"Digit5\")]\n        public JoinDataComplete Digit5 = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 5\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 6\n        /// </summary>\n        [JoinName(\"Digit6\")]\n        public JoinDataComplete Digit6 = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 6\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 7\n        /// </summary>\n        [JoinName(\"Digit7\")]\n        public JoinDataComplete Digit7 = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 7\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 8\n        /// </summary>\n        [JoinName(\"Digit8\")]\n        public JoinDataComplete Digit8 = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 8\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Digit 9\n        /// </summary>\n        [JoinName(\"Digit9\")]\n        public JoinDataComplete Digit9 = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Digit 9\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad Clear\n        /// </summary>\n        [JoinName(\"KeypadClear\")]\n        public JoinDataComplete KeypadClear = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad Clear\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad Enter\n        /// </summary>\n        [JoinName(\"KeypadEnter\")]\n        public JoinDataComplete KeypadEnter = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad Enter\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Channel Up\n        /// </summary>\n        [JoinName(\"ChannelUp\")]\n        public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Channel Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Channel Down\n        /// </summary>\n        [JoinName(\"ChannelDown\")]\n        public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Channel Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Last Channel\n        /// </summary>\n        [JoinName(\"LastChannel\")]\n        public JoinDataComplete LastChannel = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Last Channel\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Guide\n        /// </summary>\n        [JoinName(\"Guide\")]\n        public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Guide\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Info\n        /// </summary>\n        [JoinName(\"Info\")]\n        public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Info\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Red\n        /// </summary>\n        [JoinName(\"Red\")]\n        public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Red\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Green\n        /// </summary>\n        [JoinName(\"Green\")]\n        public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Green\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Yellow\n        /// </summary>\n        [JoinName(\"Yellow\")]\n        public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Yellow\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Blue\n        /// </summary>\n        [JoinName(\"Blue\")]\n        public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Blue\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Play\n        /// </summary>\n        [JoinName(\"Play\")]\n        public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Play\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Pause\n        /// </summary>\n        [JoinName(\"Pause\")]\n        public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Pause\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Stop\n        /// </summary>\n        [JoinName(\"Stop\")]\n        public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Stop\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Fast Forward\n        /// </summary>\n        [JoinName(\"FFwd\")]\n        public JoinDataComplete FFwd = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },\n            new JoinMetadata { Description = \"FFwd\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Rewind\n        /// </summary>\n        [JoinName(\"Rewind\")]\n        public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Rewind\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Chapter Plus\n        /// </summary>\n        [JoinName(\"ChapPlus\")]\n        public JoinDataComplete ChapPlus = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Chapter Plus\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Chapter Minus\n        /// </summary>\n        [JoinName(\"ChapMinus\")]\n        public JoinDataComplete ChapMinus = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Chapter Minus\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Replay\n        /// </summary>\n        [JoinName(\"Replay\")]\n        public JoinDataComplete Replay = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Replay\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Record\n        /// </summary>\n        [JoinName(\"Record\")]\n        public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Record\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Has Keypad Accessory Button 1\n        /// </summary>\n        [JoinName(\"HasKeypadAccessoryButton1\")]\n        public JoinDataComplete HasKeypadAccessoryButton1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Has Keypad Accessory Button 1\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Has Keypad Accessory Button 2\n        /// </summary>\n        [JoinName(\"HasKeypadAccessoryButton2\")]\n        public JoinDataComplete HasKeypadAccessoryButton2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Has Keypad Accessory Button 2\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad Accessory Button 1 Press\n        /// </summary>\n        [JoinName(\"KeypadAccessoryButton1Press\")]\n        public JoinDataComplete KeypadAccessoryButton1Press = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 2 },\n            new JoinMetadata { Description = \"Keypad Accessory Button 1 Press\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad Accessory Button 2 Press\n        /// </summary>\n        [JoinName(\"KeypadAccessoryButton2Press\")]\n        public JoinDataComplete KeypadAccessoryButton2Press = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 2 },\n            new JoinMetadata { Description = \"Keypad Accessory Button 2 Press\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Keypad Accessory Button 1 Label\n        /// </summary>\n        [JoinName(\"KeypadAccessoryButton1Label\")]\n        public JoinDataComplete KeypadAccessoryButton1Label = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad Accessory Button 1 Label\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Keypad Accessory Button 2 Label\n        /// </summary>\n        [JoinName(\"KeypadAccessoryButton2Label\")]\n        public JoinDataComplete KeypadAccessoryButton2Label = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Keypad Accessory Button 1 Label\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n                /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public IRBlurayBaseJoinMap(uint joinStart)\n            : this(joinStart, typeof(IRBlurayBaseJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected IRBlurayBaseJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/PduJoinMapBase.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a PduJoinMapBase\n    /// </summary>\n    public class PduJoinMapBase : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// PDU Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"PDU Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// PDU Online Status\n        /// </summary>\n        [JoinName(\"Online\")]\n        public JoinDataComplete Online = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Number of Controlled Outlets\n        /// </summary>\n        [JoinName(\"OutletCount\")]\n        public JoinDataComplete OutletCount = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Number of COntrolled Outlets\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Outlet Name\n        /// </summary>\n        [JoinName(\"OutletName\")]\n        public JoinDataComplete OutletName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Outlet Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Outlet Enabled Status\n        /// </summary>\n        [JoinName(\"OutletEnabled\")]\n        public JoinDataComplete OutletEnabled = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Outlet Enabled\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Outlet Power State\n        /// </summary>\n        [JoinName(\"OutletPowerCycle\")]\n        public JoinDataComplete OutletPowerCycle = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Outlet Power Cycle\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Outlet Power On\n        /// </summary>\n        [JoinName(\"OutletPowerOn\")]\n        public JoinDataComplete OutletPowerOn = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Outlet Power On\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Outlet Power Off\n        /// </summary>\n        [JoinName(\"OutletPowerOff\")]\n        public JoinDataComplete OutletPowerOff = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Outlet Power Off\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n        \n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public PduJoinMapBase(uint joinStart)\n            :base(joinStart, typeof(PduJoinMapBase))\n        {   \n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        public PduJoinMapBase(uint joinStart, Type type)\n            : base(joinStart, type)\n        {            \n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/SetTopBoxControllerJoinMap.cs",
    "content": "﻿using System;\n\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a SetTopBoxControllerJoinMap\n    /// </summary>\n    public class SetTopBoxControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// STB Power On\n        /// </summary>\n        [JoinName(\"PowerOn\")]\n        public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Power On\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Power Off\n        /// </summary>\n        [JoinName(\"PowerOff\")]\n        public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Power Off\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Power Toggle\n        /// </summary>\n        [JoinName(\"PowerToggle\")]\n        public JoinDataComplete PowerToggle = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Power Toggle\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Has DPad\n        /// </summary>\n        [JoinName(\"HasDpad\")]\n        public JoinDataComplete HasDpad = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Has DPad\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Nav Up\n        /// </summary>\n        [JoinName(\"Up\")]\n        public JoinDataComplete Up = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Nav Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Nav Down\n        /// </summary>\n        [JoinName(\"Down\")]\n        public JoinDataComplete Down = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Nav Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Nav Left\n        /// </summary>\n        [JoinName(\"Left\")]\n        public JoinDataComplete Left = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Nav Left\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Nav Right\n        /// </summary>\n        [JoinName(\"Right\")]\n        public JoinDataComplete Right = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Nav Right\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Select\n        /// </summary>\n        [JoinName(\"Select\")]\n        public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Select\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Menu\n        /// </summary>\n        [JoinName(\"Menu\")]\n        public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Menu\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Exit\n        /// </summary>\n        [JoinName(\"Exit\")]\n        public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Exit\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Has Numeric\n        /// </summary>\n        [JoinName(\"HasNumeric\")]\n        public JoinDataComplete HasNumeric = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Has Numeric\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 0\n        /// </summary>\n        [JoinName(\"Digit0\")]\n        public JoinDataComplete Digit0 = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 0\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 1\n        /// </summary>\n        [JoinName(\"Digit1\")]\n        public JoinDataComplete Digit1 = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 1\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 2\n        /// </summary>\n        [JoinName(\"Digit2\")]\n        public JoinDataComplete Digit2 = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 2\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 3\n        /// </summary>\n        [JoinName(\"Digit3\")]\n        public JoinDataComplete Digit3 = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 3\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 4\n        /// </summary>\n        [JoinName(\"Digit4\")]\n        public JoinDataComplete Digit4 = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 4\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 5\n        /// </summary>\n        [JoinName(\"Digit5\")]\n        public JoinDataComplete Digit5 = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 5\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 6\n        /// </summary>\n        [JoinName(\"Digit6\")]\n        public JoinDataComplete Digit6 = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 6\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 7\n        /// </summary>\n        [JoinName(\"Digit7\")]\n        public JoinDataComplete Digit7 = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 7\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 8\n        /// </summary>\n        [JoinName(\"Digit8\")]\n        public JoinDataComplete Digit8 = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 8\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Digit 9\n        /// </summary>\n        [JoinName(\"Digit9\")]\n        public JoinDataComplete Digit9 = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Digit 9\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Dash\n        /// </summary>\n        [JoinName(\"Dash\")]\n        public JoinDataComplete Dash = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Dash\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Keypad Enter\n        /// </summary>\n        [JoinName(\"KeypadEnter\")]\n        public JoinDataComplete KeypadEnter = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Keypad Enter\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Channel Up\n        /// </summary>\n        [JoinName(\"ChannelUp\")]\n        public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Channel Up\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Channel Down\n        /// </summary>\n        [JoinName(\"ChannelDown\")]\n        public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Channel Down\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Last Channel\n        /// </summary>\n        [JoinName(\"LastChannel\")]\n        public JoinDataComplete LastChannel = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Last Channel\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Guide\n        /// </summary>\n        [JoinName(\"Guide\")]\n        public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Guide\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Info\n        /// </summary>\n        [JoinName(\"Info\")]\n        public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Info\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Red\n        /// </summary>\n        [JoinName(\"Red\")]\n        public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Red\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Green\n        /// </summary>\n        [JoinName(\"Green\")]\n        public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Green\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Yellow\n        /// </summary>\n        [JoinName(\"Yellow\")]\n        public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Yellow\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Blue\n        /// </summary>\n        [JoinName(\"Blue\")]\n        public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Blue\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Has DVR\n        /// </summary>\n        [JoinName(\"HasDvr\")]\n        public JoinDataComplete HasDvr = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Has DVR\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Dvr List\n        /// </summary>\n        [JoinName(\"DvrList\")]\n        public JoinDataComplete DvrList = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB DvrList\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Play\n        /// </summary>\n        [JoinName(\"Play\")]\n        public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Play\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Pause\n        /// </summary>\n        [JoinName(\"Pause\")]\n        public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Pause\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Stop\n        /// </summary>\n        [JoinName(\"Stop\")]\n        public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Stop\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB FFwd\n        /// </summary>\n        [JoinName(\"FFwd\")]\n        public JoinDataComplete FFwd = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB FFwd\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Rewind\n        /// </summary>\n        [JoinName(\"Rewind\")]\n        public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Rewind\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Chapter Plus\n        /// </summary>\n        [JoinName(\"ChapPlus\")]\n        public JoinDataComplete ChapPlus = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Chapter Plus\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Chapter Minus\n        /// </summary>\n        [JoinName(\"ChapMinus\")]\n        public JoinDataComplete ChapMinus = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Chapter Minus\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Replay\n        /// </summary>\n        [JoinName(\"Replay\")]\n        public JoinDataComplete Replay = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Replay\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Record\n        /// </summary>\n        [JoinName(\"Record\")]\n        public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Record\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Has Keypad Accessory Button 1\n        /// </summary>\n        [JoinName(\"HasKeypadAccessoryButton1\")]\n        public JoinDataComplete HasKeypadAccessoryButton1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Has Keypad Accessory Button 1\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Has Keypad Accessory Button 2\n        /// </summary>\n        [JoinName(\"HasKeypadAccessoryButton2\")]\n        public JoinDataComplete HasKeypadAccessoryButton2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Has Keypad Accessory Button 2\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Keypad Accessory Button 1 Press\n        /// </summary>\n        [JoinName(\"KeypadAccessoryButton1Press\")]\n        public JoinDataComplete KeypadAccessoryButton1Press = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 2 },\n            new JoinMetadata { Description = \"STB Keypad Accessory Button 1 Press\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Keypad Accessory Button 2 Press\n        /// </summary>\n        [JoinName(\"KeypadAccessoryButton2Press\")]\n        public JoinDataComplete KeypadAccessoryButton2Press = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 2 },\n            new JoinMetadata { Description = \"STB Keypad Accessory Button 2 Press\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Keypad Accessory Button 1 Label\n        /// </summary>\n        [JoinName(\"KeypadAccessoryButton1Label\")]\n        public JoinDataComplete KeypadAccessoryButton1Label = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Keypad Accessory Button 1 Label\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// STB Keypad Accessory Button 2 Label\n        /// </summary>\n        [JoinName(\"KeypadAccessoryButton2Label\")]\n        public JoinDataComplete KeypadAccessoryButton2Label = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Keypad Accessory Button 1 Label\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// STB Load Presets\n        /// </summary>\n        [JoinName(\"LoadPresets\")]\n        public JoinDataComplete LoadPresets = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Load Presets\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// STB Has Presets\n        /// </summary>\n        [JoinName(\"HasPresets\")]\n        public JoinDataComplete HasPresets = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },\n            new JoinMetadata { Description = \"STB Load Presets\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public SetTopBoxControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(SetTopBoxControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected SetTopBoxControllerJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/StatusSignControllerJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a StatusSignControllerJoinMap\n    /// </summary>\n    public class StatusSignControllerJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Status Sign Online\n        /// </summary>\n        [JoinName(\"IsOnline\")]\n        public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status Sign Online\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Status Sign Name\n        /// </summary>\n        [JoinName(\"Name\")]\n        public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status Sign Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Red LED Control\n        /// </summary>\n        [JoinName(\"RedControl\")]\n        public JoinDataComplete RedControl = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status Red LED Enable / Disable\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Red LED Intensity\n        /// </summary>\n        [JoinName(\"RedLed\")]\n        public JoinDataComplete RedLed = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status Red LED Intensity\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Green LED Control\n        /// </summary>\n        [JoinName(\"GreenControl\")]\n        public JoinDataComplete GreenControl = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status Green LED Enable / Disable\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Green LED Intensity\n        /// </summary>\n        [JoinName(\"GreenLed\")]\n        public JoinDataComplete GreenLed = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status Green LED Intensity\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Blue LED Control\n        /// </summary>\n        [JoinName(\"BlueControl\")]\n        public JoinDataComplete BlueControl = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status Blue LED Enable / Disable\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Blue LED Intensity\n        /// </summary>\n        [JoinName(\"BlueLed\")]\n        public JoinDataComplete BlueLed = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Status Blue LED Intensity\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public StatusSignControllerJoinMap(uint joinStart)\n            : this(joinStart, typeof(StatusSignControllerJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected StatusSignControllerJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/SystemMonitorJoinMap.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Bridges\n{\n    /// <summary>\n    /// Represents a SystemMonitorJoinMap\n    /// </summary>\n    public class SystemMonitorJoinMap : JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// Processor Timezone\n        /// </summary>\n        [JoinName(\"TimeZone\")]\n        public JoinDataComplete TimeZone = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Timezone\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });\n\n        /// <summary>\n        /// Processor Timezone Name\n        /// </summary>\n        [JoinName(\"TimeZoneName\")]\n        public JoinDataComplete TimeZoneName = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Timezone Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor OS Version\n        /// </summary>\n        [JoinName(\"IOControllerVersion\")]\n        public JoinDataComplete IOControllerVersion = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor IO Controller Version\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor SNMP App Version\n        /// </summary>\n        [JoinName(\"SnmpAppVersion\")]\n        public JoinDataComplete SnmpAppVersion = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor SNMP App Version\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor BACNet App Version\n        /// </summary>\n        [JoinName(\"BACnetAppVersion\")]\n        public JoinDataComplete BACnetAppVersion = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor BACNet App Version\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Controller Version\n        /// </summary>\n        [JoinName(\"ControllerVersion\")]\n        public JoinDataComplete ControllerVersion = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Controller Version\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Serial Number\n        /// </summary>\n        [JoinName(\"SerialNumber\")]\n        public JoinDataComplete SerialNumber = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Serial Number\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Model\n        /// </summary>\n        [JoinName(\"Model\")]\n        public JoinDataComplete Model = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Model\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Uptime\n        /// </summary>\n        [JoinName(\"Uptime\")]\n        public JoinDataComplete Uptime = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Uptime\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Last Boot Time\n        /// </summary>\n        [JoinName(\"LastBoot\")]\n        public JoinDataComplete LastBoot = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Last Boot\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Program Offset Join\n        /// </summary>\n        [JoinName(\"ProgramOffsetJoin\")]\n        public JoinDataComplete ProgramOffsetJoin = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 5 },\n            new JoinMetadata { Description = \"All Program Data is offset between slots by 5 - First Joins Start at 11\", JoinCapabilities = eJoinCapabilities.None, JoinType = eJoinType.None });\n\t\t\n        /// <summary>\n        /// Processor Program Start\n        /// </summary>\n        [JoinName(\"ProgramStart\")]\n        public JoinDataComplete ProgramStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program Start / Fb\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Processor Program Stop\n        /// </summary>\n        [JoinName(\"ProgramStop\")]\n        public JoinDataComplete ProgramStop = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program Stop / Fb\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Processor Program Register\n        /// </summary>\n        [JoinName(\"ProgramRegister\")]\n        public JoinDataComplete ProgramRegister = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program Register / Fb\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Processor Program Unregister\n        /// </summary>\n        [JoinName(\"ProgramUnregister\")]\n        public JoinDataComplete ProgramUnregister = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program UnRegister / Fb\", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Processor Program Name\n        /// </summary>\n        [JoinName(\"ProgramName\")]\n        public JoinDataComplete ProgramName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program Name\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Program Version\n        /// </summary>\n        [JoinName(\"ProgramCompiledTime\")]\n        public JoinDataComplete ProgramCompiledTime = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program Compile Time\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Program Crestron Database Version\n        /// </summary>\n        [JoinName(\"ProgramCrestronDatabaseVersion\")]\n        public JoinDataComplete ProgramCrestronDatabaseVersion = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program Database Version\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Program Environment Version\n        /// </summary>\n        [JoinName(\"ProgramEnvironmentVersion\")]\n        public JoinDataComplete ProgramEnvironmentVersion = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program Environment Version\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Program Aggregate Info\n        /// </summary>\n        [JoinName(\"AggregatedProgramInfo\")]\n        public JoinDataComplete AggregatedProgramInfo = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Program Aggregate Info Json\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Ethernet Offset Join\n        /// </summary>\n        [JoinName(\"EthernetOffsetJoin\")]\n        public JoinDataComplete EthernetOffsetJoin = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n            new JoinMetadata { Description = \"All Ethernet Data is offset between Nics by 5 - First Joins Start at 76\", JoinCapabilities = eJoinCapabilities.None, JoinType = eJoinType.None });\n\n        /// <summary>\n        /// Processor Ethernet Hostname\n        /// </summary>\n        [JoinName(\"HostName\")]\n        public JoinDataComplete HostName = new JoinDataComplete(new JoinData { JoinNumber = 76, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Hostname\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Current Ip Address\n        /// </summary>\n        [JoinName(\"CurrentIpAddress\")]\n        public JoinDataComplete CurrentIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 77, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Current Ip Address\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Current Subnet Mask\n        /// </summary>\n        [JoinName(\"CurrentSubnetMask\")]\n        public JoinDataComplete CurrentSubnetMask = new JoinDataComplete(new JoinData { JoinNumber = 78, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Current Subnet Mask\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Current Default Gateway\n        /// </summary>\n        [JoinName(\"CurrentDefaultGateway\")]\n        public JoinDataComplete CurrentDefaultGateway = new JoinDataComplete(new JoinData { JoinNumber = 79, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Current Default Gateway\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Static Ip Address\n        /// </summary>\n        [JoinName(\"StaticIpAddress\")]\n        public JoinDataComplete StaticIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 80, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Static Ip Address\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Static Subnet Mask\n        /// </summary>\n        [JoinName(\"StaticSubnetMask\")]\n        public JoinDataComplete StaticSubnetMask = new JoinDataComplete(new JoinData { JoinNumber = 81, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Static Subnet Mask\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Static Default Gateway\n        /// </summary>\n        [JoinName(\"StaticDefaultGateway\")]\n        public JoinDataComplete StaticDefaultGateway = new JoinDataComplete(new JoinData { JoinNumber = 82, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Static Default Gateway\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Domain\n        /// </summary>\n        [JoinName(\"Domain\")]\n        public JoinDataComplete Domain = new JoinDataComplete(new JoinData { JoinNumber = 83, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Domain\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Dns Server\n        /// </summary>\n        [JoinName(\"DnsServer\")]\n        public JoinDataComplete DnsServer = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Dns Server\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Mac Address\n        /// </summary>\n        [JoinName(\"MacAddress\")]\n        public JoinDataComplete MacAddress = new JoinDataComplete(new JoinData { JoinNumber = 85, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Mac Address\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Ethernet Dhcp Status\n        /// </summary>\n        [JoinName(\"DhcpStatus\")]\n        public JoinDataComplete DhcpStatus = new JoinDataComplete(new JoinData { JoinNumber = 86, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Processor Ethernet Dhcp Status\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Reboot\n        /// </summary>\n\t\t[JoinName(\"ProcessorRebot\")]\n\t\tpublic JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n\t\t\tnew JoinMetadata { Description = \"Reboot processor\", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Is Appliance Fb\n        /// </summary>\n\t\t[JoinName(\"IsAppliance\")]\n\t\tpublic JoinDataComplete IsAppliance = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n\t\t\tnew JoinMetadata { Description = \"Is appliance Fb\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Is Server Fb\n        /// </summary>\n\t\t[JoinName(\"IsServer\")]\n\t\tpublic JoinDataComplete IsServer = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n\t\t\tnew JoinMetadata { Description = \"Is server Fb\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Program Reset\n        /// </summary>\n\t\t[JoinName(\"ProgramReset\")]\n\t\tpublic JoinDataComplete ProgramReset = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },\n\t\t\tnew JoinMetadata { Description = \"Resets the program\", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });\n\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public SystemMonitorJoinMap(uint joinStart)\n            : this(joinStart, typeof(SystemMonitorJoinMap))\n        {\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        protected SystemMonitorJoinMap(uint joinStart, Type type)\n            : base(joinStart, type)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs",
    "content": "using System;\nusing PepperDash.Essentials.Core;\nnamespace PepperDash.Essentials.Core.Bridges.JoinMaps\n{\n /// <summary>\n /// Represents a VideoCodecControllerJoinMap\n /// </summary>\n\tpublic class VideoCodecControllerJoinMap : JoinMapBaseAdvanced\n\t{\n\t\t#region Digital\n\n\t\t/// <summary>\n\t\t/// Device is Online\n\t\t/// </summary>\n\t\t[JoinName(\"IsOnline\")]\n\t\tpublic JoinDataComplete IsOnline = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 1,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Device is Online\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// If High, will send DTMF tones to the call set by SelectCall analog.  If low sends DTMF tones to last connected call.\n\t\t/// </summary>\n        [JoinName(\"SendDtmfToSpecificCallIndex\")]\n        public JoinDataComplete SendDtmfToSpecificCallIndex = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 10,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"If High, will send DTMF tones to the call set by SelectCall analog.  If low sends DTMF tones to last connected call.\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// DTMF 1\n\t\t/// </summary>\n        [JoinName(\"Dtmf1\")]\n\t\tpublic JoinDataComplete Dtmf1 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 11,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 1\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 2\n\t\t/// </summary>\n        [JoinName(\"Dtmf2\")]\n\t\tpublic JoinDataComplete Dtmf2 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 12,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 2\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 3\n\t\t/// </summary>\n        [JoinName(\"Dtmf3\")]\n\t\tpublic JoinDataComplete Dtmf3 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 13,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 3\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 4\n\t\t/// </summary>\n        [JoinName(\"Dtmf4\")]\n\t\tpublic JoinDataComplete Dtmf4 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 14,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 4\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 5\n\t\t/// </summary>\n        [JoinName(\"Dtmf5\")]\n\t\tpublic JoinDataComplete Dtmf5 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 15,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 5\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 6\n\t\t/// </summary>\n        [JoinName(\"Dtmf6\")]\n\t\tpublic JoinDataComplete Dtmf6 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 16,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 6\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 7\n\t\t/// </summary>\n        [JoinName(\"Dtmf7\")]\n\t\tpublic JoinDataComplete Dtmf7 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 17,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 7\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 8\n\t\t/// </summary>\n        [JoinName(\"Dtmf8\")]\n\t\tpublic JoinDataComplete Dtmf8 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 18,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 8\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 9\n\t\t/// </summary>\n        [JoinName(\"Dtmf9\")]\n\t\tpublic JoinDataComplete Dtmf9 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 19,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 9\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF 0\n\t\t/// </summary>\n        [JoinName(\"Dtmf0\")]\n\t\tpublic JoinDataComplete Dtmf0 = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 20,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF 0\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF *\n\t\t/// </summary>\n        [JoinName(\"DtmfStar\")]\n\t\tpublic JoinDataComplete DtmfStar = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 21,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF *\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// DTMF #\n\t\t/// </summary>\n        [JoinName(\"DtmfPound\")]\n\t\tpublic JoinDataComplete DtmfPound = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 22,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"DTMF #\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// End All Calls\n\t\t/// </summary>\n        [JoinName(\"EndAllCalls\")]\n\t\tpublic JoinDataComplete EndAllCalls = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 24,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"End All Calls\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Current Hook State\n\t\t/// </summary>\n\t\t[JoinName(\"HookState\")]\n\t\tpublic JoinDataComplete HookState = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 31,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Current Hook State\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Speed Dial\n\t\t/// </summary>\n\t\t[JoinName(\"SpeedDialStart\")]\n\t\tpublic JoinDataComplete SpeedDialStart = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 41,\n\t\t\t\tJoinSpan = 4\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Speed Dial\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Incoming Call\n\t\t/// </summary>\n\t\t[JoinName(\"IncomingCall\")]\n\t\tpublic JoinDataComplete IncomingCall = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 50,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Incoming Call\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Answer Incoming Call\n\t\t/// </summary>\n\t\t[JoinName(\"IncomingAnswer\")]\n\t\tpublic JoinDataComplete IncomingAnswer = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 51,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Answer Incoming Call\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Reject Incoming Call\n\t\t/// </summary>\n\t\t[JoinName(\"IncomingReject\")]\n\t\tpublic JoinDataComplete IncomingReject = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 52,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Reject Incoming Call\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Manual Dial\n\t\t/// </summary>\n\t\t[JoinName(\"ManualDial\")]\n\t\tpublic JoinDataComplete ManualDial = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 71,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Dial manual string specified by CurrentDialString serial join\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Dial Phone\n\t\t/// </summary>\n        [JoinName(\"DialPhone\")]\n\t\tpublic JoinDataComplete DialPhone = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 72,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Dial Phone\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Phone Hook State\n\t\t/// </summary>\n\t\t[JoinName(\"PhoneHookState\")]\n\t\tpublic JoinDataComplete PhoneHookState = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 72,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Dial Phone\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Hang Up Phone\n\t\t/// </summary>\n        [JoinName(\"HangUpPhone\")]\n\t\tpublic JoinDataComplete HangUpPhone = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 73,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Hang Up Phone\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// End Call\n\t\t/// </summary>\n        [JoinName(\"EndCallStart\")]\n        public JoinDataComplete EndCallStart = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 81,\n                JoinSpan = 8\n            },\n            new JoinMetadata\n            {\n                Description = \"End a specific call by call index. \",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Join All Calls\n\t\t/// </summary>\n        [JoinName(\"JoinAllCalls\")]\n        public JoinDataComplete JoinAllCalls = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 90,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Join all calls\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Join Call\n\t\t/// </summary>\n        [JoinName(\"JoinCallStart\")]\n        public JoinDataComplete JoinCallStart = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 91,\n                JoinSpan = 8\n            },\n            new JoinMetadata\n            {\n                Description = \"Join a specific call by call index. \",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Directory Search Busy\n\t\t/// </summary>\n\t\t[JoinName(\"DirectorySearchBusy\")]\n\t\tpublic JoinDataComplete DirectorySearchBusy = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 100,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory Search Busy FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Selected Entry Is Contact\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryEntryIsContact\")]\n\t\tpublic JoinDataComplete DirectoryEntryIsContact = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 101,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory Selected Entry Is Contact FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Line Selected\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryLineSelected\")]\n\t\tpublic JoinDataComplete DirectoryLineSelected = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 101,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory Line Selected FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Is Root\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryIsRoot\")]\n\t\tpublic JoinDataComplete DirectoryIsRoot = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 102,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory is on Root FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Has Changed\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryHasChanged\")]\n\t\tpublic JoinDataComplete DirectoryHasChanged = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 103,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory has changed FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Go to Root\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryRoot\")]\n\t\tpublic JoinDataComplete DirectoryRoot = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 104,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Go to Directory Root\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Go Back One Level\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryFolderBack\")]\n\t\tpublic JoinDataComplete DirectoryFolderBack = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 105,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Go back one directory level\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Dial Selected Line\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryDialSelectedLine\")]\n\t\tpublic JoinDataComplete DirectoryDialSelectedLine = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 106,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Dial selected directory line\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Disable Auto Dial Selected Line\n\t\t/// </summary>\n        [JoinName(\"DirectoryDisableAutoDialSelectedLine\")]\n        public JoinDataComplete DirectoryDisableAutoDialSelectedLine = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 107,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Set high to disable automatic dialing of a contact when selected\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Directory Dial Selected Contact Method\n\t\t/// </summary>\n        [JoinName(\"DirectoryDialSelectedContactMethod\")]\n        public JoinDataComplete DirectoryDialSelectedContactMethod = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 108,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Pulse to dial the selected contact method\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Directory Clear Selected\n\t\t/// </summary>\n        [JoinName(\"DirectoryClearSelected\")]\n        public JoinDataComplete DirectoryClearSelected = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 110,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Clear Selected Entry and String from Search\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Camera Tilt Up\n\t\t/// </summary>\n\t\t[JoinName(\"CameraTiltUp\")]\n\t\tpublic JoinDataComplete CameraTiltUp = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 111,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Tilt Up\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Tilt Down\n\t\t/// </summary>\n\t\t[JoinName(\"CameraTiltDown\")]\n\t\tpublic JoinDataComplete CameraTiltDown = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 112,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Tilt Down\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Pan Left\n\t\t/// </summary>\n\t\t[JoinName(\"CameraPanLeft\")]\n\t\tpublic JoinDataComplete CameraPanLeft = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 113,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Pan Left\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Pan Right\n\t\t/// </summary>\n\t\t[JoinName(\"CameraPanRight\")]\n\t\tpublic JoinDataComplete CameraPanRight = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 114,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Pan Right\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Zoom In\n\t\t/// </summary>\n\t\t[JoinName(\"CameraZoomIn\")]\n\t\tpublic JoinDataComplete CameraZoomIn = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 115,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Zoom In\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Zoom Out\n\t\t/// </summary>\n\t\t[JoinName(\"CameraZoomOut\")]\n\t\tpublic JoinDataComplete CameraZoomOut = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 116,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Zoom Out\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Focus Near\n\t\t/// </summary>\n        [JoinName(\"CameraFocusNear\")]\n        public JoinDataComplete CameraFocusNear = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 117,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Camera Focus Near\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Camera Focus Far\n\t\t/// </summary>\n        [JoinName(\"CameraFocusFar\")]\n        public JoinDataComplete CameraFocusFar = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 118,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Camera Focus Far\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Camera Auto Focus\n\t\t/// </summary>\n        [JoinName(\"CameraFocusAuto\")]\n        public JoinDataComplete CameraFocusAuto = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 119,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Camera Auto Focus Trigger\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Camera Preset Save\n\t\t/// </summary>\n\t\t[JoinName(\"CameraPresetSave\")]\n\t\tpublic JoinDataComplete CameraPresetSave = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 121,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Pulse to save selected preset spcified by CameraPresetSelect analog join.  FB will pulse for 3s when preset saved.\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Preset Recall\n\t\t/// </summary>\n\t\t[JoinName(\"CameraModeAuto\")]\n\t\tpublic JoinDataComplete CameraModeAuto = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 131,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Mode Auto.  Enables camera auto tracking mode, with feedback\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Mode Manual\n\t\t/// </summary>\n\t\t[JoinName(\"CameraModeManual\")]\n\t\tpublic JoinDataComplete CameraModeManual = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 132,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Mode Manual.  Disables camera auto tracking mode, with feedback\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Mode Off\n\t\t/// </summary>\n\t\t[JoinName(\"CameraModeOff\")]\n\t\tpublic JoinDataComplete CameraModeOff = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 133,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Mode Off.  Disables camera video, with feedback. Works like video mute.\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Self View\n\t\t/// </summary>\n\t\t[JoinName(\"CameraSelfView\")]\n\t\tpublic JoinDataComplete CameraSelfView = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 141,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Self View Toggle/FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Layout\n\t\t/// </summary>\n\t\t[JoinName(\"CameraLayout\")]\n\t\tpublic JoinDataComplete CameraLayout = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 142,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Layout Toggle\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Supports Auto Mode\n\t\t/// </summary>\n\t\t[JoinName(\"CameraSupportsAutoMode\")]\n\t\tpublic JoinDataComplete CameraSupportsAutoMode = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 143,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Supports Auto Mode FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Supports Off Mode\n\t\t/// </summary>\n\t\t[JoinName(\"CameraSupportsOffMode\")]\n\t\tpublic JoinDataComplete CameraSupportsOffMode = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 144,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Supports Off Mode FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Update Meetings\n\t\t/// </summary>\n\t\t[JoinName(\"UpdateMeetings\")]\n\t\tpublic JoinDataComplete UpdateMeetings = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 160,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Update Meetings\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Dial Meeting Start\n\t\t/// </summary>\n        [JoinName(\"DialMeetingStart\")]\n\t\tpublic JoinDataComplete DialMeetingStart = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 161,\n\t\t\t\tJoinSpan = 10\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Join meeting\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Mic Mute On\n\t\t/// </summary>\n\t\t[JoinName(\"MicMuteOn\")]\n\t\tpublic JoinDataComplete MicMuteOn = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 171,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Mic Mute On\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Mic Mute Off\n\t\t/// </summary>\n\t\t[JoinName(\"MicMuteOff\")]\n\t\tpublic JoinDataComplete MicMuteOff = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 172,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Mic Mute Off\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Mic Mute Toggle\n\t\t/// </summary>\n\t\t[JoinName(\"MicMuteToggle\")]\n\t\tpublic JoinDataComplete MicMuteToggle = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 173,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Mic Mute Toggle\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Volume Up\n\t\t/// </summary>\n\t\t[JoinName(\"VolumeUp\")]\n\t\tpublic JoinDataComplete VolumeUp = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 174,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Volume Up\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Volume Down\n\t\t/// </summary>\n\t\t[JoinName(\"VolumeDown\")]\n\t\tpublic JoinDataComplete VolumeDown = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 175,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Volume Down\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Volume Mute On\n\t\t/// </summary>\n\t\t[JoinName(\"VolumeMuteOn\")]\n\t\tpublic JoinDataComplete VolumeMuteOn = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 176,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Volume Mute On\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Volume Mute Off\n\t\t/// </summary>\n\t\t[JoinName(\"VolumeMuteOff\")]\n\t\tpublic JoinDataComplete VolumeMuteOff = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 177,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Volume Mute Off\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Volume Mute Toggle\n\t\t/// </summary>\n\t\t[JoinName(\"VolumeMuteToggle\")]\n\t\tpublic JoinDataComplete VolumeMuteToggle = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 178,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Volume Mute Toggle\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Remove Selected Recent Call Item\n\t\t/// </summary>\n        [JoinName(\"RemoveSelectedRecentCallItem\")]\n        public JoinDataComplete RemoveSelectedRecentCallItem = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 181,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Pulse to remove the selected recent call item specified by the SelectRecentCallItem analog join\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Dial Selected Recent Call Item\n\t\t/// </summary>\n        [JoinName(\"DialSelectedRecentCallItem\")]\n        public JoinDataComplete DialSelectedRecentCallItem = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 182,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Pulse to dial the selected recent call item specified by the SelectRecentCallItem analog join\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Source Share Start\n\t\t/// </summary>\n\t\t[JoinName(\"SourceShareStart\")]\n\t\tpublic JoinDataComplete SourceShareStart = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 201,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Start Sharing & Feedback\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Source Share End\n\t\t/// </summary>\n\t\t[JoinName(\"SourceShareEnd\")]\n\t\tpublic JoinDataComplete SourceShareEnd = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 202,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Stop Sharing & Feedback\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Source Share Auto Start\n\t\t/// </summary>\n\t\t[JoinName(\"AutoShareWhileInCall\")]\n\t\tpublic JoinDataComplete SourceShareAutoStart = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 203,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"When high, will autostart sharing when a call is joined\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Recieving Content\n\t\t/// </summary>\n\t\t[JoinName(\"RecievingContent\")]\n\t\tpublic JoinDataComplete RecievingContent = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 204,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Recieving content from the far end\",\n\t\t\t\tJoinType = eJoinType.Digital,\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Selfview Position\n\t\t/// </summary>\n\t\t[JoinName(\"SelfviewPosition\")]\n\t\tpublic JoinDataComplete SelfviewPosition = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 211,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Toggles selfview position\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Hold All Calls\n\t\t/// </summary>\n        [JoinName(\"HoldAllCalls\")]\n        public JoinDataComplete HoldAllCalls = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 220,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Holds all calls\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Hold Call at Index\n\t\t/// </summary>\n        [JoinName(\"HoldCallsStart\")]\n        public JoinDataComplete HoldCallsStart = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 221,\n                JoinSpan = 8\n            },\n            new JoinMetadata\n            {\n                Description = \"Holds Call at specified index. FB reported on Call Status XSIG\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Resume All Calls\n\t\t/// </summary>\n        [JoinName(\"ResumeCallsStart\")]\n        public JoinDataComplete ResumeCallsStart = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 231,\n                JoinSpan = 8\n            },\n            new JoinMetadata\n            {\n                Description = \"Resume Call at specified index\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Multi Site Option Is Enabled\n\t\t/// </summary>\n        [JoinName(\"MultiSiteOptionIsEnabled\")]\n        public JoinDataComplete MultiSiteOptionIsEnabled = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 301,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Multi site option is enabled FB\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Auto Answer Enabled\n\t\t/// </summary>\n        [JoinName(\"AutoAnswerEnabled\")]\n        public JoinDataComplete AutoAnswerEnabled = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 302,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Auto Answer is enabled FB\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Participant Audio Mute Toggle\n\t\t/// </summary>\n        [JoinName(\"ParticipantAudioMuteToggleStart\")]\n        public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete(\n\t\t\tnew JoinData \n\t\t\t{ \n\t\t\t\tJoinNumber = 501,\n\t\t\t\tJoinSpan = 50 \n\t\t\t},\n            new JoinMetadata\n            {\n                Description = \"Toggles the participant's audio mute status\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Participant Video Mute Toggle\n\t\t/// </summary>\n        [JoinName(\"ParticipantVideoMuteToggleStart\")]\n        public JoinDataComplete ParticipantVideoMuteToggleStart = new JoinDataComplete(\n\t\t\tnew JoinData \n\t\t\t{ \n\t\t\t\tJoinNumber = 801, \n\t\t\t\tJoinSpan = 50 \n\t\t\t},\n            new JoinMetadata\n            {\n                Description = \"Toggles the participant's video mute status\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\t\t/// <summary>\n\t\t/// Participant Pin Toggle\n\t\t/// </summary>\n        [JoinName(\"ParticipantPinToggleStart\")]\n        public JoinDataComplete ParticipantPinToggleStart = new JoinDataComplete(\n\t\t\tnew JoinData \n\t\t\t{ \n\t\t\t\tJoinNumber = 1101, \n\t\t\t\tJoinSpan = 50 \n\t\t\t},\n            new JoinMetadata\n            {\n                Description = \"Toggles the participant's pin status\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\n\t\t#endregion\n\n\n\n\t\t#region Analog\n\n        // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set\n\n\t\t/// <summary>\n\t\t/// Meetings To Display\n\t\t/// </summary>\t\t\n        [JoinName(\"MeetingsToDisplay\")]\n        public JoinDataComplete MeetingsToDisplay = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 40,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Set/FB the number of meetings to display via the bridge xsig; default: 3 meetings.\",\n                JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Select Call\n\t\t/// </summary>\n        [JoinName(\"SelectCall\")]\n        public JoinDataComplete SelectCall = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 24,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Sets the selected Call for DTMF commands. Valid values 1-8\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Connected Call Count\n\t\t/// </summary>\n        [JoinName(\"ConnectedCallCount\")]\n        public JoinDataComplete ConnectedCallCount = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 25,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Reports the number of currently connected calls\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Minutes Before Meeting Start\n\t\t/// </summary>\n\t\t[JoinName(\"MinutesBeforeMeetingStart\")]\n\t\tpublic JoinDataComplete MinutesBeforeMeetingStart = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 41,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Minutes before meeting start that a meeting is joinable\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Number Select\n\t\t/// </summary>\n\t\t[JoinName(\"CameraNumberSelect\")]\n\t\tpublic JoinDataComplete CameraNumberSelect = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 60,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Number Select/FB.  1 based index.  Valid range is 1 to the value reported by CameraCount.\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Count\n\t\t/// </summary>\n        [JoinName(\"CameraCount\")]\n        public JoinDataComplete CameraCount = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 61,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Reports the number of cameras\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Directory Row Count\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryRowCount\")]\n\t\tpublic JoinDataComplete DirectoryRowCount = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 101,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory Row Count FB\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Select Row\n\t\t/// </summary>\n\t\t[JoinName(\"DirectorySelectRow\")]\n\t\tpublic JoinDataComplete DirectorySelectRow = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 101,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory Select Row and Feedback\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Selected Contact Method Count\n\t\t/// </summary>\n        [JoinName(\"SelectedContactMethodCount\")]\n        public JoinDataComplete SelectedContactMethodCount = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 102,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Reports the number of contact methods for the selected contact\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Select Contact Method\n\t\t/// </summary>\n        [JoinName(\"SelectContactMethod\")]\n        public JoinDataComplete SelectContactMethod = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 103,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Selects a contact method by index\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Directory Select Row Feedback\n\t\t/// </summary>\n        [JoinName(\"DirectorySelectRowFeedback\")]\n        public JoinDataComplete DirectorySelectRowFeedback = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 104,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Directory Select Row and Feedback\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\n\t\t/// <summary>\n\t\t/// Camera Preset Select\n\t\t/// </summary>\n\t\t[JoinName(\"CameraPresetSelect\")]\n\t\tpublic JoinDataComplete CameraPresetSelect = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 121,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Preset Select\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Far End Preset Select\n\t\t/// </summary>\n        [JoinName(\"FarEndPresetSelect\")]\n        public JoinDataComplete FarEndPresetSelect = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 122,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Far End Preset Preset Select\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Participant Count\n\t\t/// </summary>\n\t\t[JoinName(\"ParticipantCount\")]\n\t\tpublic JoinDataComplete ParticipantCount = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 151,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Current Participant Count\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Meeting Count\n\t\t/// </summary>\n\t\t[JoinName(\"Meeting Count Fb\")]\n\t\tpublic JoinDataComplete MeetingCount = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 161,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Meeting Count\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Volume Level\n\t\t/// </summary>\n\t\t[JoinName(\"VolumeLevel\")]\n\t\tpublic JoinDataComplete VolumeLevel = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 174,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Volume Level\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n\t\t\t\tJoinType = eJoinType.Analog\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Select Recent Call Item\n\t\t/// </summary>\n        [JoinName(\"SelectRecentCallItem\")]\n        public JoinDataComplete SelectRecentCallItem = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 180,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Select/FB for Recent Call Item.  Valid values 1 - 10\",\n                JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Recent Call Occurrence Type\n\t\t/// </summary>\n        [JoinName(\"RecentCallOccurrenceType\")]\n        public JoinDataComplete RecentCallOccurrenceType = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 181,\n                JoinSpan = 10\n            },\n            new JoinMetadata\n            {\n                Description = \"Recent Call Occurrence Type. [0-3] 0 = Unknown, 1 = Placed, 2 = Received, 3 = NoAnswer\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t/// <summary>\n\t\t/// Recent Call Count\n\t\t/// </summary>\n        [JoinName(\"RecentCallCount\")]\n        public JoinDataComplete RecentCallCount = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 191,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Recent Call Count\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Analog\n            });\n\n\t\t#endregion\n\n\n\n\t\t#region Serials\n\n\t\t/// <summary>\n\t\t/// Current Dial String\n\t\t/// </summary>\n\t\t[JoinName(\"CurrentDialString\")]\n\t\tpublic JoinDataComplete CurrentDialString = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 1,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Value to dial when ManualDial digital join is pulsed\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Phone Dial String\n\t\t/// </summary>\n        [JoinName(\"PhoneDialString\")]\n\t\tpublic JoinDataComplete PhoneDialString = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 2,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Phone Dial String\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Current Call Data\n\t\t/// </summary>\n        [JoinName(\"CurrentCallData\")]\n\t\tpublic JoinDataComplete CurrentCallData = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 2,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Current Call Data - XSIG\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Call Direction\n\t\t/// </summary>\n\t\t[JoinName(\"CallDirection\")]\n\t\tpublic JoinDataComplete CallDirection = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 22,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Current Call Direction\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Incoming Call Name\n\t\t/// </summary>\n\t\t[JoinName(\"IncomingCallName\")]\n\t\tpublic JoinDataComplete IncomingCallName = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 51,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Incoming Call Name\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Incoming Call Number\n\t\t/// </summary>\n\t\t[JoinName(\"IncomingCallNumber\")]\n\t\tpublic JoinDataComplete IncomingCallNumber = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 52,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Incoming Call Number\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Search String\n\t\t/// </summary>\n\t\t[JoinName(\"DirectorySearchString\")]\n\t\tpublic JoinDataComplete DirectorySearchString = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 100,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory Search String\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Directory Entries\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryEntries\")]\n\t\tpublic JoinDataComplete DirectoryEntries = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 101,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Directory Entries - XSig, 255 entries\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Schedule Data\n\t\t/// </summary>\n\t\t[JoinName(\"Schedule\")]\n\t\tpublic JoinDataComplete Schedule = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 102,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Schedule Data - XSIG\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Contact Methods\n\t\t/// </summary>\n        [JoinName(\"ContactMethods\")]\n        public JoinDataComplete ContactMethods = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 103,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Contact Methods - XSig, 10 entries\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// Camera Preset Names\n\t\t/// </summary>\n\t\t[JoinName(\"CameraPresetNames\")]\n\t\tpublic JoinDataComplete CameraPresetNames = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 121,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Camera Preset Names - XSIG, max of 15\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Current Layout String\n\t\t/// </summary>\n        [JoinName(\"CurrentLayoutStringFb\")]\n\t\tpublic JoinDataComplete CurrentLayoutStringFb = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 141,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Current Layout Fb\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Available Layouts XSig\n\t\t/// </summary>\n        [JoinName(\"AvailableLayoutsFb\")]\n        public JoinDataComplete AvailableLayoutsFb = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 142,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"xSig of all available layouts\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// Select Layout\n\t\t/// </summary>\n        [JoinName(\"SelectLayout\")]\n        public JoinDataComplete SelectLayout = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 142,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"Select Layout by string\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\n\t\t/// <summary>\n\t\t/// Current Participants XSig\n\t\t/// </summary>\n\t\t[JoinName(\"CurrentParticipants\")]\n\t\tpublic JoinDataComplete CurrentParticipants = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 151,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata()\n\t\t\t{\n\t\t\t\tDescription = \"Current Participants XSig\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Camera Names XSig\n\t\t/// </summary>\n        [JoinName(\"CameraNamesFb\")]\n        public JoinDataComplete CameraNamesFb = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 161,\n                JoinSpan = 10\n            },\n            new JoinMetadata\n            {\n                Description = \"Camera Name Fb\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// Selected Recent Call Name\n\t\t/// </summary>\n        [JoinName(\"SelectedRecentCallName\")]\n        public JoinDataComplete SelectedRecentCallName = new JoinDataComplete(\n         new JoinData\n         {\n             JoinNumber = 171,\n             JoinSpan = 1\n         },\n         new JoinMetadata\n         {\n             Description = \"Selected Recent Call Name\",\n             JoinCapabilities = eJoinCapabilities.ToSIMPL,\n             JoinType = eJoinType.Serial\n         });\n\n\t\t/// <summary>\n\t\t/// Selected Recent Call Number\n\t\t/// </summary>\n        [JoinName(\"SelectedRecentCallNumber\")]\n        public JoinDataComplete SelectedRecentCallNumber = new JoinDataComplete(\n         new JoinData\n         {\n             JoinNumber = 172,\n             JoinSpan = 1\n         },\n         new JoinMetadata\n         {\n             Description = \"Selected Recent Call Number\",\n             JoinCapabilities = eJoinCapabilities.ToSIMPL,\n             JoinType = eJoinType.Serial\n         });\n\n\t\t/// <summary>\n\t\t/// Recent Call Names\n\t\t/// </summary>\n        [JoinName(\"RecentCallNamesStart\")]\n        public JoinDataComplete RecentCallNamesStart = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 181,\n                JoinSpan = 10\n            },\n            new JoinMetadata\n            {\n                Description = \"Recent Call Names\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// Recent Call Numbers\n\t\t/// </summary>\n        [JoinName(\"RecentCallTimesStart\")]\n        public JoinDataComplete RecentCallTimesStart = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 191,\n                JoinSpan = 10\n            },\n            new JoinMetadata\n            {\n                Description = \"Recent Calls Times\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// Current Source\n\t\t/// </summary>\n\t\t[JoinName(\"CurrentSource\")]\n\t\tpublic JoinDataComplete CurrentSource = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 201,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Current Source\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Selfview Position Feedback\n\t\t/// </summary>\n\t\t[JoinName(\"SelfviewPositionFb\")]\n\t\tpublic JoinDataComplete SelfviewPositionFb = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 211,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"advance selfview position\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Device IP Address\n\t\t/// </summary>\n        [JoinName(\"DeviceIpAddresss\")]\n        public JoinDataComplete DeviceIpAddresss = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 301,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"IP Address of device\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// SIP Phone Number\n\t\t/// </summary>\n        [JoinName(\"SipPhoneNumber\")]\n        public JoinDataComplete SipPhoneNumber = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 302,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"SIP phone number of device\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// E164 Alias\n\t\t/// </summary>\n        [JoinName(\"E164Alias\")]\n        public JoinDataComplete E164Alias = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 303,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"E164 alias of device\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// H323 ID\n\t\t/// </summary>\n        [JoinName(\"H323Id\")]\n        public JoinDataComplete H323Id = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 304,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"H323 ID of device\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// SIP URI\n\t\t/// </summary>\n        [JoinName(\"SipUri\")]\n        public JoinDataComplete SipUri = new JoinDataComplete(\n            new JoinData\n            {\n                JoinNumber = 305,\n                JoinSpan = 1\n            },\n            new JoinMetadata\n            {\n                Description = \"SIP URI of device\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n\t\t/// <summary>\n\t\t/// Selected Directory Entry Name\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryEntrySelectedName\")]\n\t\tpublic JoinDataComplete DirectoryEntrySelectedName = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 356,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Selected Directory Entry Name\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Selected Directory Entry Number\n\t\t/// </summary>\n\t\t[JoinName(\"DirectoryEntrySelectedNumber\")]\n\t\tpublic JoinDataComplete DirectoryEntrySelectedNumber = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 357,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Selected Directory Entry Number\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Selected Directory Folder Name\n\t\t/// </summary>\n\t\t[JoinName(\"DirectorySelectedFolderName\")]\n\t\tpublic JoinDataComplete DirectorySelectedFolderName = new JoinDataComplete(\n\t\t\tnew JoinData\n\t\t\t{\n\t\t\t\tJoinNumber = 358,\n\t\t\t\tJoinSpan = 1\n\t\t\t},\n\t\t\tnew JoinMetadata\n\t\t\t{\n\t\t\t\tDescription = \"Selected Directory Folder Name\",\n\t\t\t\tJoinCapabilities = eJoinCapabilities.ToSIMPL,\n\t\t\t\tJoinType = eJoinType.Serial\n\t\t\t});\n\n\t\t#endregion\n\n\n\t\t/// <summary>\n\t\t/// Constructor for the VideoCodecControllerJoinMap class.\n\t\t/// </summary>\n\t\t/// <param name=\"joinStart\">Join this join map will start at</param>\n\t\tpublic VideoCodecControllerJoinMap(uint joinStart)\n\t\t\t: base(joinStart, typeof(VideoCodecControllerJoinMap))\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"joinStart\">Join this join map will start at</param>\n\t\t/// <param name=\"type\">Type of the child join map</param>\n\t\tpublic VideoCodecControllerJoinMap(uint joinStart, Type type)\n\t\t\t: base(joinStart, type)\n\t\t{\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DM;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a CecPortController\n    /// </summary>\n    public class CecPortController : Device, IBasicCommunicationWithStreamDebugging\n    {\n        /// <summary>\n        /// Gets or sets the StreamDebugging\n        /// </summary>\n        public CommunicationStreamDebugging StreamDebugging { get; private set; }\n\n        /// <summary>\n        /// Event raised when bytes are received\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// Event raised when text is received\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Gets or sets the IsConnected\n        /// </summary>\n        public bool IsConnected { get { return true; } }\n\n        ICec Port;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key of the device</param>\n        /// <param name=\"postActivationFunc\">post activation function for the device</param>\n        /// <param name=\"config\">configuration for the device</param>\n        public CecPortController(string key, Func<EssentialsControlPropertiesConfig, ICec> postActivationFunc,\n            EssentialsControlPropertiesConfig config) : base(key)\n        {\n            StreamDebugging = new CommunicationStreamDebugging(key);\n\n            AddPostActivationAction(() =>\n            {\n                Port = postActivationFunc(config);\n\n                Port.StreamCec.CecChange += StreamCec_CecChange;\n            });\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key of the device</param>\n        /// <param name=\"port\">CEC port</param>\n        public CecPortController(string key, ICec port)\n            : base(key)\n        {\n            Port = port;\n\n            Port.StreamCec.CecChange += new CecChangeEventHandler(StreamCec_CecChange);\n        }\n\n        void StreamCec_CecChange(Cec cecDevice, CecEventArgs args)\n        {\n            if (args.EventId == CecEventIds.CecMessageReceivedEventId)\n                OnDataReceived(cecDevice.Received.StringValue);\n            else if (args.EventId == CecEventIds.ErrorFeedbackEventId)\n                if (cecDevice.ErrorFeedback.BoolValue)\n                    Debug.LogMessage(LogEventLevel.Verbose, this, \"CEC NAK Error\");\n        }\n\n        void OnDataReceived(string s)\n        {\n            var bytesHandler = BytesReceived;\n            if (bytesHandler != null)\n            {\n                var bytes = Encoding.GetEncoding(28591).GetBytes(s);\n                this.PrintReceivedBytes(bytes);\n                bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));\n            }\n            var textHandler = TextReceived;\n            if (textHandler != null)\n            {\n                this.PrintReceivedText(s);\n                textHandler(this, new GenericCommMethodReceiveTextArgs(s));\n            }\n        }\n\n        #region IBasicCommunication Members\n\n        /// <summary>\n        /// SendText method\n        /// </summary>\n        public void SendText(string text)\n        {\n            if (Port == null)\n                return;\n            this.PrintSentText(text);\n            Port.StreamCec.Send.StringValue = text;\n        }\n\n        /// <summary>\n        /// SendBytes method\n        /// </summary>\n        public void SendBytes(byte[] bytes)\n        {\n            if (Port == null)\n                return;\n            var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);\n            this.PrintSentBytes(bytes);\n            Debug.LogMessage(LogEventLevel.Information, this, \"Sending {0} bytes: '{1}'\", bytes.Length, ComTextHelper.GetEscapedText(bytes));\n            Port.StreamCec.Send.StringValue = text;\n        }\n\n        /// <summary>\n        /// Connect method\n        /// </summary>\n        public void Connect()\n        {\n        }\n\n        /// <summary>\n        /// Disconnect method\n        /// </summary>\n        public void Disconnect()\n        {\n        }\n\n        #endregion\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <summary>\n        /// SimulateReceive method\n        /// </summary>\n        public void SimulateReceive(string s)\n        {\n            // split out hex chars and build string\n            var split = Regex.Split(s, @\"(\\\\[Xx][0-9a-fA-F][0-9a-fA-F])\");\n            StringBuilder b = new StringBuilder();\n            foreach (var t in split)\n            {\n                if (t.StartsWith(@\"\\\") && t.Length == 4)\n                    b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));\n                else\n                    b.Append(t);\n            }\n\n            OnDataReceived(b.ToString());\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs",
    "content": "﻿using System;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.GeneralIO;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Represents a ComPortController\n\t/// </summary>\n\tpublic class ComPortController : Device, IBasicCommunicationWithStreamDebugging\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the StreamDebugging\n\t\t/// </summary>\n\t\tpublic CommunicationStreamDebugging StreamDebugging { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Event fired when bytes are received\n\t\t/// </summary>\n\t\tpublic event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n\t\t/// <summary>\n\t\t/// Event fired when text is received\n\t\t/// </summary>\n\t\tpublic event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IsConnected\n\t\t/// </summary>\n\t\tpublic bool IsConnected { get { return true; } }\n\n\t\tComPort Port;\n\t\tComPort.ComPortSpec Spec;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"postActivationFunc\"></param>\n\t\t/// <param name=\"spec\"></param>\n\t\t/// <param name=\"config\"></param>\n\t\tpublic ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc,\n\t\t\tComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)\n\t\t{\n\t\t\tStreamDebugging = new CommunicationStreamDebugging(key);\n\n\t\t\tSpec = spec;\n\n\t\t\tAddPostActivationAction(() =>\n\t\t\t{\n\t\t\t\tPort = postActivationFunc(config);\n\n\t\t\t\tRegisterAndConfigureComPort();\n\t\t\t});\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\">Device key</param>\n\t\t/// <param name=\"port\">COM port instance</param>\n\t\t/// <param name=\"spec\">COM port specification</param>\n\t\tpublic ComPortController(string key, ComPort port, ComPort.ComPortSpec spec)\n\t\t\t: base(key)\n\t\t{\n\t\t\tif (port == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"ERROR: Invalid com port, continuing but comms will not function\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tPort = port;\n\t\t\tSpec = spec;\n\t\t\t//IsConnected = new BoolFeedback(CommonBoolCue.IsConnected, () => true);\n\n\t\t\tRegisterAndConfigureComPort();\n\t\t}\n\n\t\tprivate void RegisterAndConfigureComPort()\n\t\t{\n\t\t\tif (Port == null)\n\t\t\t{\n\t\t\t\tthis.LogInformation($\"Configured {Port.Parent.GetType().Name}-comport-{Port.ID} for {Key} does not exist.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\n\t\t\tif (Port.Parent is CrestronControlSystem || Port.Parent is CenIoCom102)\n\t\t\t{\n\t\t\t\tvar result = Port.Register();\n\t\t\t\tif (result != eDeviceRegistrationUnRegistrationResponse.Success)\n\t\t\t\t{\n\t\t\t\t\tthis.LogError($\"Cannot register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.LogInformation($\"Successfully registered {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})\");\n\t\t\t}\n\n\t\t\tvar specResult = Port.SetComPortSpec(Spec);\n\t\t\tif (specResult != 0)\n\t\t\t{\n\t\t\t\tthis.LogError($\"Cannot set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.LogInformation($\"Successfully set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})\");\n\n\t\t\tPort.SerialDataReceived += Port_SerialDataReceived;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Destructor\n\t\t/// </summary>\n\t\t~ComPortController()\n\t\t{\n\t\t\tPort.SerialDataReceived -= Port_SerialDataReceived;\n\t\t}\n\n\t\tvoid Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args)\n\t\t{\n\t\t\tOnDataReceived(args.SerialData);\n\t\t}\n\n\t\tvoid OnDataReceived(string s)\n\t\t{\n\t\t\tvar eventSubscribed = false;\n\n\t\t\tvar bytesHandler = BytesReceived;\n\t\t\tif (bytesHandler != null)\n\t\t\t{\n\t\t\t\tvar bytes = Encoding.GetEncoding(28591).GetBytes(s);\n\t\t\t\tthis.PrintReceivedBytes(bytes);\n\t\t\t\tbytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));\n\t\t\t\teventSubscribed = true;\n\t\t\t}\n\t\t\tvar textHandler = TextReceived;\n\t\t\tif (textHandler != null)\n\t\t\t{\n\t\t\t\tthis.PrintReceivedText(s);\n\t\t\t\ttextHandler(this, new GenericCommMethodReceiveTextArgs(s));\n\t\t\t\teventSubscribed = true;\n\t\t\t}\n\n\t\t\tif (!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, \"Received data but no handler is registered\");\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Deactivate method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override bool Deactivate()\n\t\t{\n\t\t\treturn Port.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;\n\t\t}\n\n\t\t#region IBasicCommunication Members\n\n\t\t/// <summary>\n\t\t/// SendText method\n\t\t/// </summary>\n\t\tpublic void SendText(string text)\n\t\t{\n\t\t\tif (Port == null)\n\t\t\t\treturn;\n\n\t\t\tthis.PrintSentText(text);\n\t\t\tPort.Send(text);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SendBytes method\n\t\t/// </summary>\n\t\tpublic void SendBytes(byte[] bytes)\n\t\t{\n\t\t\tif (Port == null)\n\t\t\t\treturn;\n\t\t\tvar text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);\n\t\t\tthis.PrintSentBytes(bytes);\n\n\t\t\tPort.Send(text);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Connect method\n\t\t/// </summary>\n\t\tpublic void Connect()\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Disconnect method\n\t\t/// </summary>\n\t\tpublic void Disconnect()\n\t\t{\n\t\t}\n\n\t\t#endregion\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"s\"></param>\n\t\t/// <summary>\n\t\t/// SimulateReceive method\n\t\t/// </summary>\n\t\tpublic void SimulateReceive(string s)\n\t\t{\n\t\t\t// split out hex chars and build string\n\t\t\tvar split = Regex.Split(s, @\"(\\\\[Xx][0-9a-fA-F][0-9a-fA-F])\");\n\t\t\tStringBuilder b = new StringBuilder();\n\t\t\tforeach (var t in split)\n\t\t\t{\n\t\t\t\tif (t.StartsWith(@\"\\\") && t.Length == 4)\n\t\t\t\t\tb.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));\n\t\t\t\telse\n\t\t\t\t\tb.Append(t);\n\t\t\t}\n\n\t\t\tOnDataReceived(b.ToString());\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/ComSpecJsonConverter.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// This converter creates a proper ComPort.ComPortSpec struct from more-friendly JSON values.  It uses\n    /// ComSpecPropsJsonConverter to finish the individual properties.\n    /// </summary>\n    public class ComSpecJsonConverter : JsonConverter\n    {\n        /// <summary>\n        /// ReadJson method\n        /// </summary>\n        /// <param name=\"reader\">reader to use</param>\n        /// <param name=\"objectType\">type of the object being read</param>\n        /// <param name=\"existingValue\">existing value of the object being read</param>\n        /// <param name=\"serializer\">serializer to use</param>\n        /// <returns>deserialized object</returns>\n        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)\n        {\n            if (objectType == typeof(ComPort.ComPortSpec?))\n            {\n                var newSer = new JsonSerializer();\n                newSer.Converters.Add(new ComSpecPropsJsonConverter());\n                newSer.ObjectCreationHandling = ObjectCreationHandling.Replace;\n                return newSer.Deserialize<ComPort.ComPortSpec?>(reader);\n            }\n            return null;\n        }\n\n        /// <summary>\n        /// CanConvert method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CanConvert(Type objectType)\n        {\n            return objectType == typeof(ComPort.ComPortSpec?);\n        }\n\n        /// <summary>\n        /// Gets or sets the CanRead\n        /// </summary>\n        public override bool CanRead { get { return true; } }\n\n        /// <summary>\n        /// Gets or sets the CanWrite\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CanWrite { get { return false; } }\n\n        /// <summary>\n        /// WriteJson method\n        /// </summary>\n        /// <inheritdoc />\n        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    /// <summary>\n    /// Represents a ComSpecPropsJsonConverter\n    /// </summary>\n    public class ComSpecPropsJsonConverter : JsonConverter\n    {\n        /// <inheritdoc />\n        public override bool CanConvert(Type objectType)\n        {\n            return objectType == typeof(ComPort.eComBaudRates)\n                || objectType == typeof(ComPort.eComDataBits)\n                || objectType == typeof(ComPort.eComParityType)\n                || objectType == typeof(ComPort.eComHardwareHandshakeType)\n                || objectType == typeof(ComPort.eComSoftwareHandshakeType)\n                || objectType == typeof(ComPort.eComProtocolType)\n                || objectType == typeof(ComPort.eComStopBits);\n        }\n\n        /// <summary>\n        /// Gets or sets the CanRead\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CanRead { get { return true; } }\n\n        /// <summary>\n        /// ReadJson method\n        /// </summary>\n        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)\n        {\n            //Debug.LogMessage(LogEventLevel.Verbose, \"ReadJson type: \" + objectType.Name);\n            if (objectType == typeof(ComPort.eComBaudRates))\n                return Enum.Parse(typeof(ComPort.eComBaudRates), \"ComspecBaudRate\" + reader.Value, false);\n            else if (objectType == typeof(ComPort.eComDataBits))\n                return Enum.Parse(typeof(ComPort.eComDataBits), \"ComspecDataBits\" + reader.Value, true);\n            else if (objectType == typeof(ComPort.eComHardwareHandshakeType))\n                return Enum.Parse(typeof(ComPort.eComHardwareHandshakeType), \"ComspecHardwareHandshake\" + reader.Value, true);\n            else if (objectType == typeof(ComPort.eComParityType))\n                return Enum.Parse(typeof(ComPort.eComParityType), \"ComspecParity\" + reader.Value, true);\n            else if (objectType == typeof(ComPort.eComProtocolType))\n                return Enum.Parse(typeof(ComPort.eComProtocolType), \"ComspecProtocol\" + reader.Value, true);\n            else if (objectType == typeof(ComPort.eComSoftwareHandshakeType))\n                return Enum.Parse(typeof(ComPort.eComSoftwareHandshakeType), \"ComspecSoftwareHandshake\" + reader.Value, true);\n            else if (objectType == typeof(ComPort.eComStopBits))\n                return Enum.Parse(typeof(ComPort.eComStopBits), \"ComspecStopBits\" + reader.Value, true);\n            return null;\n        }\n\n        /// <summary>\n        /// WriteJson method\n        /// </summary>\n        /// <inheritdoc />\n        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/CommBridge.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing Crestron.SimplSharp.CrestronSockets;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Devices;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Implements IBasicCommunication and sends all communication through an EISC\n    /// </summary>\n    [Description(\"Generic communication wrapper class for any IBasicCommunication type\")]\n    public class CommBridge : EssentialsBridgeableDevice, IBasicCommunication\n    {\n        private EiscApiAdvanced eisc;\n\n        private IBasicCommunicationJoinMap joinMap;\n\n        /// <summary>\n        /// Event triggered when text is received through the communication bridge.\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n        /// <summary>\n        /// Event triggered when bytes are received through the communication bridge.\n        /// </summary>\n        public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n        /// <summary>\n        /// Indicates whether the communication bridge is currently connected.\n        /// </summary>\n        public bool IsConnected { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"CommBridge\"/> class.\n        /// </summary>\n        /// <param name=\"key\">The unique key for the communication bridge.</param>\n        /// <param name=\"name\">The display name for the communication bridge.</param>\n        public CommBridge(string key, string name)\n            : base(key, name)\n        {\n\n        }\n\n        /// <summary>\n        /// Sends a byte array through the communication bridge.\n        /// </summary>\n        /// <param name=\"bytes\">The byte array to send.</param>\n        public void SendBytes(byte[] bytes)\n        {\n            if (eisc == null)\n            {\n                this.LogWarning(\"EISC is null, cannot send bytes.\");\n                return;\n            }\n            eisc.Eisc.SetString(joinMap.SendText.JoinNumber, Encoding.ASCII.GetString(bytes, 0, bytes.Length));\n        }\n\n        /// <summary>\n        /// Sends a text string through the communication bridge.\n        /// </summary>\n        /// <param name=\"text\">The text string to send.</param>\n        public void SendText(string text)\n        {\n            if (eisc == null)\n            {\n                this.LogWarning(\"EISC is null, cannot send text.\");\n                return;\n            }\n            eisc.Eisc.SetString(joinMap.SendText.JoinNumber, text);\n        }\n\n        /// <summary>\n        /// Initiates a connection through the communication bridge.\n        /// </summary>\n        public void Connect()\n        {\n            if (eisc == null)\n            {\n                this.LogWarning(\"EISC is null, cannot connect.\");\n                return;\n            }\n            eisc.Eisc.SetBool(joinMap.Connect.JoinNumber, true);\n        }\n\n        /// <summary>\n        /// Terminates the connection through the communication bridge.\n        /// </summary>\n        public void Disconnect()\n        {\n            if (eisc == null)\n            {\n                this.LogWarning(\"EISC is null, cannot disconnect.\");\n                return;\n            }\n            eisc.Eisc.SetBool(joinMap.Connect.JoinNumber, false);\n        }\n\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            joinMap = new IBasicCommunicationJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<IBasicCommunicationJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                this.LogWarning(\"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            this.LogDebug(\"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n\n            eisc = bridge;\n\n            trilist.SetBoolSigAction(joinMap.Connected.JoinNumber, (b) => IsConnected = b);\n\n            trilist.SetStringSigAction(joinMap.TextReceived.JoinNumber, (s) =>\n            {\n                TextReceived?.Invoke(this, new GenericCommMethodReceiveTextArgs(s));\n                BytesReceived?.Invoke(this, new GenericCommMethodReceiveBytesArgs(Encoding.ASCII.GetBytes(s)));\n            });\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs",
    "content": "﻿\n\nusing System;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DM;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic class CommFactory\n\t{\n\t\t/// <summary>\n\t\t/// GetControlPropertiesConfig method\n\t\t/// </summary>\n\t\t/// <param name=\"deviceConfig\">The Device config object</param>\n\t\t/// <returns>EssentialsControlPropertiesConfig object</returns>\n\t\tpublic static EssentialsControlPropertiesConfig GetControlPropertiesConfig(DeviceConfig deviceConfig)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\treturn JsonConvert.DeserializeObject<EssentialsControlPropertiesConfig>\n\t\t\t\t\t(deviceConfig.Properties[\"control\"].ToString());\n\t\t\t\t//Debug.LogMessage(LogEventLevel.Verbose, \"Control TEST: {0}\", JsonConvert.SerializeObject(controlConfig));\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"ERROR: [{0}] Control properties deserialize failed:\\r{1}\", deviceConfig.Key, e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\n\t\t/// <summary>\n\t\t/// Returns a comm method of either com port, TCP, SSH, and puts this into the DeviceManager\n\t\t/// </summary>\n\t\t/// <param name=\"deviceConfig\">The Device config object</param>\n\t\t/// <summary>\n\t\t/// CreateCommForDevice method\n\t\t/// </summary>\n\t\tpublic static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig)\n\t\t{\n\t\t\tEssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig);\n\t\t\tif (controlConfig == null)\n\t\t\t\treturn null;\n\n\t\t\tIBasicCommunication comm = null;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar c = controlConfig.TcpSshProperties;\n\t\t\t\tswitch (controlConfig.Method)\n\t\t\t\t{\n\t\t\t\t\tcase eControlMethod.Com:\n\t\t\t\t\t\tcomm = new ComPortController(deviceConfig.Key + \"-com\", GetComPort, controlConfig.ComParams.Value, controlConfig);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eControlMethod.ComBridge:\n\t\t\t\t\t\tcomm = new CommBridge(deviceConfig.Key + \"-simpl\", deviceConfig.Name + \" Simpl\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eControlMethod.Cec:\n\t\t\t\t\t\tcomm = new CecPortController(deviceConfig.Key + \"-cec\", GetCecPort, controlConfig);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eControlMethod.IR:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eControlMethod.Ssh:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar ssh = new GenericSshClient(deviceConfig.Key + \"-ssh\", c.Address, c.Port, c.Username, c.Password)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAutoReconnect = c.AutoReconnect,\n\t\t\t\t\t\t\t\tDisableEcho = c.DisableSshEcho\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (ssh.AutoReconnect)\n\t\t\t\t\t\t\t\tssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;\n\t\t\t\t\t\t\tcomm = ssh;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\tcase eControlMethod.Tcpip:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar tcp = new GenericTcpIpClient(deviceConfig.Key + \"-tcp\", c.Address, c.Port, c.BufferSize)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAutoReconnect = c.AutoReconnect\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (tcp.AutoReconnect)\n\t\t\t\t\t\t\t\ttcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;\n\t\t\t\t\t\t\tcomm = tcp;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\tcase eControlMethod.Udp:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar udp = new GenericUdpServer(deviceConfig.Key + \"-udp\", c.Address, c.Port, c.BufferSize);\n\t\t\t\t\t\t\tcomm = udp;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\tcase eControlMethod.UdpClient:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar udpClient = new GenericUdpClient(deviceConfig.Key + \"-udpClient\", c.Address, c.Port, c.BufferSize)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAutoReconnect = c.AutoReconnect\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (udpClient.AutoReconnect)\n\t\t\t\t\t\t\t\tudpClient.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;\n\t\t\t\t\t\t\tcomm = udpClient;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\tcase eControlMethod.Telnet:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase eControlMethod.SecureTcpIp:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + \"-secureTcp\", c.Address, c.Port, c.BufferSize)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAutoReconnect = c.AutoReconnect\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (secureTcp.AutoReconnect)\n\t\t\t\t\t\t\t\tsecureTcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;\n\t\t\t\t\t\t\tcomm = secureTcp;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Cannot create communication from JSON:\\r{0}\\r\\rException:\\r{1}\",\n\t\t\t\t\tdeviceConfig.Properties.ToString(), e);\n\t\t\t}\n\n\t\t\t// put it in the device manager if it's the right flavor\n\t\t\tif (comm is Device comDev)\n\t\t\t\tDeviceManager.AddDevice(comDev);\n\t\t\treturn comm;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// GetComPort method\n\t\t/// </summary>\n\t\tpublic static ComPort GetComPort(EssentialsControlPropertiesConfig config)\n\t\t{\n\t\t\tvar comPar = config.ComParams;\n\t\t\tvar dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey);\n\t\t\tif (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts)\n\t\t\t\treturn dev.ComPorts[config.ControlPortNumber.Value];\n\t\t\tDebug.LogMessage(LogEventLevel.Information, \"GetComPort: Device '{0}' does not have com port {1}\", config.ControlPortDevKey, config.ControlPortNumber);\n\t\t\treturn null;\n\t\t}\n\n\t\t/// <summary>\n\t\t///  Gets an ICec port from a RoutingInput or RoutingOutput on a device\n\t\t/// </summary>\n\t\t/// <param name=\"config\"></param>\n\t\t/// <returns></returns>\n\t\t/// <summary>\n\t\t/// GetCecPort method\n\t\t/// </summary>\n\t\tpublic static ICec GetCecPort(ControlPropertiesConfig config)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey);\n\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"GetCecPort: device '{0}' {1}\", config.ControlPortDevKey, dev == null\n\t\t\t\t\t? \"is not valid, failed to get cec port\"\n\t\t\t\t\t: \"found in device manager, attempting to get cec port\");\n\n\t\t\t\tif (dev == null)\n\t\t\t\t\treturn null;\n\n\t\t\t\tif (String.IsNullOrEmpty(config.ControlPortName))\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"GetCecPort: '{0}' - Configuration missing 'ControlPortName'\", config.ControlPortDevKey);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\n\t\t\t\tvar inputsOutputs = dev as IRoutingInputsOutputs;\n\t\t\t\tif (inputsOutputs == null)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"GetCecPort: Device '{0}' does not support IRoutingInputsOutputs, failed to get CEC port called '{1}'\",\n\t\t\t\t\t\tconfig.ControlPortDevKey, config.ControlPortName);\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tvar inputPort = inputsOutputs.InputPorts[config.ControlPortName];\n\t\t\t\tif (inputPort != null && inputPort.Port is ICec)\n\t\t\t\t\treturn inputPort.Port as ICec;\n\n\n\t\t\t\tvar outputPort = inputsOutputs.OutputPorts[config.ControlPortName];\n\t\t\t\tif (outputPort != null && outputPort.Port is ICec)\n\t\t\t\t\treturn outputPort.Port as ICec;\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"GetCecPort Exception Message: {0}\", ex.Message);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, \"GetCecPort Exception StackTrace: {0}\", ex.StackTrace);\n\t\t\t\tif (ex.InnerException != null)\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"GetCecPort Exception InnerException: {0}\", ex.InnerException);\n\t\t\t}\n\n\t\t\tDebug.LogMessage(LogEventLevel.Information, \"GetCecPort: Device '{0}' does not have a CEC port called '{1}'\",\n\t\t\t\t\tconfig.ControlPortDevKey, config.ControlPortName);\n\n\t\t\treturn null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Helper to grab the IComPorts device for this PortDeviceKey. Key \"controlSystem\" will\n\t\t/// return the ControlSystem object from the Global class.\n\t\t/// </summary>\n\t\t/// <returns>IComPorts device or null if the device is not found or does not implement IComPorts</returns>\n\t\t/// <summary>\n\t\t/// GetIComPortsDeviceFromManagedDevice method\n\t\t/// </summary>\n\t\tpublic static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey)\n\t\t{\n\t\t\tif ((ComPortDevKey.Equals(\"controlSystem\", System.StringComparison.OrdinalIgnoreCase)\n\t\t\t\t|| ComPortDevKey.Equals(\"processor\", System.StringComparison.OrdinalIgnoreCase))\n\t\t\t\t&& Global.ControlSystem is IComPorts)\n\t\t\t\treturn Global.ControlSystem;\n\t\t\telse\n\t\t\t{\n\t\t\t\tvar dev = DeviceManager.GetDeviceForKey(ComPortDevKey) as IComPorts;\n\t\t\t\tif (dev == null)\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"ComPortConfig: Cannot find com port device '{0}'\", ComPortDevKey);\n\t\t\t\treturn dev;\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a EssentialsControlPropertiesConfig\n\t/// </summary>\n\tpublic class EssentialsControlPropertiesConfig :\n\t\t\tControlPropertiesConfig\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the ComParams\n\t\t/// </summary>\n\t\t[JsonProperty(\"comParams\", NullValueHandling = NullValueHandling.Ignore)]\n\t\t[JsonConverter(typeof(ComSpecJsonConverter))]\n\t\tpublic ComPort.ComPortSpec? ComParams { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the CresnetId\n\t\t/// </summary>\n\t\t[JsonProperty(\"cresnetId\", NullValueHandling = NullValueHandling.Ignore)]\n\t\tpublic string CresnetId { get; set; }\n\n\t\t/// <summary>\n\t\t/// Attempts to provide uint conversion of string CresnetId\n\t\t/// </summary>\n\t\t[JsonIgnore]\n\t\tpublic uint CresnetIdInt\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\treturn Convert.ToUInt32(CresnetId, 16);\n\t\t\t\t}\n\t\t\t\tcatch (Exception)\n\t\t\t\t{\n\t\t\t\t\tthrow new FormatException(string.Format(\"ERROR:Unable to convert Cresnet ID: {0} to hex.  Error:\\n{1}\", CresnetId));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets the InfinetId\n\t\t/// </summary>\n\t\t[JsonProperty(\"infinetId\", NullValueHandling = NullValueHandling.Ignore)]\n\t\tpublic string InfinetId { get; set; }\n\n\t\t/// <summary>\n\t\t/// Attepmts to provide uiont conversion of string InifinetId\n\t\t/// </summary>\n\t\t[JsonIgnore]\n\t\tpublic uint InfinetIdInt\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\treturn Convert.ToUInt32(InfinetId, 16);\n\t\t\t\t}\n\t\t\t\tcatch (Exception)\n\t\t\t\t{\n\t\t\t\t\tthrow new FormatException(string.Format(\"ERROR:Unable to conver Infinet ID: {0} to hex.  Error:\\n{1}\", InfinetId));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a IrControlSpec\n\t/// </summary>\n\tpublic class IrControlSpec\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the PortDeviceKey\n\t\t/// </summary>\n\t\tpublic string PortDeviceKey { get; set; }\n\t\t/// <summary>\n\t\t/// Gets or sets the PortNumber\n\t\t/// </summary>\n\t\tpublic uint PortNumber { get; set; }\n\t\t/// <summary>\n\t\t/// Gets or sets the File\n\t\t/// </summary>\n\t\tpublic string File { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/CommunicationExtras.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic interface IComPortsDevice\n\t{\n\t\t/// <summary>\n\t\t/// Gets the Device\n\t\t/// </summary>\n\t\tIComPorts Device { get; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/ConsoleCommMockDevice.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Represents a ConsoleCommMockDevice\n /// </summary>\n\tpublic class ConsoleCommMockDevice : EssentialsDevice, ICommunicationMonitor\n\t{\n  /// <summary>\n  /// Gets or sets the Communication\n  /// </summary>\n\t\tpublic IBasicCommunication Communication { get; private set; }\n  /// <summary>\n  /// Gets or sets the PortGather\n  /// </summary>\n\t\tpublic CommunicationGather PortGather { get; private set; }\n  /// <summary>\n  /// Gets or sets the CommunicationMonitor\n  /// </summary>\n\t\tpublic StatusMonitorBase CommunicationMonitor { get; private set; }\n\n  /// <summary>\n  /// Gets or sets the LineEnding\n  /// </summary>\n\t\tpublic string LineEnding { get; set; }\n\n  /// <summary>\n  /// Gets or sets the ShowHexResponse\n  /// </summary>\n\t\tpublic bool ShowHexResponse { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the ConsoleCommMockDevice class.\n        /// </summary>\n        /// <param name=\"key\">The key of the device.</param>\n        /// <param name=\"name\">The name of the device.</param>\n        /// <param name=\"props\">The properties configuration for the device.</param>\n        /// <param name=\"comm\">The communication method for the device.</param>\n\t\tpublic ConsoleCommMockDevice(string key, string name, ConsoleCommMockDevicePropertiesConfig props, IBasicCommunication comm)\n\t\t\t:base(key, name)\n\t\t{\n\t\t\tCommunication = comm;\n\t\t\tPortGather = new CommunicationGather(Communication, '\\x0d');\n\t\t\t//PortGather.LineReceived += this.Port_LineReceived;\n\t\t\tCommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);\n\t\t\tLineEnding = props.LineEnding;\n\t\t}\n\n  /// <summary>\n  /// CustomActivate method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic override bool CustomActivate()\n\t\t{\n\t\t\tCommunication.Connect();\n\t\t\tCommunicationMonitor.StatusChange += (o, a) => { Debug.LogMessage(LogEventLevel.Verbose, this, \"Communication monitor state: {0}\", CommunicationMonitor.Status); };\n\t\t\tCommunicationMonitor.Start();\n\n\t\t\tCrestronConsole.AddNewConsoleCommand(SendLine, \"send\" + Key, \"\", ConsoleAccessLevelEnum.AccessOperator);\n\t\t\tCrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), \"con\" + Key, \"\", ConsoleAccessLevelEnum.AccessOperator);\n\t\t\treturn true;\n\t\t}\n\n\t\tvoid SendLine(string s)\n\t\t{\n\t\t\t//if (Debug.Level == 2)\n\t\t\t//    Debug.LogMessage(LogEventLevel.Verbose, this, \"    Send '{0}'\", ComTextHelper.GetEscapedText(s));\n\t\t\tCommunication.SendText(s + LineEnding);\n\t\t}\n\t}\n\n    /// <summary>\n    /// Represents a ConsoleCommMockDevicePropertiesConfig\n    /// </summary>\n\tpublic class ConsoleCommMockDevicePropertiesConfig\n\t{\n        /// <summary>\n        /// Gets or sets the LineEnding\n        /// </summary>\n\t\tpublic string LineEnding { get; set; }\n\n        /// <summary>\n        /// Gets or sets the CommunicationMonitorProperties\n        /// </summary>\n\t\tpublic CommunicationMonitorConfig CommunicationMonitorProperties { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the ConsoleCommMockDevicePropertiesConfig class.\n        /// </summary>\n\t\tpublic ConsoleCommMockDevicePropertiesConfig()\n\t\t{\n\t\t\tLineEnding = \"\\x0a\";\n\t\t}\n\t}\n\n    /// <summary>\n    /// Represents a ConsoleCommMockDeviceFactory\n    /// </summary>\n    public class ConsoleCommMockDeviceFactory : EssentialsDeviceFactory<ConsoleCommMockDevice>\n    {\n        /// <summary>\n        /// Initializes a new instance of the ConsoleCommMockDeviceFactory class.\n        /// </summary>\n        public ConsoleCommMockDeviceFactory()\n        {\n            TypeNames = new List<string>() { \"commmock\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Comm Mock Device\");\n            var comm = CommFactory.CreateCommForDevice(dc);\n            var props = Newtonsoft.Json.JsonConvert.DeserializeObject<ConsoleCommMockDevicePropertiesConfig>(\n                dc.Properties.ToString());\n            return new ConsoleCommMockDevice(dc.Key, dc.Name, props, comm);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/GenericComm.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp.CrestronSockets;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Devices;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Serves as a generic wrapper class for all styles of IBasicCommuncation ports\n    /// </summary>\n    [Description(\"Generic communication wrapper class for any IBasicCommunication type\")]\n    public class GenericComm : ReconfigurableBridgableDevice\n    {\n        EssentialsControlPropertiesConfig PropertiesConfig;\n\n        /// <summary>\n        /// Gets the CommPort\n        /// </summary>\n        public IBasicCommunication CommPort { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"config\">the config of the device</param>\n        public GenericComm(DeviceConfig config)\n            : base(config)\n        {\n\n            PropertiesConfig = CommFactory.GetControlPropertiesConfig(config);\n\n            var commPort = CommFactory.CreateCommForDevice(config);\n\n            //Fixing decision to require '-comPorts' in declaration for DGE in order to get a device with comports included\n            if (commPort == null)\n            {\n                config.Key = config.Key + \"-comPorts\";\n                commPort = CommFactory.CreateCommForDevice(config);\n            }\n\n            CommPort = commPort;\n\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        public static IKeyed BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Generic Comm Device\");\n            return new GenericComm(dc);\n        }\n\n        /// <summary>\n        /// SetPortConfig method\n        /// </summary>\n        public void SetPortConfig(string portConfig)\n        {\n            // TODO: Deserialize new EssentialsControlPropertiesConfig and handle as necessary\n            try\n            {\n                PropertiesConfig = JsonConvert.DeserializeObject<EssentialsControlPropertiesConfig>\n                    (portConfig);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Error deserializing port config: {0}\", e);\n            }\n        }\n\n        /// <summary>\n        /// CustomSetConfig method\n        /// </summary>\n        /// <param name=\"config\">the new device configuration</param>\n        protected override void CustomSetConfig(DeviceConfig config)\n        {\n            PropertiesConfig = CommFactory.GetControlPropertiesConfig(config);\n\n            ConfigWriter.UpdateDeviceConfig(config);\n        }\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new IBasicCommunicationJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<IBasicCommunicationJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            if (CommPort == null)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to link device '{0}'.  CommPort is null\", Key);\n                return;\n            }\n\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n\n            // this is a permanent event handler. This cannot be -= from event\n            CommPort.TextReceived += (s, a) =>\n            {\n                trilist.SetString(joinMap.TextReceived.JoinNumber, a.Text);\n            };\n            trilist.SetStringSigAction(joinMap.SendText.JoinNumber, s => CommPort.SendText(s));\n            trilist.SetStringSigAction(joinMap.SetPortConfig.JoinNumber, SetPortConfig);\n\n\n            var sComm = CommPort as ISocketStatus;\n            if (sComm == null) return;\n            sComm.ConnectionChange += (s, a) =>\n            {\n                trilist.SetUshort(joinMap.Status.JoinNumber, (ushort)(a.Client.ClientStatus));\n                trilist.SetBool(joinMap.Connected.JoinNumber, a.Client.ClientStatus ==\n                                                   SocketStatus.SOCKET_STATUS_CONNECTED);\n            };\n\n            trilist.SetBoolSigAction(joinMap.Connect.JoinNumber, b =>\n            {\n                if (b)\n                {\n                    sComm.Connect();\n                }\n                else\n                {\n                    sComm.Disconnect();\n                }\n            });\n        }\n    }\n\n    /// <summary>\n    /// Represents a GenericCommFactory\n    /// </summary>\n    public class GenericCommFactory : EssentialsDeviceFactory<GenericComm>\n    {\n        /// <summary>\n        /// Initializes a new instance of the GenericCommFactory class.\n        /// </summary>\n        public GenericCommFactory()\n        {\n            TypeNames = new List<string>() { \"genericComm\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Generic Comm Device\");\n            return new GenericComm(dc);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/GenericHttpClient.cs",
    "content": "﻿using Crestron.SimplSharp.Net.Http;\nusing PepperDash.Core;\nusing System;\n\nnamespace PepperDash.Essentials.Core\n{\n\t\n\t/// <summary>\n\t/// Represents a GenericHttpClient\n\t/// </summary>\n    [Obsolete(\"Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines\")]\n\tpublic class GenericHttpClient : Device, IBasicCommunication\n\t{\n\t\tprivate readonly HttpClient Client;\n\n\t\t/// <summary>\n\t\t/// Event raised when response is received\n\t\t/// </summary>\n\t\tpublic event EventHandler<GenericHttpClientEventArgs> ResponseRecived;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\">key of the device</param>\n\t\t/// <param name=\"name\">name of the device</param>\n\t\t/// <param name=\"hostname\">hostname for the HTTP client</param>\n\t\tpublic GenericHttpClient(string key, string name, string hostname)\n\t\t\t: base(key, name)\n\t\t{\n            Client = new HttpClient\n            {\n                HostName = hostname\n            };\n\n\n        }\n\n\t\t/// <summary>\n\t\t/// SendText method\n\t\t/// </summary>\n\t\t/// <param name=\"path\">the path to send the request to</param>\n\t\tpublic void SendText(string path)\n\t\t{\n\t\t\tHttpClientRequest request = new HttpClientRequest();\n\t\t\tstring url = string.Format(\"http://{0}/{1}\", Client.HostName, path);\n\t\t\trequest.Url = new UrlParser(url);\n\t\t\tHttpClient.DISPATCHASYNC_ERROR error = Client.DispatchAsyncEx(request, Response, request); \n\t\t}\n\n\t\t/// <summary>\n\t\t/// SendText method\n\t\t/// </summary>\n\t\t/// <param name=\"format\">format for the items</param>\n\t\t/// <param name=\"items\">items to format</param>\n\t\tpublic void SendText(string format, params object[] items)\n\t\t{\n\t\t\tHttpClientRequest request = new HttpClientRequest();\n\t\t\tstring url = string.Format(\"http://{0}/{1}\", Client.HostName, string.Format(format, items));\n\t\t\trequest.Url = new UrlParser(url);\n\t\t\tHttpClient.DISPATCHASYNC_ERROR error = Client.DispatchAsyncEx(request, Response, request);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SendTextNoResponse method\n\t\t/// </summary>\n\t\t/// <param name=\"format\">format for the items</param>\n\t\t/// <param name=\"items\">items to format</param>\n\t\tpublic void SendTextNoResponse(string format, params object[] items)\n\t\t{\n\t\t\tHttpClientRequest request = new HttpClientRequest();\n\t\t\tstring url = string.Format(\"http://{0}/{1}\", Client.HostName, string.Format(format, items));\n\t\t\trequest.Url = new UrlParser(url);\n\t\t\tClient.Dispatch(request);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Response method\n\t\t/// </summary>\n\t\t/// <param name=\"response\">response received from the HTTP client</param>\n\t\t/// <param name=\"error\">error status of the HTTP callback</param>\n\t\t/// <param name=\"request\">original HTTP client request</param>\n\t\tprivate void Response(HttpClientResponse response, HTTP_CALLBACK_ERROR error, object request)\n\t\t{\n\t\t\tif (error == HTTP_CALLBACK_ERROR.COMPLETED)\n\t\t\t{\n\t\t\t\tvar responseReceived = response;\n\n\t\t\t\tif (responseReceived.ContentString.Length > 0)\n\t\t\t\t{\n                    ResponseRecived?.Invoke(this, new GenericHttpClientEventArgs(responseReceived.ContentString, (request as HttpClientRequest).Url.ToString(), error));\n                }\n\t\t\t}\n\n\t\t}\n\n\n\t\t#region IBasicCommunication Members\n\n\t\t/// <summary>\n\t\t/// SendBytes method\n\t\t/// </summary>\n\t\t/// <param name=\"bytes\">bytes to send</param>\n\t\tpublic void SendBytes(byte[] bytes)\n\t\t{\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\n\n\t\t#endregion\n\n\t\t#region ICommunicationReceiver Members\n\n\t\t/// <summary>\n\t\t/// BytesReceived event\n\t\t/// </summary>\n\t\tpublic event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;\n\n\t\t/// <summary>\n\t\t/// Connect method\n\t\t/// </summary>\n\t\tpublic void Connect()\n\t\t{\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Disconnect method\n\t\t/// </summary>\n\t\tpublic void Disconnect()\n\t\t{\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// IsConnected property\n\t\t/// </summary>\n\t\tpublic bool IsConnected\n\t\t{\n\t\t\tget { return true; }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// TextReceived event\n\t\t/// </summary>\n\t\tpublic event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;\n\n\t\t#endregion\n\t}\n\n\t/// <summary>\n\t/// Represents a GenericHttpClientEventArgs\n\t/// </summary>\n\tpublic class GenericHttpClientEventArgs : EventArgs\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the ResponseText\n\t\t/// </summary>\n\t\tpublic string ResponseText { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the RequestPath\n\t\t/// </summary>\n\t\tpublic string RequestPath { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Error\n\t\t/// </summary>\n\t\tpublic HTTP_CALLBACK_ERROR Error { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"response\">response text</param>\n\t\t/// <param name=\"request\">request path</param>\n\t\t/// <param name=\"error\">error status</param>\n\t\tpublic GenericHttpClientEventArgs(string response, string request, HTTP_CALLBACK_ERROR error)\n\t\t{\n\t\t\tResponseText = response;\n\t\t\tRequestPath = request;\n\t\t\tError = error;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharpPro;\n\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Helper class for IR port operations\n\t/// </summary>\n\tpublic static class IRPortHelper\n\t{\n\t\t/// <summary>\n\t\t/// Gets the IrDriverPathPrefix\n\t\t/// </summary>\n\t\tpublic static string IrDriverPathPrefix\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn Global.FilePathPrefix + \"IR\" + Global.DirectorySeparator;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds either the ControlSystem or a device controller that contains IR ports and\n\t\t/// returns a port from the hardware device\n\t\t/// </summary>\n\t\t/// <param name=\"propsToken\">JSON token containing properties</param>\n\t\t/// <returns>IrPortConfig object.  The port and or filename will be empty/null \n\t\t/// if valid values don't exist on config</returns>\n\t\tpublic static IrOutPortConfig GetIrPort(JToken propsToken)\n\t\t{\n\t\t\tvar control = propsToken[\"control\"];\n\t\t\tif (control == null)\n\t\t\t\treturn null;\n\t\t\tif (control[\"method\"].Value<string>() != \"ir\")\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"IRPortHelper called with non-IR properties\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tvar port = new IrOutPortConfig();\n\n\t\t\tvar portDevKey = control.Value<string>(\"controlPortDevKey\");\n\t\t\tvar portNum = control.Value<uint>(\"controlPortNumber\");\n\t\t\tif (portDevKey == null || portNum == 0)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"WARNING: Properties is missing port device or port number\");\n\t\t\t\treturn port;\n\t\t\t}\n\n\t\t\tIIROutputPorts irDev = null;\n\t\t\tif (portDevKey.Equals(\"controlSystem\", StringComparison.OrdinalIgnoreCase)\n\t\t\t\t|| portDevKey.Equals(\"processor\", StringComparison.OrdinalIgnoreCase))\n\t\t\t\tirDev = Global.ControlSystem;\n\t\t\telse\n\t\t\t\tirDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts;\n\n\t\t\tif (irDev == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"[Config] Error, device with IR ports '{0}' not found\", portDevKey);\n\t\t\t\treturn port;\n\t\t\t}\n\n\t\t\tif (portNum <= irDev.NumberOfIROutputPorts) // success!\n\t\t\t{\n\t\t\t\tvar file = IrDriverPathPrefix + control[\"irFile\"].Value<string>();\n\t\t\t\tport.Port = irDev.IROutputPorts[portNum];\n\t\t\t\tport.FileName = file;\n\t\t\t\treturn port; // new IrOutPortConfig { Port = irDev.IROutputPorts[portNum], FileName = file };\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"[Config] Error, device '{0}' IR port {1} out of range\",\n\t\t\t\t\tportDevKey, portNum);\n\t\t\t\treturn port;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// GetIrOutputPort method\n\t\t/// </summary>\n\t\t/// <param name=\"dc\">DeviceConfig to get the IR port for</param>\n\t\t/// <returns>IROutputPort or null if not found</returns>\n\t    public static IROutputPort GetIrOutputPort(DeviceConfig dc)\n\t    {\n\t        var irControllerKey = dc.Key + \"-ir\";\n\t        if (dc.Properties == null)\n\t        {\n\t            Debug.LogMessage(LogEventLevel.Information, \"[{0}] WARNING: Device config does not include properties.  IR will not function.\", dc.Key);\n\t            return null;\n\t        }\n\n\t        var control = dc.Properties[\"control\"];\n\t        if (control == null)\n\t        {\n\t            Debug.LogMessage(LogEventLevel.Information,\n\t                \"WARNING: Device config does not include control properties.  IR will not function for {0}\", dc.Key);\n\t            return null;\n\t        }\n\n\t        var portDevKey = control.Value<string>(\"controlPortDevKey\");\n\t        var portNum = control.Value<uint>(\"controlPortNumber\");\n\t        IIROutputPorts irDev = null;\n\n\t        if (portDevKey == null)\n\t        {\n\t            Debug.LogMessage(LogEventLevel.Information, \"WARNING: control properties is missing ir device for {0}\", dc.Key);\n\t            return null;\n\t        }\n\n\t        if (portNum == 0)\n\t        {\n\t            Debug.LogMessage(LogEventLevel.Information, \"WARNING: control properties is missing ir port number for {0}\", dc.Key);\n\t            return null;\n\t        }\n\n\t        if (portDevKey.Equals(\"controlSystem\", StringComparison.OrdinalIgnoreCase)\n\t            || portDevKey.Equals(\"processor\", StringComparison.OrdinalIgnoreCase))\n\t            irDev = Global.ControlSystem;\n\t        else\n\t            irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts;\n\n\t        if (irDev == null)\n\t        {\n\t            Debug.LogMessage(LogEventLevel.Information, \"WARNING: device with IR ports '{0}' not found\", portDevKey);\n\t            return null;\n\t        }\n\t        if (portNum > irDev.NumberOfIROutputPorts)\n\t        {\n\t            Debug.LogMessage(LogEventLevel.Information, \"WARNING: device '{0}' IR port {1} out of range\",\n\t                portDevKey, portNum);\n\t            return null;\n\t        }\n\n\n\t        var port = irDev.IROutputPorts[portNum];\n\n            \n\n\t        return port;\n\t    }\n\n\t\t/// <summary>\n\t\t/// GetIrOutputPortController method\n\t\t/// </summary>\n\t\t/// <param name=\"config\">DeviceConfig to create the IrOutputPortController for</param>\n\t\t/// <returns>IrOutputPortController object</returns>\n\t    public static IrOutputPortController GetIrOutputPortController(DeviceConfig config)\n\t    {\n            Debug.LogMessage(LogEventLevel.Debug, \"Attempting to create new Ir Port Controller\");\n\n\t        if (config == null)\n\t        {\n\t            return null;\n\t        }\n\n            var postActivationFunc = new Func<DeviceConfig,IROutputPort> (GetIrOutputPort);\n\t        var irDevice = new IrOutputPortController(config.Key + \"-ir\", postActivationFunc, config);\n\n\t        return irDevice;\n\t    }\n\n\t    /*\n        /// <summary>\n        /// GetIrOutputPortController method\n        /// </summary>\n        public static IrOutputPortController GetIrOutputPortController(DeviceConfig devConf)\n        {\n            var irControllerKey = devConf.Key + \"-ir\";\n            if (devConf.Properties == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"[{0}] WARNING: Device config does not include properties.  IR will not function.\", devConf.Key);\n                return new IrOutputPortController(irControllerKey, null, \"\");\n            }\n\n            var control = devConf.Properties[\"control\"];\n            if (control == null)\n            {\n                var c = new IrOutputPortController(irControllerKey, null, \"\");\n                Debug.LogMessage(LogEventLevel.Information, c, \"WARNING: Device config does not include control properties.  IR will not function\");\n                return c;\n            }\n\n            var portDevKey = control.Value<string>(\"controlPortDevKey\");\n            var portNum = control.Value<uint>(\"controlPortNumber\");\n            IIROutputPorts irDev = null;\n\n            if (portDevKey == null)\n            {\n                var c = new IrOutputPortController(irControllerKey, null, \"\");\n                Debug.LogMessage(LogEventLevel.Information, c, \"WARNING: control properties is missing ir device\");\n                return c;\n            }\n\n            if (portNum == 0)\n            {\n                var c = new IrOutputPortController(irControllerKey, null, \"\");\n                Debug.LogMessage(LogEventLevel.Information, c, \"WARNING: control properties is missing ir port number\");\n                return c;\n            } \n\n            if (portDevKey.Equals(\"controlSystem\", StringComparison.OrdinalIgnoreCase)\n                || portDevKey.Equals(\"processor\", StringComparison.OrdinalIgnoreCase))\n                irDev = Global.ControlSystem;\n            else\n                irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts;\n\n            if (irDev == null)\n            {\n                var c = new IrOutputPortController(irControllerKey, null, \"\");\n                Debug.LogMessage(LogEventLevel.Information, c, \"WARNING: device with IR ports '{0}' not found\", portDevKey);\n                return c;\n            }\n\n            if (portNum <= irDev.NumberOfIROutputPorts) // success!\n                return new IrOutputPortController(irControllerKey, irDev.IROutputPorts[portNum],\n                    IrDriverPathPrefix + control[\"irFile\"].Value<string>());\n            else\n            {\n                var c = new IrOutputPortController(irControllerKey, null, \"\");\n                Debug.LogMessage(LogEventLevel.Information, c, \"WARNING: device '{0}' IR port {1} out of range\",\n                    portDevKey, portNum);\n                return c;\n            }\n        }*/\n\t}\n\n\t/// <summary>\n\t/// Represents a IrOutPortConfig\n\t/// </summary>\n\tpublic class IrOutPortConfig\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the Port\n\t\t/// </summary>\n\t\t[JsonProperty(\"port\")]\n  \t\tpublic IROutputPort Port { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the FileName\n\t\t/// </summary>\t\t\n\t\t[JsonProperty(\"fileName\")]\n\t\tpublic string FileName { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets a value indicating whether to use bridge join map\n\t\t/// </summary>\n\t\t[JsonProperty(\"useBridgeJoinMap\")]\n\t\tpublic bool UseBridgeJoinMap { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic IrOutPortConfig()\n\t\t{\n\t\t\tFileName = \"\";\t\t\t\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/AudioControlPointListItem.cs",
    "content": "﻿using Crestron.SimplSharpPro;\nusing Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n    /// <summary>\n    /// Represents a AudioControlPointListItem\n    /// </summary>\n    public class AudioControlPointListItem\n    {\n        /// <summary>\n        /// Level controls for this audio control point\n        /// </summary>\n        [JsonProperty(\"levelControls\")]\n        public Dictionary<string, LevelControlListItem> LevelControls { get; set; } = new Dictionary<string, LevelControlListItem>();\n\n        /// <summary>\n        /// Presets for this audio control point\n        /// </summary>\n        [JsonProperty(\"presets\")]\n        public Dictionary<string, PresetListItem> Presets { get; set; } = new Dictionary<string, PresetListItem>();\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/BaseStreamingDeviceProperties.cs",
    "content": "using System;\r\nusing Newtonsoft.Json;\r\n\r\nnamespace PepperDash.Essentials.Core.Config\r\n{\r\n  /// <summary>\r\n  /// Represents the base properties for a streaming device.\r\n  /// </summary>\r\n  public class BaseStreamingDeviceProperties\r\n  {\r\n    /// <summary>\r\n    /// The multicast video address for the streaming device.\r\n    /// </summary>\r\n    [JsonProperty(\"multicastVideoAddress\", NullValueHandling = NullValueHandling.Ignore)]\r\n    public string MulticastVideoAddress { get; set; }\r\n\r\n    /// <summary>\r\n    /// The multicast audio address for the streaming device.\r\n    /// </summary>\r\n    [JsonProperty(\"multicastAudioAddress\", NullValueHandling = NullValueHandling.Ignore)]\r\n    public string MulticastAudioAddress { get; set; }\r\n\r\n    /// <summary>\r\n    /// The URL for the streaming device's media stream.\r\n    /// </summary>\r\n    [JsonProperty(\"streamUrl\", NullValueHandling = NullValueHandling.Ignore)]\r\n    public string StreamUrl { get; set; }\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/BasicConfig.cs",
    "content": "﻿\n\nusing System.Collections.Generic;\nusing System.Linq;\nusing Newtonsoft.Json;\n\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Core.Devices;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n\t/// <summary>\n\t///  Override this and splice on specific room type behavior, as well as other properties\n\t/// </summary>\n\tpublic class BasicConfig\n\t{\n        /// <summary>\n        /// Gets or sets the Info\n        /// </summary>\n\t\t[JsonProperty(\"info\")]\n\t\tpublic InfoConfig Info { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Devices\n        /// </summary>\n\t\t[JsonProperty(\"devices\")]\n\t\tpublic List<DeviceConfig> Devices { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SourceLists\n        /// </summary>\n\t\t[JsonProperty(\"sourceLists\")]\n\t\tpublic Dictionary<string, Dictionary<string, SourceListItem>> SourceLists { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DestinationLists\n        /// </summary>\n        [JsonProperty(\"destinationLists\")]\n        public Dictionary<string, Dictionary<string, DestinationListItem>> DestinationLists { get; set; }\n\n        /// <summary>\n        /// Gets or sets the AudioControlPointLists\n        /// </summary>\n        [JsonProperty(\"audioControlPointLists\")]\n        public Dictionary<string, AudioControlPointListItem> AudioControlPointLists { get; set; }\n\n        /// <summary>\n        /// Gets or sets the CameraLists\n        /// </summary>\n        [JsonProperty(\"cameraLists\")]\n        public Dictionary<string, Dictionary<string, CameraListItem>> CameraLists { get; set; }\n\n        /// <summary>\n        /// Gets or sets the TieLines\n        /// </summary>\n        [JsonProperty(\"tieLines\")]\n  \t\tpublic List<TieLineConfig> TieLines { get; set; }\n\n        /// <summary>\n        /// Gets or sets the JoinMaps\n        /// </summary>\n        [JsonProperty(\"joinMaps\")]\n        public Dictionary<string, JObject> JoinMaps { get; set; }\n\n        /// <summary>\n        /// BasicConfig Constructor\n        /// </summary>\n        public BasicConfig()\n        {\n            Info = new InfoConfig();\n            Devices = new List<DeviceConfig>();\n            SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>();\n            DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>();\n            AudioControlPointLists = new Dictionary<string, AudioControlPointListItem>();\n            CameraLists = new Dictionary<string, Dictionary<string, CameraListItem>>();\n            TieLines = new List<TieLineConfig>();\n            JoinMaps = new Dictionary<string, JObject>();\n        }\n\n\t\t/// <summary>\n\t\t/// Checks SourceLists for a given list and returns it if found. Otherwise, returns null\n\t\t/// </summary>\n\t\tpublic Dictionary<string, SourceListItem> GetSourceListForKey(string key)\n\t\t{\n\t\t\tif (SourceLists == null || string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key))\n\t\t\t\treturn null;\n\t\t\t\n\t\t\treturn SourceLists[key];\n\t\t}\n\n        /// <summary>\n        /// Retrieves a DestinationListItem based on the key\n        /// </summary>\n        /// <param name=\"key\">key of the list to retrieve</param>\n        /// <returns>DestinationList if the key exists, null otherwise</returns>\n\t    public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)\n\t    {\n\t        if (DestinationLists == null || string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))\n\t        {\n\t            return null;\n\t        }\n\n\t        return DestinationLists[key];\n\t    }\n\n        /// <summary>\n        /// Retrieves a AudioControlPointList based on the key\n        /// </summary>\n        /// <param name=\"key\">key of the list to retrieve</param>\n        /// <returns>AudioControlPointList if the key exists, null otherwise</returns>\n        /// <summary>\n        /// GetAudioControlPointListForKey method\n        /// </summary>\n        public AudioControlPointListItem GetAudioControlPointListForKey(string key)\n        {\n            if (AudioControlPointLists == null ||  string.IsNullOrEmpty(key) || !AudioControlPointLists.ContainsKey(key))\n                return null;\n\n            return AudioControlPointLists[key];\n        }\n\n        /// <summary>\n        /// Checks CameraLists for a given list and returns it if found. Otherwise, returns null\n        /// </summary>\n        /// <param name=\"key\">Key of desired camera list</param>\n        public Dictionary<string, CameraListItem> GetCameraListForKey(string key)\n        {\n            if (CameraLists == null || string.IsNullOrEmpty(key) || !CameraLists.ContainsKey(key))\n                return null;\n\n            return CameraLists[key];\n        }\n\n        /// <summary>\n        /// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null\n        /// </summary>\n        /// <param name=\"key\">Key of desired device</param>\n        public DeviceConfig GetDeviceForKey(string key)\n        {\n            if (string.IsNullOrEmpty(key))\n                return null;\n\n            var deviceConfig = Devices.FirstOrDefault(d => d.Key.Equals(key));\n\n            if (deviceConfig != null)\n                return deviceConfig;\n            else\n            {\n                return null;\n            }\n        }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/ConfigPropertiesHelpers.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Core;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n    /// <summary>\n    /// Represents a ConfigPropertiesHelpers\n    /// </summary>\n    public class ConfigPropertiesHelpers\n    {\n        /// <summary>\n        /// GetHasAudio method\n        /// </summary>\n        public static bool GetHasAudio(DeviceConfig deviceConfig)\n        {\n            return deviceConfig.Properties.Value<bool>(\"hasAudio\");\n        }\n\n        /// <summary>\n        /// Returns the value of properties.hasControls, or false if not defined\n        /// </summary>\n        public static bool GetHasControls(DeviceConfig deviceConfig)\n        {\n            return deviceConfig.Properties.Value<bool>(\"hasControls\");\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/DeviceConfig.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharpPro;\n\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n    /// <summary>\n    /// Represents a DeviceConfig\n    /// </summary>\n    public class DeviceConfig\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        [JsonProperty(\"key\")]\n        public string Key { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Uid\n        /// </summary>\n        [JsonProperty(\"uid\")]\n        public int Uid { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Group\n        /// </summary>\n        [JsonProperty(\"group\")]\n        public string Group { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        [JsonProperty(\"type\")]\n        public string Type { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Properties\n        /// </summary>\n        [JsonProperty(\"properties\")]\n        [JsonConverter(typeof(DevicePropertiesConverter))]\n        public JToken Properties { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"dc\">device config</param>\n        public DeviceConfig(DeviceConfig dc)\n        {\n            Key = dc.Key;\n            Uid = dc.Uid;\n            Name = dc.Name;\n            Group = dc.Group;\n            Type = dc.Type;\n\n            Properties = JToken.Parse(dc.Properties.ToString());\n\n            //Properties = JToken.FromObject(dc.Properties);\n        }\n\n        /// <summary>\n        /// Default Constructor\n        /// </summary>\n        public DeviceConfig() { }\n    }\n\n    /// <summary>\n    /// Represents a DevicePropertiesConverter\n    /// </summary>\n    public class DevicePropertiesConverter : JsonConverter\n    {\n\n        /// <summary>\n        /// CanConvert method\n        /// </summary>\n        public override bool CanConvert(Type objectType)\n        {\n            return objectType == typeof(JToken);\n        }\n\n        /// <summary>\n        /// ReadJson method\n        /// </summary>\n        /// <param name=\"reader\">reader to use</param>\n        /// <param name=\"objectType\">type of object being read</param>\n        /// <param name=\"existingValue\">existing value for the object</param>\n        /// <param name=\"serializer\">serializer to use</param>\n        /// <returns></returns>\n        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)\n        {\n            return JToken.ReadFrom(reader);\n        }\n\n        /// <inheritdoc />\n        public override bool CanWrite\n        {\n            get\n            {\n                return false;\n            }\n        }\n\n        /// <summary>\n        /// WriteJson method\n        /// </summary>\n        /// <inheritdoc />\n        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)\n        {\n            throw new NotImplementedException(\"SOD OFF HOSER\");\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs",
    "content": "﻿\n\nusing System;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n\t/// <summary>\n\t/// Loads the ConfigObject from the file\n\t/// </summary>\n\tpublic class ConfigReader\n\t{\n        /// <summary>\n        /// Local Config Present Message\n        /// </summary>\n\t    public const string LocalConfigPresent =\n            @\"\n***************************************************\n************* Using Local config file *************\n***************************************************\";\n\n        /// <summary>\n        /// The loaded config object\n        /// </summary>\n\t\tpublic static EssentialsConfig ConfigObject { get; private set; }\n\n        /// <summary>\n        /// LoadConfig2 method\n        /// </summary>\n\t\tpublic static bool LoadConfig2()\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Loading unmerged system/template portal configuration file.\");\n\t\t\ttry\n\t\t\t{\n                // Check for local config file first\n                var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName;\n\n                bool localConfigFound = false;\n\n                Debug.LogMessage(LogEventLevel.Information, \"Attempting to load Local config file: '{0}'\", filePath);\n\n                // Check for local config directory first\n\n                var configFiles = GetConfigFiles(filePath);\n\n                if (configFiles != null)\n                {\n                    if (configFiles.Length > 1)\n                    {\n                        Debug.LogMessage(LogEventLevel.Information,\n                            \"****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****\");\n                        return false;\n                    }\n                    if(configFiles.Length == 1)\n                    {\n                        localConfigFound = true;\n                        \n                    }\n                }\n                else\n                {\n                    Debug.LogMessage(LogEventLevel.Information,\n                        \"Local Configuration file not present.\", filePath);\n\n                }\n\n                // Check for Portal Config\n                if(!localConfigFound)\n                {\n                    filePath = Global.FilePathPrefix + Global.ConfigFileName;\n\n                    Debug.LogMessage(LogEventLevel.Information, \"Attempting to load Portal config file: '{0}'\", filePath);\n\n                    configFiles = GetConfigFiles(filePath);\n\n                    if (configFiles != null)\n                    {\n                        Debug.LogMessage(LogEventLevel.Verbose, \"{0} config files found matching pattern\", configFiles.Length);\n\n                        if (configFiles.Length > 1)\n                        {\n                            Debug.LogMessage(LogEventLevel.Information,\n                                \"****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****\");\n                            return false;\n                        }\n                        else if (configFiles.Length == 1)\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, \"Found Portal config file: '{0}'\", filePath);\n                        }\n                        else\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, \"No config file found.\");\n                            return false;\n                        }\n                    }\n                    else\n                    {\n                        Debug.LogMessage(LogEventLevel.Information,\n                            \"ERROR: Portal Configuration file not present. Please load file and reset program.\");\n                        return false;\n                    }\n                }\n\n                // Get the actual file path\n                filePath = configFiles[0].FullName;\n\n                // Generate debug statement if using a local file.\n\t\t\t    if (localConfigFound)\n\t\t\t    {\n                    GetLocalFileMessage(filePath);\n\t\t\t    }\n\n                // Read the file\n                using (StreamReader fs = new StreamReader(filePath))\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Loading config file: '{0}'\", filePath);\n\n                    if (localConfigFound)\n                    {\n                        ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject<EssentialsConfig>();\n\n                        Debug.LogMessage(LogEventLevel.Information, \"Successfully Loaded Local Config\");\n\n                        return true;\n                    }                   \n                    else\n                    {\n                        var parsedConfig = JObject.Parse(fs.ReadToEnd());\n\n                        // Check if it's a v2 config (check for \"version\" node)\n                        // this means it's already merged by the Portal API\n                        // from the v2 config tool\n                        var isV2Config = parsedConfig[\"versions\"] != null;\n                        \n                        if (isV2Config)\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, \"Config file is a v2 format, no merge necessary.\");\n                            ConfigObject = parsedConfig.ToObject<EssentialsConfig>();\n                            Debug.LogMessage(LogEventLevel.Information, \"Successfully Loaded v2 Config\");\n                            return true;\n                        }\n\n                        // Extract SystemUrl and TemplateUrl into final config output\n                        ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject<EssentialsConfig>();\n\n                        if (parsedConfig[\"system_url\"] != null)\n                        {\n                            ConfigObject.SystemUrl = parsedConfig[\"system_url\"].Value<string>();\n                        }\n\n                        if (parsedConfig[\"template_url\"] != null)\n                        {\n                            ConfigObject.TemplateUrl = parsedConfig[\"template_url\"].Value<string>();\n                        }\n                    }\n\n                    Debug.LogMessage(LogEventLevel.Information, \"Successfully Loaded Merged Config\");\n\n                    return true;\n                }\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n                Debug.LogMessage(LogEventLevel.Information, \"ERROR: Config load failed: \\r{0}\", e);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n        /// <summary>\n        /// Returns all the files from the directory specified.\n        /// </summary>\n        /// <param name=\"filePath\">path to the directory</param>\n        /// <returns>config files</returns>\n        public static FileInfo[] GetConfigFiles(string filePath)\n        {\n            // Get the directory\n            var dir = Path.GetDirectoryName(filePath);\n\n            if (Directory.Exists(dir))\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"Searching in Directory '{0}'\", dir);\n                // Get the directory info\n                var dirInfo = new DirectoryInfo(dir);\n\n                // Get the file name\n                var fileName = Path.GetFileName(filePath);\n                Debug.LogMessage(LogEventLevel.Debug, \"For Config Files matching: '{0}'\", fileName);\n\n                // Get the files that match from the directory\n                return dirInfo.GetFiles(fileName);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information,\n                    \"Directory not found: \", dir);\n\n                return null;\n            }\n        }\n\n\t\t/// <summary>\n\t\t/// Returns the group for a given device key in config\n\t\t/// </summary>\n\t\t/// <param name=\"key\">Key of the device</param>\n\t\t/// <returns>Group name if the device is found, null otherwise</returns>\n        public static string GetGroupForDeviceKey(string key)\n        {\n            var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));\n            return dev == null ? null : dev.Group;\n        }\n\n\t    private static void GetLocalFileMessage(string filePath)\n\t    {\n            var filePathLength = filePath.Length + 2;\n            var debugStringWidth = filePathLength + 12;\n\n            if (debugStringWidth < 51)\n            {\n                debugStringWidth = 51;\n            }\n            var qualifier = (filePathLength % 2 != 0)\n                ? \" Using Local Config File \"\n                : \" Using Local  Config File \";\n            var bookend1 = (debugStringWidth - qualifier.Length) / 2;\n            var bookend2 = (debugStringWidth - filePathLength) / 2;\n\n\n\t        var newDebugString = new StringBuilder()\n\t            .Append(CrestronEnvironment.NewLine)\n                // Line 1\n\t            .Append(new string('*', debugStringWidth))\n\t            .Append(CrestronEnvironment.NewLine)\n                // Line 2\n\t            .Append(new string('*', debugStringWidth))\n\t            .Append(CrestronEnvironment.NewLine)\n                // Line 3\n\t            .Append(new string('*', 2))\n\t            .Append(new string(' ', debugStringWidth - 4))\n\t            .Append(new string('*', 2))\n\t            .Append(CrestronEnvironment.NewLine)\n                // Line 4\n\t            .Append(new string('*', 2))\n\t            .Append(new string(' ', bookend1 - 2))\n\t            .Append(qualifier)\n\t            .Append(new string(' ', bookend1 - 2))\n\t            .Append(new string('*', 2))\n\t            .Append(CrestronEnvironment.NewLine)\n                // Line 5\n\t            .Append(new string('*', 2))\n\t            .Append(new string(' ', bookend2 - 2))\n\t            .Append(\" \" + filePath + \" \")\n\t            .Append(new string(' ', bookend2 - 2))\n\t            .Append(new string('*', 2))\n\t            .Append(CrestronEnvironment.NewLine)\n                // Line 6\n\t            .Append(new string('*', 2))\n\t            .Append(new string(' ', debugStringWidth - 4))\n\t            .Append(new string('*', 2))\n\t            .Append(CrestronEnvironment.NewLine)\n                // Line 7\n\t            .Append(new string('*', debugStringWidth))\n\t            .Append(CrestronEnvironment.NewLine)\n                // Line 8\n\t            .Append(new string('*', debugStringWidth));\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Found Local config file: '{0}'\", filePath);\n            Debug.LogMessage(LogEventLevel.Information, newDebugString.ToString());\n\t    }\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing Crestron.SimplSharp.Net.Http;\nusing Crestron.SimplSharpPro.Diagnostics;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n    /// <summary>\n    /// ConfigUpdater class\n    /// </summary>\n    [Obsolete(\"ConfigUpdater is no longer supported and will be removed in a future release.\")]\n    public static class ConfigUpdater\n    {\n        /// <summary>\n        /// ConfigStatusChanged event\n        /// </summary>\n        public static event EventHandler<ConfigStatusEventArgs> ConfigStatusChanged;\n\n        /// <summary>\n        /// GetConfigFromServer method\n        /// </summary>\n        /// <param name=\"url\">URL of the config server</param>\n        public static void GetConfigFromServer(string url)\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Attempting to get new config from '{0}'\", url);\n\n            // HTTP GET \n            var req = new HttpClientRequest();\n\n            try\n            {\n                req.RequestType = RequestType.Get;\n                req.Url.Parse(url);\n\n                new HttpClient().DispatchAsync(req, (r, e) =>\n                    {\n                        if (e == HTTP_CALLBACK_ERROR.COMPLETED)\n                        {\n                            if (r.Code == 200)\n                            {\n                                var newConfig = r.ContentString;\n\n                                OnStatusUpdate(eUpdateStatus.ConfigFileReceived);\n\n                                ArchiveExistingPortalConfigs();\n\n                                CheckForLocalConfigAndDelete();\n\n                                WriteConfigToFile(newConfig);\n\n                                RestartProgram();\n                            }\n                            else\n                            {\n                                Debug.LogMessage(LogEventLevel.Information, \"Config Update Process Stopped. Failed to get config file from server: {0}\", r.Code);\n                                OnStatusUpdate(eUpdateStatus.UpdateFailed);\n                            }\n                        }\n                        else\n                            Debug.LogMessage(LogEventLevel.Information, \"Request for config from Server Failed: {0}\", e);\n                    });\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"Error Getting Config from Server: {0}\", e);\n            }\n\n        }\n\n        static void OnStatusUpdate(eUpdateStatus status)\n        {\n            var handler = ConfigStatusChanged;\n\n            if (handler != null)\n            {\n                handler(typeof(ConfigUpdater), new ConfigStatusEventArgs(status));\n            }\n        }\n\n        static void WriteConfigToFile(string configData)\n        {\n            var filePath = Global.FilePathPrefix + \"configurationFile-updated.json\";\n\n            try\n            {\n                var config = JObject.Parse(configData).ToObject<EssentialsConfig>();\n\n                ConfigWriter.WriteFile(filePath, configData);\n\n                OnStatusUpdate(eUpdateStatus.WritingConfigFile);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"Error parsing new config: {0}\", e);\n\n                OnStatusUpdate(eUpdateStatus.UpdateFailed);\n            }\n        }\n\n        /// <summary>\n        /// Checks for any existing portal config files and archives them\n        /// </summary>\n        static void ArchiveExistingPortalConfigs()\n        {\n            var filePath = Global.FilePathPrefix + Global.ConfigFileName;\n\n            var configFiles = ConfigReader.GetConfigFiles(filePath);\n\n            if (configFiles != null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Existing config files found.  Moving to Archive folder.\");\n\n                OnStatusUpdate(eUpdateStatus.ArchivingConfigs);\n\n                MoveFilesToArchiveFolder(configFiles);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"No Existing config files found in '{0}'. Nothing to archive\", filePath);\n            }\n        }\n\n        /// <summary>\n        /// Checks for presence of archive folder and if found deletes contents.\n        /// Moves any config files to the archive folder and adds a .bak suffix\n        /// </summary>\n        /// <param name=\"files\"></param>\n        static void MoveFilesToArchiveFolder(FileInfo[] files)\n        {\n            string archiveDirectoryPath = Global.FilePathPrefix + \"archive\";\n\n            if (!Directory.Exists(archiveDirectoryPath))\n            {\n                // Directory does not exist, create it\n                Directory.Create(archiveDirectoryPath);\n            }\n            else\n            {\n                // Directory exists, first clear any contents\n                var archivedConfigFiles = ConfigReader.GetConfigFiles(archiveDirectoryPath + Global.DirectorySeparator + Global.ConfigFileName + \".bak\");\n\n                if (archivedConfigFiles != null && archivedConfigFiles.Length > 0)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"{0} Existing files found in archive folder.  Deleting.\", archivedConfigFiles.Length);\n\n                    for (int i = 0; i < archivedConfigFiles.Length; i++)\n                    {\n                        var file = archivedConfigFiles[i];\n                        Debug.LogMessage(LogEventLevel.Information, \"Deleting archived file: '{0}'\", file.FullName);\n                        file.Delete();\n                    }\n                }\n\n            }\n\n            // Move any files from the program folder to the archive folder\n            foreach (var file in files)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Moving config file '{0}' to archive folder\", file.FullName);\n\n                // Moves the file and appends the .bak extension\n                var fileDest = archiveDirectoryPath + \"/\" + file.Name + \".bak\";\n                if (!File.Exists(fileDest))\n                {\n                    file.MoveTo(fileDest);\n                }\n                else\n                    Debug.LogMessage(LogEventLevel.Information, \"Cannot move file to archive folder.  Existing file already exists with same name: '{0}'\", fileDest);\n            }\n        }\n\n        /// <summary>\n        /// Checks for LocalConfig folder in file system and deletes if found\n        /// </summary>\n        static void CheckForLocalConfigAndDelete()\n        {\n            var folderPath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder;\n\n            if (Directory.Exists(folderPath))\n            {\n                OnStatusUpdate(eUpdateStatus.DeletingLocalConfig);\n                Directory.Delete(folderPath);\n                Debug.LogMessage(LogEventLevel.Information, \"Local Config Found in '{0}'. Deleting.\", folderPath);\n            }\n        }\n\n        /// <summary>\n        /// Connects to the processor via SSH and restarts the program\n        /// </summary>\n        static void RestartProgram()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Attempting to Reset Program\");\n\n            OnStatusUpdate(eUpdateStatus.RestartingProgram);\n\n            string response = string.Empty;\n\n            CrestronConsole.SendControlSystemCommand(string.Format(\"progreset -p:{0}\", InitialParametersClass.ApplicationNumber), ref response);\n\n            Debug.LogMessage(LogEventLevel.Debug, \"Console Response: {0}\", response);\n        }\n\n    }\n\n    /// <summary>\n    /// Enumeration of eUpdateStatus values\n    /// </summary>\n    public enum eUpdateStatus\n    {\n        /// <summary>\n        /// UpdateStarted status\n        /// </summary>\n        UpdateStarted,\n\n        /// <summary>\n        /// ConfigFileReceived status\n        /// </summary>\n        ConfigFileReceived,\n\n        /// <summary>\n        /// ArchivingConfigs status\n        /// </summary>\n        ArchivingConfigs,\n\n        /// <summary>\n        /// DeletingLocalConfig status\n        /// </summary>\n        DeletingLocalConfig,\n\n        /// <summary>\n        /// WritingConfigFile status\n        /// </summary>\n        WritingConfigFile,\n\n        /// <summary>\n        /// RestartingProgram status\n        /// </summary>\n        RestartingProgram,\n\n        /// <summary>\n        /// UpdateSucceeded status\n        /// </summary>\n        UpdateSucceeded,\n\n        /// <summary>\n        /// UpdateFailed status\n        /// </summary>\n        UpdateFailed\n    }\n\n    /// <summary>\n    /// Represents a ConfigStatusEventArgs\n    /// </summary>\n    public class ConfigStatusEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the UpdateStatus\n        /// </summary>\n        public eUpdateStatus UpdateStatus { get; private set; }\n\n        /// <summary>\n        /// ConfigStatusEventArgs Constructor\n        /// </summary>\n        /// <param name=\"status\"></param>\n        public ConfigStatusEventArgs(eUpdateStatus status)\n        {\n            UpdateStatus = status;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n    /// <summary>\n    /// Responsible for updating config at runtime, and writing the updates out to a local file\n    /// </summary>\n    public class ConfigWriter\n    {\n        /// <summary>\n        /// LocalConfigFolder constant\n        /// </summary>\n        public const string LocalConfigFolder = \"LocalConfig\";\n\n        /// <summary>\n        /// WriteTimeout constant\n        /// </summary>\n        public const long WriteTimeout = 30000;\n\n        /// <summary>\n        /// WriteTimer variable\n        /// </summary>\n        public static CTimer WriteTimer;\n\n\t\tstatic CCriticalSection fileLock = new CCriticalSection();\n\n        /// <summary>\n        /// Updates the config properties of a device\n        /// </summary>\n        /// <param name=\"deviceKey\">The key of the device to update</param>\n        /// <param name=\"properties\">The new properties for the device</param>\n        /// <returns>True if the update was successful, otherwise false</returns>\n        public static bool UpdateDeviceProperties(string deviceKey, JToken properties)\n        {\n            bool success = false;\n\n            // Get the current device config\n            var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(deviceKey));\n\n            if (deviceConfig != null)\n            {\n                // Replace the current properties JToken with the new one passed into this method\n                deviceConfig.Properties = properties;\n\n                Debug.LogMessage(LogEventLevel.Debug, \"Updated properties of device: '{0}'\", deviceKey);\n\n                success = true;\n            }\n\n            ResetTimer();\n\n            return success;\n        }\n\n        /// <summary>\n        /// UpdateDeviceConfig method\n        /// </summary>\n        /// <param name=\"config\">The new device config</param>\n        /// <returns>True if the update was successful, otherwise false</returns>\n        public static bool UpdateDeviceConfig(DeviceConfig config)\n        {\n            bool success = false;\n\n            var deviceConfigIndex = ConfigReader.ConfigObject.Devices.FindIndex(d => d.Key.Equals(config.Key));\n\n            if (deviceConfigIndex >= 0)\n            {\n                ConfigReader.ConfigObject.Devices[deviceConfigIndex] = config;\n\n                Debug.LogMessage(LogEventLevel.Debug, \"Updated config of device: '{0}'\", config.Key);\n\n                success = true;\n            }\n\n            ResetTimer();\n\n            return success;\n        }\n\n        /// <summary>\n        /// UpdateRoomConfig method\n        /// </summary>\n        /// <param name=\"config\">The new room config</param>\n        /// <returns>True if the update was successful, otherwise false</returns>\n        public static bool UpdateRoomConfig(DeviceConfig config)\n        {\n            bool success = false;\n\n\t\t\tvar roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key));\n\n\t\t\tif (roomConfigIndex >= 0)\n            {\n                ConfigReader.ConfigObject.Rooms[roomConfigIndex] = config;\n\n                Debug.LogMessage(LogEventLevel.Debug, \"Updated room of device: '{0}'\", config.Key);\n\n                success = true;\n            }\n\n            ResetTimer();\n\n            return success;\n        }\n\n        /// <summary>\n        /// Resets (or starts) the write timer\n        /// </summary>\n        static void ResetTimer()\n        {\n            if (WriteTimer == null)\n                WriteTimer = new CTimer(WriteConfigFile, WriteTimeout);\n\n            WriteTimer.Reset(WriteTimeout);\n\n            Debug.LogMessage(LogEventLevel.Debug, \"Config File write timer has been reset.\");\n        }\n\n        /// <summary>\n        /// Writes the current config to a file in the LocalConfig subfolder\n        /// </summary>\n        private static void WriteConfigFile(object o)\n        {\n            var filePath = Global.FilePathPrefix + LocalConfigFolder + Global.DirectorySeparator + \"configurationFile.json\";\n\n            var configData = JsonConvert.SerializeObject(ConfigReader.ConfigObject);\n\n            WriteFile(filePath, configData);\n        }\n\n        /// <summary>\n        /// Writes the current config data to a file\n        /// </summary>\n        /// <param name=\"filePath\">The file path to write to</param>\n        /// <param name=\"configData\">The config data to write</param>\n        public static void WriteFile(string filePath, string configData)\n        {\n            if (WriteTimer != null)\n                WriteTimer.Stop();\n\n            Debug.LogMessage(LogEventLevel.Information, \"Writing Configuration to file\");\n\n            Debug.LogMessage(LogEventLevel.Information, \"Attempting to write config file: '{0}'\", filePath);\n\n            try\n            {\n                if (fileLock.TryEnter())\n                {\n                    using (StreamWriter sw = new StreamWriter(filePath))\n                    {\n                        sw.Write(configData);\n                        sw.Flush();\n                    }\n                }\n                else\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Unable to enter FileLock\");\n                }\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Error: Config write failed: \\r{0}\", e);\n            }\n            finally\n            {\n                if (fileLock != null && !fileLock.Disposed)\n                    fileLock.Leave();\n\n            }\n        }\n\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text.RegularExpressions;\n\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n    /// <summary>\n    /// Loads the ConfigObject from the file\n    /// </summary>\n    public class EssentialsConfig : BasicConfig\n    {\n        /// <summary>\n        /// Gets or sets the SystemUrl\n        /// </summary>\n        [JsonProperty(\"system_url\")]\n        public string SystemUrl { get; set; }\n\n        /// <summary>\n        /// Gets or sets the TemplateUrl\n        /// </summary>\n        [JsonProperty(\"template_url\")]\n        public string TemplateUrl { get; set; }\n\n        /// <summary>\n        /// Gets the SystemUuid extracted from the SystemUrl\n        /// </summary>\n\t\t[JsonProperty(\"systemUuid\")]\n        public string SystemUuid\n        {\n            get\n            {\n                string uuid;\n\n                if (string.IsNullOrEmpty(SystemUrl))\n                {\n                    uuid = \"missing url\";\n                }\n                else if (SystemUrl.Contains(\"#\"))\n                {\n                    var result = Regex.Match(SystemUrl, @\"https?:\\/\\/.*\\/systems\\/(.*)\\/#.*\");\n                    uuid = result.Groups[1].Value;\n                }\n                else if (SystemUrl.Contains(\"detail\"))\n                {\n                    var result = Regex.Match(SystemUrl, @\"https?:\\/\\/.*\\/systems\\/detail\\/(.*)\\/.*\");\n                    uuid = result.Groups[1].Value;\n                }\n                else\n                {\n                    var result = Regex.Match(SystemUrl, @\"https?:\\/\\/.*\\/systems\\/(.*)\\/.*\");\n                    uuid = result.Groups[1].Value;\n                }\n\n                return uuid;\n            }\n        }\n\n        /// <summary>\n        /// Gets the TemplateUuid extracted from the TemplateUrl\n        /// </summary>\n\t\t[JsonProperty(\"templateUuid\")]\n        public string TemplateUuid\n        {\n            get\n            {\n                string uuid;\n\n                if (string.IsNullOrEmpty(TemplateUrl))\n                {\n                    uuid = \"missing template url\";\n                }\n                else if (TemplateUrl.Contains(\"#\"))\n                {\n                    var result = Regex.Match(TemplateUrl, @\"https?:\\/\\/.*\\/templates\\/(.*)\\/#.*\");\n                    uuid = result.Groups[1].Value;\n                }\n                else if (TemplateUrl.Contains(\"detail\"))\n                {\n                    var result = Regex.Match(TemplateUrl, @\"https?:\\/\\/.*\\/system-templates\\/detail\\/(.*)\\/system-template-versions\\/detail\\/(.*)\\/.*\");\n                    uuid = result.Groups[2].Value;\n                }\n                else\n                {\n                    var result = Regex.Match(TemplateUrl, @\"https?:\\/\\/.*\\/system-templates\\/(.*)\\/system-template-versions\\/(.*)\\/.*\");\n                    uuid = result.Groups[2].Value;\n                }\n\n                return uuid;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the Rooms\n        /// </summary>\n        [JsonProperty(\"rooms\")]\n        public List<DeviceConfig> Rooms { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Versions\n        /// </summary>\n        public VersionData Versions { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"EssentialsConfig\"/> class.\n        /// </summary>\n        public EssentialsConfig()\n            : base()\n        {\n            Rooms = new List<DeviceConfig>();\n        }\n    }\n\n    /// <summary>\n    /// Represents version data for Essentials and its packages\n    /// </summary>\n    public class VersionData\n    {\n        /// <summary>\n        /// Gets or sets the Essentials version\n        /// </summary>\n        [JsonProperty(\"essentials\")]\n        public NugetVersion Essentials { get; set; }\n\n        /// <summary>\n        /// Gets or sets the list of Packages\n        /// </summary>\n        [JsonProperty(\"packages\")]\n        public List<NugetVersion> Packages { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"VersionData\"/> class.\n        /// </summary>\n        public VersionData()\n        {\n            Packages = new List<NugetVersion>();\n        }\n    }\n\n    /// <summary>\n    /// Represents a NugetVersion\n    /// </summary>\n    public class NugetVersion\n    {\n        /// <summary>\n        /// Gets or sets the Version\n        /// </summary>\n        [JsonProperty(\"version\")]\n        public string Version { get; set; }\n\n        /// <summary>\n        /// Gets or sets the PackageId\n        /// </summary>\n        [JsonProperty(\"packageId\")]\n        public string PackageId { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a SystemTemplateConfigs\n    /// </summary>\n    public class SystemTemplateConfigs\n    {\n        /// <summary>\n        /// Gets or sets the System\n        /// </summary>\n\t\tpublic EssentialsConfig System { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Template\n        /// </summary>\n\t\tpublic EssentialsConfig Template { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/ILoadConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for ILoadConfig\n    /// </summary>\n    public interface ILoadConfig\n    {\n        /// <summary>\n        /// GoWithLoad method\n        /// </summary>\n        void GoWithLoad();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/InfoConfig.cs",
    "content": "﻿\n\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n    /// <summary>\n    /// Represents the info section of a Config file\n    /// </summary>\n    public class InfoConfig\n\t{\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n\t\t[JsonProperty(\"name\")]\n\t\tpublic string Name { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Date\n        /// </summary>\n\t\t[JsonProperty(\"date\")]\n\t\tpublic DateTime Date { get; set; }\n\t\t\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n\t\t[JsonProperty(\"type\")]\n\t\tpublic string Type { get; set; }\n\t\t\n        /// <summary>\n        /// Gets or sets the Version\n        /// </summary>\n\t\t[JsonProperty(\"version\")]\n\t\tpublic string Version { get; set; }\n\n        /// <summary>\n        /// Gets or sets the RuntimeInfo\n        /// </summary>\n        [JsonProperty(\"runtimeInfo\")]\n        public RuntimeInfo RuntimeInfo { get; set; } \n\t\t\n        /// <summary>\n        /// Gets or sets the Comment\n        /// </summary>\n\t\t[JsonProperty(\"comment\")]\n\t\tpublic string Comment { get; set; }\n\n        /// <summary>\n        /// Gets or sets the HostName\n        /// </summary>\n        [JsonProperty(\"hostname\")]\n        public string HostName { get; set; }\n\n        /// <summary>\n        /// Gets or sets the AppNumber\n        /// </summary>\n        [JsonProperty(\"appNumber\")]\n        public uint AppNumber { get; set; }\n\n        /// <summary>\n        /// InfoConfig Constructor\n        /// </summary>\n\t\tpublic InfoConfig()\n\t\t{\n\t\t\tName = \"\";\n\t\t\tDate = DateTime.Now;\n\t\t\tType = \"\";\n\t\t\tVersion = \"\";\n\t\t\tComment = \"\";\n            HostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);\n            AppNumber = InitialParametersClass.ApplicationNumber;\n\n            RuntimeInfo = new RuntimeInfo();\n\t\t}\n\t}\n\n    \n    /// <summary>\n    /// Represents a RuntimeInfo\n    /// </summary>\n    public class RuntimeInfo\n    {\n        /// <summary>\n        /// The name of the running application\n        /// </summary>\n        [JsonProperty(\"appName\")]\n        public string AppName {get; set;}\n        //{\n        //    get\n        //    {\n        //        return Assembly.GetExecutingAssembly().GetName().Name;\n        //    }\n        //} \n\n        /// <summary>\n        /// The Assembly version of the running application\n        /// </summary>\n        [JsonProperty(\"assemblyVersion\")]\n        public string AssemblyVersion {get; set;}\n        //{\n        //    get\n        //    {\n        //        var version = Assembly.GetExecutingAssembly().GetName().Version;\n        //        return string.Format(\"{0}.{1}.{2}\", version.Major, version.Minor, version.Build);\n        //    }\n        //} \n\n        /// <summary>, \n        /// The OS Version of the processor (Firmware Version)\n        /// </summary>\n        [JsonProperty(\"osVersion\")]\n        public string OsVersion {get; set;}\n\t\t//{\n\t\t//    get\n\t\t//    {\n\t\t//        return Crestron.SimplSharp.CrestronEnvironment.OSVersion.Firmware;\n\t\t//    }\n\t\t//} \n\n        /// <summary>\n        /// The information gathered by the processor at runtime about it's NICs and their IP addresses.   \n        /// </summary>\n        [JsonProperty(\"ipInfo\")]\n        public Dictionary<short, EthernetAdapterInfo> IpInfo\n        {\n            get\n            {\n                return Global.EthernetAdapterInfoCollection;\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Config/SourceDevicePropertiesConfigBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n /// <summary>\n /// Represents a SourceDevicePropertiesConfigBase\n /// </summary>\n\tpublic class SourceDevicePropertiesConfigBase\n\t{\n  /// <summary>\n  /// Gets or sets the DisableSharing\n  /// </summary>\n\t\tpublic bool DisableSharing { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Core.JsonStandardObjects;\nusing PepperDash.Essentials.Core.Bridges;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Abstract base class for Crestron GenericBase devices\n    /// </summary>\n\tpublic abstract class CrestronGenericBaseDevice : EssentialsDevice, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking\n\t{\n        /// <summary>\n        /// Gets or sets the Hardware\n        /// </summary>\n\t    protected GenericBase Hardware;\n\n        /// <summary>\n        /// Gets or sets the Feedbacks\n        /// </summary>\n        public FeedbackCollection<Feedback> Feedbacks { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IsOnline\n        /// </summary>\n\t\tpublic BoolFeedback IsOnline { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IsRegistered\n        /// </summary>\n\t\tpublic BoolFeedback IsRegistered { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IpConnectionsText\n        /// </summary>\n\t\tpublic StringFeedback IpConnectionsText { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the PreventRegistration\n        /// </summary>\n\t\tpublic bool PreventRegistration { get; protected set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key of the device</param>\n        /// <param name=\"name\">name of the device</param>\n        /// <param name=\"hardware\">hardware instance</param>\n\t    protected CrestronGenericBaseDevice(string key, string name, GenericBase hardware)\n            : base(key, name)\n        {\n            Feedbacks = new FeedbackCollection<Feedback>();\n\n            Hardware = hardware;\n            IsOnline = new BoolFeedback(\"IsOnlineFeedback\", () => Hardware.IsOnline);\n            IsRegistered = new BoolFeedback(\"IsRegistered\", () => Hardware.Registered);\n            IpConnectionsText = new StringFeedback(\"IpConnectionsText\", () => Hardware.ConnectedIpList != null ? string.Join(\",\", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()) : string.Empty);\n            AddToFeedbackList(IsOnline, IpConnectionsText);\n\n            CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);\n        }\n\n        /// <summary>\n        /// Constructor without hardware instance\n        /// </summary>\n        /// <param name=\"key\">key of the device</param>\n        /// <param name=\"name\">name of the device</param>\n        protected CrestronGenericBaseDevice(string key, string name)\n            : base(key, name)\n        {\n            Feedbacks = new FeedbackCollection<Feedback>();\n\n        }\n\n        /// <summary>\n        /// Registers the Crestron GenericBase hardware instance\n        /// </summary>\n        /// <param name=\"hardware\">hardware instance</param>\n\t    protected void RegisterCrestronGenericBase(GenericBase hardware)\n\t    {\n            Hardware = hardware;\n            IsOnline = new BoolFeedback(\"IsOnlineFeedback\", () => Hardware.IsOnline);\n            IsRegistered = new BoolFeedback(\"IsRegistered\", () => Hardware.Registered);\n            IpConnectionsText = new StringFeedback(\"IpConnectionsText\", () => Hardware.ConnectedIpList != null ? string.Join(\",\", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()) : string.Empty);\n            AddToFeedbackList(IsOnline, IpConnectionsText);\n\n            CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);\n\t    }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n\t\tpublic override bool CustomActivate()\n\t\t{\n            Debug.LogMessage(LogEventLevel.Information, this, \"Activating\");\n            if (!PreventRegistration)\n            {\n                //Debug.LogMessage(LogEventLevel.Debug, this, \"  Does not require registration. Skipping\");\n\n                if (Hardware.Registerable && !Hardware.Registered)\n                {\n                    var response = Hardware.RegisterWithLogging(Key);\n                    if (response != eDeviceRegistrationUnRegistrationResponse.Success)\n                    {\n                        //Debug.LogMessage(LogEventLevel.Information, this, \"ERROR: Cannot register Crestron device: {0}\", response);\n                        return false;\n                    }\n                }\n\n                IsRegistered.FireUpdate();\n            }\n            else\n            {\n                AddPostActivationAction(() =>\n                    {\n                        if (Hardware.Registerable && !Hardware.Registered)\n                        {\n                            var response = Hardware.RegisterWithLogging(Key);\n                        }\n\n                        IsRegistered.FireUpdate();\n                    });\n            }\n\n            foreach (var f in Feedbacks)\n            {\n                f.FireUpdate();\n            }\n\n            Hardware.OnlineStatusChange += Hardware_OnlineStatusChange;\n            CommunicationMonitor.Start();    \n\n\t\t\treturn base.CustomActivate();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// This disconnects events and unregisters the base hardware device.\n\t\t/// </summary>\n\t\t/// <returns>true if successful, otherwise false</returns>\n\t\tpublic override bool Deactivate()\n\t\t{\n\t\t\tCommunicationMonitor.Stop();\n\t\t\tHardware.OnlineStatusChange -= Hardware_OnlineStatusChange;\n\n\t\t\tvar success = Hardware.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;\n\n            IsRegistered.FireUpdate();\n\n            return success;\n\t\t}\n\n\t    /// <summary>\n        /// Adds feedback(s) to the list\n        /// </summary>\n        /// <param name=\"newFbs\">feedback(s) to be added to the list</param>\n        public void AddToFeedbackList(params Feedback[] newFbs)\n        {\n            foreach (var f in newFbs)\n            {\n                if (f == null) continue;\n\n                if (!Feedbacks.Contains(f))\n                {\n                    Feedbacks.Add(f);\n                }\n            }\n        }\n\n\t\tvoid Hardware_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)\n\t\t{\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"OnlineStatusChange Event.  Online = {0}\", args.DeviceOnLine);\n\n            if (!Hardware.Registered)\n            {\n                return;  // protects in cases where device has been unregistered and feedbacks would attempt to access null sigs.\n            }\n\n            foreach (var feedback in Feedbacks)\n            {\n                if (feedback != null)\n                    feedback.FireUpdate();\n            }         \n\t\t}\n\n\t\t#region IStatusMonitor Members\n\n        /// <summary>\n        /// Gets or sets the CommunicationMonitor\n        /// </summary>\n\t\tpublic StatusMonitorBase CommunicationMonitor { get; private set; }\n\t\t#endregion\n\n        #region IUsageTracking Members\n\n        /// <summary>\n        /// Gets or sets the UsageTracker\n        /// </summary>\n        public UsageTracking UsageTracker { get; set; }\n\n        #endregion\n\t}\n\n    /// <summary>\n    /// Abstract base class for Crestron GenericBase devices that are bridgeable\n    /// </summary>\n    public abstract class CrestronGenericBridgeableBaseDevice : CrestronGenericBaseDevice, IBridgeAdvanced\n    {\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key of the device</param>\n        /// <param name=\"name\">name of the device</param>\n        /// <param name=\"hardware\">hardware instance</param>\n        protected CrestronGenericBridgeableBaseDevice(string key, string name, GenericBase hardware) : base(key, name, hardware)\n        {\n        }\n\n        /// <summary>\n        /// Constructor without hardware instance\n        /// </summary>\n        /// <param name=\"key\">key of the device</param>\n        /// <param name=\"name\">name of the device</param>\n        protected CrestronGenericBridgeableBaseDevice(string key, string name)\n            : base(key, name)\n        {\n        }\n\n        /// <summary>\n        /// Links to API\n        /// </summary>\n        /// <param name=\"trilist\">the trilist</param>\n        /// <param name=\"joinStart\">the starting join number</param>\n        /// <param name=\"joinMapKey\">the join map key</param>\n        /// <param name=\"bridge\">the bridge instance</param>\n        public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);\n    }\n\n\n\t//***********************************************************************************\n    /// <summary>\n    /// Represents a CrestronGenericBaseDeviceEventIds\n    /// </summary>\n\tpublic class CrestronGenericBaseDeviceEventIds\n\t{\n        /// <summary>\n        /// IsOnline event ID\n        /// </summary>\n\t\tpublic const uint IsOnline = 1;\n\n        /// <summary>\n        /// IpConnectionsText event ID\n        /// </summary>\n\t\tpublic const uint IpConnectionsText =2;\n\t}\n\n\t/// <summary>\n\t/// Adds logging to Register() failure\n\t/// </summary>\n\tpublic static class GenericBaseExtensions\n\t{\n        /// <summary>\n        /// RegisterWithLogging method\n        /// </summary>\n        /// <param name=\"device\">the GenericBase device</param>\n        /// <param name=\"key\">the device key</param>\n\t\tpublic static eDeviceRegistrationUnRegistrationResponse RegisterWithLogging(this GenericBase device, string key)\n\t\t{\n\t\t    var result = device.Register();\n\t\t\t\n\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Register device result: '{0}', type '{1}', result {2}\", key, device, result);\n\t\t\t//if (result != eDeviceRegistrationUnRegistrationResponse.Success)\n\t\t\t//{\n\t\t\t//    Debug.LogMessage(LogEventLevel.Information, \"Cannot register device '{0}': {1}\", key, result);\n\t\t\t//}\n\t\t\treturn result;\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs.orig",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// A bridge class to cover the basic features of GenericBase hardware\n\t/// </summary>\n\tpublic class CrestronGenericBaseDevice : Device, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking\n\t{\n\t\tpublic virtual GenericBase Hardware { get; protected set; }\n\n\t\tpublic BoolFeedback IsOnline { get; private set; }\n\t\tpublic BoolFeedback IsRegistered { get; private set; }\n\t\tpublic StringFeedback IpConnectionsText { get; private set; }\n\n\t\tpublic CrestronGenericBaseDevice(string key, string name, GenericBase hardware)\n\t\t\t: base(key, name)\n\t\t{\n\t\t\tHardware = hardware;\n\t\t\tIsOnline = new BoolFeedback(CommonBoolCue.IsOnlineFeedback, () => Hardware.IsOnline);\n\t\t\tIsRegistered = new BoolFeedback(new Cue(\"IsRegistered\", 0, eCueType.Bool), () => Hardware.Registered);\n\t\t\tIpConnectionsText = new StringFeedback(CommonStringCue.IpConnectionsText, () => \n\t\t\t\tstring.Join(\",\", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()));\n\t\t\tCommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Make sure that overriding classes call this!\n\t\t/// Registers the Crestron device, connects up to the base events, starts communication monitor\n\t\t/// </summary>\n\t\tpublic override bool CustomActivate()\n\t\t{\n            Debug.LogMessage(LogEventLevel.Information, this, \"Activating\");\n            var response = Hardware.RegisterWithLogging(Key);\n            if (response != eDeviceRegistrationUnRegistrationResponse.Success)\n            {\n<<<<<<< HEAD\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"ERROR: Cannot register Crestron device: {0}\", response);\n\t\t\t\treturn false;\n\t\t\t}\n=======\n                Debug.LogMessage(LogEventLevel.Information, this, \"ERROR: Cannot register Crestron device: {0}\", response);\n                return false;\n            }\n>>>>>>> origin/feature/ecs-342-neil\n            Hardware.OnlineStatusChange += new OnlineStatusChangeEventHandler(Hardware_OnlineStatusChange);\n            CommunicationMonitor.Start();    \n\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// This disconnects events and unregisters the base hardware device.\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic override bool Deactivate()\n\t\t{\n\t\t\tCommunicationMonitor.Stop();\n\t\t\tHardware.OnlineStatusChange -= Hardware_OnlineStatusChange;\n\n\t\t\treturn Hardware.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns a list containing the Outputs that we want to expose.\n\t\t/// </summary>\n\t\tpublic virtual List<Feedback> Feedbacks\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn new List<Feedback>\n\t\t\t\t{\n\t\t\t\t\tIsOnline,\n\t\t\t\t\tIsRegistered,\n\t\t\t\t\tIpConnectionsText\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tvoid Hardware_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)\n\t\t{\n\t\t\tIsOnline.FireUpdate();\n\t\t}\n\n\t\t#region IStatusMonitor Members\n\n\t\tpublic StatusMonitorBase CommunicationMonitor { get; private set; }\n\t\t#endregion\n\n        #region IUsageTracking Members\n\n        public UsageTracking UsageTracker { get; set; }\n\n        #endregion\n\t}\n\n\t//***********************************************************************************\n\tpublic class CrestronGenericBaseDeviceEventIds\n\t{\n\t\tpublic const uint IsOnline = 1;\n\t\tpublic const uint IpConnectionsText =2;\n\t}\n\n\t/// <summary>\n\t/// Adds logging to Register() failure\n\t/// </summary>\n\tpublic static class GenericBaseExtensions\n\t{\n\t\tpublic static eDeviceRegistrationUnRegistrationResponse RegisterWithLogging(this GenericBase device, string key)\n\t\t{\n\t\t\tvar result = device.Register();\n\t\t\tif (result != eDeviceRegistrationUnRegistrationResponse.Success)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Cannot register device '{0}': {1}\", key, result);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\n\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n\n    /// <summary>\n    /// Represents a GenericDigitalInputDevice\n    /// </summary>\n    /// [Description(\"Wrapper class for Digital Input\")]\n    public class GenericDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IHasFeedback\n    {\n        private DigitalInput inputPort;\n        private readonly bool invertState;\n\n        /// <summary>\n        /// Gets or sets the InputStateFeedback\n        /// </summary>\n        public BoolFeedback InputStateFeedback { get; private set; }\n\n        /// <inheritdoc />\n        public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"GenericDigitalInputDevice\"/> class.\n        /// </summary>\n        /// <param name=\"key\">key for device</param>\n        /// <param name=\"name\">name for device</param>\n        /// <param name=\"postActivationFunc\">function to call after activation. Should return the DigitalInput</param>\n        /// <param name=\"config\">config for device</param>\n        public GenericDigitalInputDevice(string key, string name, Func<IOPortConfig, DigitalInput> postActivationFunc,\n            IOPortConfig config)\n            : base(key, name)\n        {\n            invertState = string.Equals(config.CircuitType, \"NC\", StringComparison.OrdinalIgnoreCase);\n\n            InputStateFeedback = new BoolFeedback(\"inputState\", () => invertState ? !inputPort.State : inputPort.State);\n\n            AddPostActivationAction(() =>\n            {\n                inputPort = postActivationFunc(config);\n\n                inputPort.Register();\n\n                inputPort.StateChange += InputPort_StateChange;\n            });\n        }\n\n        #region Events\n\n        void InputPort_StateChange(DigitalInput digitalInput, DigitalInputEventArgs args)\n        {\n            InputStateFeedback.FireUpdate();\n        }\n\n        #endregion\n\n        #region PreActivate\n\n        private static DigitalInput GetDigitalInput(IOPortConfig dc)\n        {\n\n            if (dc.PortDeviceKey.Equals(\"processor\"))\n            {\n                if (!Global.ControlSystem.SupportsDigitalInput)\n                {\n                    Debug.LogError(\"GetDigitalInput: Processor does not support Digital Inputs\");\n                    return null;\n                }\n\n                return Global.ControlSystem.DigitalInputPorts[dc.PortNumber];\n            }\n\n            if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IDigitalInputPorts ioPortDevice))\n            {\n                Debug.LogError(\"GetDigitalInput: Device {key} is not a valid device\", dc.PortDeviceKey);\n                return null;\n            }\n\n            if (dc.PortNumber > ioPortDevice.NumberOfDigitalInputPorts)\n            {\n                Debug.LogError(\"GetDigitalInput: Device {key} does not contain a digital input port {port}\", dc.PortDeviceKey, dc.PortNumber);\n                return null;\n            }\n\n            return ioPortDevice.DigitalInputPorts[dc.PortNumber];\n        }\n\n        #endregion\n\n        #region Bridge Linking\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new IDigitalInputJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<IDigitalInputJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                this.LogWarning(\"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            try\n            {\n                this.LogDebug(\"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n\n                // Link feedback for input state\n                InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]);\n            }\n            catch (Exception e)\n            {\n                this.LogError(\"Unable to link device {key}.  {message}\", Key, e.Message);\n                this.LogDebug(e, \"Stack Trace: \");\n            }\n        }\n\n        #endregion\n\n        #region Factory\n\n        /// <summary>\n        /// Factory for creating GenericDigitalInputDevice devices\n        /// </summary>\n        public class GenericDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericDigitalInputDevice>\n        {\n            /// <summary>\n            /// Constructor for GenericDigitalInputDeviceFactory\n            /// </summary>\n            public GenericDigitalInputDeviceFactory()\n            {\n                TypeNames = new List<string>() { \"digitalinput\" };\n            }\n\n            /// <inheritdoc />\n            public override EssentialsDevice BuildDevice(DeviceConfig dc)\n            {\n                Debug.LogDebug(\"Factory Attempting to create new Generic Digital Input Device\");\n\n                var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());\n\n                if (props == null) return null;\n\n                var portDevice = new GenericDigitalInputDevice(dc.Key, dc.Name, GetDigitalInput, props);\n\n                return portDevice;\n            }\n        }\n\n        #endregion\n\n    }\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/GenericRelayDevice.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Represents a generic device controlled by relays\n    /// </summary>\n    [Description(\"Wrapper class for a Relay\")]\n    public class GenericRelayDevice : EssentialsBridgeableDevice, ISwitchedOutput\n    {\n        /// <summary>\n        /// The RelayOutput controlled by this device\n        /// </summary>\n        public Relay RelayOutput { get; private set; }\n\n        /// <summary>\n        /// Feedback to indicate whether the output is on\n        /// </summary>\n        public BoolFeedback OutputIsOnFeedback { get; private set; }\n\n        //Maintained for compatibility with PepperDash.Essentials.Core.Devices.CrestronProcessor\n        /// <summary>\n        /// Constructor for GenericRelayDevice\n        /// </summary>\n        /// <param name=\"key\">key of the device</param>\n        /// <param name=\"relay\">Relay output controlled by this device</param>\n        public GenericRelayDevice(string key, Relay relay) :\n            base(key)\n        {\n            OutputIsOnFeedback = new BoolFeedback(new Func<bool>(() => RelayOutput.State));\n\n            RelayOutput = relay;\n            RelayOutput.Register();\n\n            RelayOutput.StateChange += RelayOutput_StateChange;\n        }\n\n        /// <summary>\n        /// Constructor for GenericRelayDevice\n        /// </summary>\n        /// <param name=\"key\">key of the device</param>\n        /// <param name=\"name\">name of the device</param>\n        /// <param name=\"postActivationFunc\">function to get the relay output</param>\n        /// <param name=\"config\">IO port configuration</param>\n        public GenericRelayDevice(string key, string name, Func<IOPortConfig, Relay> postActivationFunc,\n            IOPortConfig config)\n            : base(key, name)\n        {\n            OutputIsOnFeedback = new BoolFeedback(() => RelayOutput.State);\n\n            AddPostActivationAction(() =>\n            {\n                RelayOutput = postActivationFunc(config);\n\n                if (RelayOutput == null)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, this, \"Unable to get parent relay device for device key {0} and port {1}\", config.PortDeviceKey, config.PortNumber);\n                    return;\n                }\n\n                RelayOutput.Register();\n\n                RelayOutput.StateChange += RelayOutput_StateChange;\n            });\n        }\n\n        #region PreActivate\n\n        private static Relay GetRelay(IOPortConfig dc)\n        {\n\n            IRelayPorts relayDevice;\n\n            if(dc.PortDeviceKey.Equals(\"processor\"))\n            {\n                if (!Global.ControlSystem.SupportsRelay)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Processor does not support relays\");\n                    return null;\n                }\n                relayDevice = Global.ControlSystem;\n\n                return relayDevice.RelayPorts[dc.PortNumber];\n            }\n            \n            var essentialsDevice = DeviceManager.GetDeviceForKey(dc.PortDeviceKey);\n            if (essentialsDevice == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Device {0} was not found in Device Manager. Check configuration or for errors with device.\", dc.PortDeviceKey);\n                return null;\n            }\n\n            relayDevice = essentialsDevice as IRelayPorts;\n            \n            if (relayDevice == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Device {0} is not a valid relay parent. Please check configuration.\", dc.PortDeviceKey);\n                return null;\n            }\n\n            if (dc.PortNumber <= relayDevice.NumberOfRelayPorts)\n            {\n                return relayDevice.RelayPorts[dc.PortNumber];\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, \"Device {0} does not contain a port {1}\", dc.PortDeviceKey, dc.PortNumber);\n            return null;\n        }\n\n        #endregion\n\n        #region Events\n\n        void RelayOutput_StateChange(Relay relay, RelayEventArgs args)\n        {\n            OutputIsOnFeedback.FireUpdate();\n        }\n\n        #endregion\n\n        #region Methods\n\n        /// <summary>\n        /// OpenRelay method\n        /// </summary>\n        public void OpenRelay()\n        {\n            RelayOutput.State = false;\n        }\n\n        /// <summary>\n        /// CloseRelay method\n        /// </summary>\n        public void CloseRelay()\n        {\n            RelayOutput.State = true;\n        }\n\n        /// <summary>\n        /// ToggleRelayState method\n        /// </summary>\n        public void ToggleRelayState()\n        {\n            if (RelayOutput.State == true)\n                OpenRelay();\n            else\n                CloseRelay();\n        }\n\n        #endregion\n\n        #region ISwitchedOutput Members\n\n        void ISwitchedOutput.On()\n        {\n            CloseRelay();\n        }\n\n        void ISwitchedOutput.Off()\n        {\n            OpenRelay();\n        }\n\n        #endregion\n\n        #region Bridge Linking\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new GenericRelayControllerJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<GenericRelayControllerJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            if (RelayOutput == null)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to link device '{0}'.  Relay is null\", Key);\n                return;\n            }\n\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n\n            trilist.SetBoolSigAction(joinMap.Relay.JoinNumber, b =>\n            {\n                if (b)\n                    CloseRelay();\n                else\n                    OpenRelay();\n            });\n\n            // feedback for relay state\n\n            OutputIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Relay.JoinNumber]);\n        }\n\n        #endregion\n\n        #region Factory\n\n        /// <summary>\n        /// Represents a GenericRelayDeviceFactory\n        /// </summary>\n        public class GenericRelayDeviceFactory : EssentialsDeviceFactory<GenericRelayDevice>\n        {\n            /// <summary>\n            /// Constructor for GenericRelayDeviceFactory\n            /// </summary>\n            public GenericRelayDeviceFactory()\n            {\n                TypeNames = new List<string>() { \"relayoutput\" };\n            }\n\n            /// <summary>\n            /// BuildDevice method\n            /// </summary>\n            /// <inheritdoc />\n            public override EssentialsDevice BuildDevice(DeviceConfig dc)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Generic Relay Device\");\n\n                var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());\n\n                if (props == null) return null;\n\n                var portDevice = new GenericRelayDevice(dc.Key, dc.Name, GetRelay, props);\n\n                return portDevice;\n            }\n        }\n\n        #endregion\n\n\n    }\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportAnalogInputDevice.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Represents a generic digital input deviced tied to a versiport\n    /// </summary>\n    public class GenericVersiportAnalogInputDevice : EssentialsBridgeableDevice, IAnalogInput, IHasFeedback\n    {\n        private Versiport inputPort;\n\n        /// <inheritdoc />\n        public IntFeedback InputValueFeedback { get; private set; }\n\n        /// <summary>\n        /// Get the InputMinimumChangeFeedback\n        /// </summary>\n        /// <remarks>\n        /// Updates when the analog input minimum change value changes\n        /// </remarks>\n        public IntFeedback InputMinimumChangeFeedback { get; private set; }\n\n        /// <inheritdoc />\n        public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"GenericVersiportAnalogInputDevice\"/> class.\n        /// </summary>\n        /// <param name=\"key\">key for the device</param>\n        /// <param name=\"name\">name for the device</param>\n        /// <param name=\"postActivationFunc\">function to call after activation</param>\n        /// <param name=\"config\">IO port configuration</param>\n        public GenericVersiportAnalogInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :\n            base(key, name)\n        {\n            InputValueFeedback = new IntFeedback(\"inputValue\", () => inputPort.AnalogIn);\n            InputMinimumChangeFeedback = new IntFeedback(\"inputMinimumChange\", () => inputPort.AnalogMinChange);\n\n            AddPostActivationAction(() =>\n            {\n                inputPort = postActivationFunc(config);\n\n                inputPort.Register();\n\n                inputPort.SetVersiportConfiguration(eVersiportConfiguration.AnalogInput);\n                inputPort.AnalogMinChange = (ushort)(config.MinimumChange > 0 ? config.MinimumChange : 655);\n                if (config.DisablePullUpResistor)\n                    inputPort.DisablePullUpResistor = true;\n\n                inputPort.VersiportChange += InputPort_VersiportChange;\n\n                this.LogDebug(\"Created GenericVersiportAnalogInputDevice on port {port}.  DisablePullUpResistor: {pullUpResistorDisabled}\", config.PortNumber, inputPort.DisablePullUpResistor);\n            });\n\n        }\n\n        /// <summary>\n        /// Set minimum voltage change for device to update voltage changed method\n        /// </summary>\n        /// <param name=\"value\">valid values range from 0 - 65535, representing the full 100% range of the processor voltage source.  Check processor documentation for details</param>        \n        public void SetMinimumChange(ushort value)\n        {\n            inputPort.AnalogMinChange = value;\n        }\n\n        void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)\n        {\n            this.LogDebug(\"Versiport change: {event}\", args.Event);\n\n            if (args.Event == eVersiportEvent.AnalogInChange)\n                InputValueFeedback.FireUpdate();\n            if (args.Event == eVersiportEvent.AnalogMinChangeChange)\n                InputMinimumChangeFeedback.FireUpdate();\n        }\n\n\n        #region Bridge Linking\n\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new IAnalogInputJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<IAnalogInputJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                this.LogWarning(\"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            try\n            {\n                this.LogDebug(\"Linking to Trilist '{trilistId}'\", trilist.ID.ToString(\"X\"));\n\n                // Link feedback for input state\n                InputValueFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputValue.JoinNumber]);\n                InputMinimumChangeFeedback.LinkInputSig(trilist.UShortInput[joinMap.MinimumChange.JoinNumber]);\n                trilist.SetUShortSigAction(joinMap.MinimumChange.JoinNumber, SetMinimumChange);\n\n            }\n            catch (Exception e)\n            {\n                this.LogError(\"Unable to link device {key}: {message}\", Key, e.Message);\n                this.LogDebug(e, \"Stack Trace: \");\n            }\n\n            trilist.OnlineStatusChange += (d, args) =>\n            {\n                if (!args.DeviceOnLine) return;\n                InputValueFeedback.FireUpdate();\n                InputMinimumChangeFeedback.FireUpdate();\n            };\n\n        }\n\n        #endregion\n\n\n        /// <summary>\n        /// GetVersiportDigitalInput method\n        /// </summary>\n        public static Versiport GetVersiportDigitalInput(IOPortConfig dc)\n        {\n            if (dc.PortDeviceKey.Equals(\"processor\"))\n            {\n                if (!Global.ControlSystem.SupportsVersiport)\n                {\n                    Debug.LogError(\"GetVersiportAnalogInput: Processor does not support Versiports\");\n                    return null;\n                }\n                return Global.ControlSystem.VersiPorts[dc.PortNumber];\n            }\n\n            if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))\n            {\n                Debug.LogError(\"GetVersiportAnalogInput: Device {key} is not a valid device\", dc.PortDeviceKey);\n                return null;\n            }\n\n            if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)\n            {\n                Debug.LogError(\"GetVersiportAnalogInput: Device {key} does not contain a port {port}\", dc.PortDeviceKey, dc.PortNumber);\n                return null;\n            }\n            if (!ioPortDevice.VersiPorts[dc.PortNumber].SupportsAnalogInput)\n            {\n                Debug.LogError(\"GetVersiportAnalogInput: Device {key} does not support AnalogInput on port {port}\", dc.PortDeviceKey, dc.PortNumber);\n                return null;\n            }\n\n            return ioPortDevice.VersiPorts[dc.PortNumber];\n        }\n    }\n\n\n    /// <summary>\n    /// Factory for creating GenericVersiportAnalogInputDevice devices\n    /// </summary>\n    public class GenericVersiportAnalogInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportAnalogInputDevice>\n    {\n        /// <summary>\n        /// Constructor for GenericVersiportAnalogInputDeviceFactory\n        /// </summary>\n        public GenericVersiportAnalogInputDeviceFactory()\n        {\n            TypeNames = new List<string>() { \"versiportanaloginput\" };\n        }\n\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogDebug(\"Factory Attempting to create new Generic Versiport Device\");\n\n            var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());\n\n            if (props == null) return null;\n\n            var portDevice = new GenericVersiportAnalogInputDevice(dc.Key, dc.Name, GenericVersiportAnalogInputDevice.GetVersiportDigitalInput, props);\n\n            return portDevice;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Represents a generic digital input deviced tied to a versiport\n    /// </summary>\n    public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider, IHasFeedback\n    {\n        private Versiport inputPort;\n        private readonly bool invertState;\n\n        /// <summary>\n        /// Gets or sets the InputStateFeedback\n        /// </summary>\n        public BoolFeedback InputStateFeedback { get; private set; }\n\n        /// <inheritdoc />\n        public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();\n\n        /// <summary>\n        /// Gets or sets the PartitionPresentFeedback\n        /// </summary>\n        public BoolFeedback PartitionPresentFeedback { get; }\n\n        /// <summary>\n        /// Get partition state\n        /// </summary>\n        public bool PartitionPresent => !inputPort.DigitalIn;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"GenericVersiportDigitalInputDevice\"/> class.\n        /// </summary>\n        /// <param name=\"key\">key for device</param>\n        /// <param name=\"name\">name for device</param>\n        /// <param name=\"postActivationFunc\">function to call after activation. Should return the Versiport</param>\n        /// <param name=\"config\">config for device</param>\n        public GenericVersiportDigitalInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :\n            base(key, name)\n        {\n            var circuitType = string.IsNullOrEmpty(config.CircuitType) ? \"NO\" : config.CircuitType;\n            invertState = circuitType.Equals(\"NC\", StringComparison.OrdinalIgnoreCase);\n\n            InputStateFeedback = new BoolFeedback(\"inputState\", () => invertState ? !inputPort.DigitalIn : inputPort.DigitalIn);\n            PartitionPresentFeedback = new BoolFeedback(\"partitionPresent\", () => !inputPort.DigitalIn);\n\n            AddPostActivationAction(() =>\n            {\n                inputPort = postActivationFunc(config);\n\n                inputPort.Register();\n\n                inputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalInput);\n                if (config.DisablePullUpResistor)\n                    inputPort.DisablePullUpResistor = true;\n\n                inputPort.VersiportChange += InputPort_VersiportChange;\n\n                InputStateFeedback.FireUpdate();\n                PartitionPresentFeedback.FireUpdate();\n\n                this.LogDebug(\"Created GenericVersiportDigitalInputDevice for port {port}.  DisablePullUpResistor: {pullUpResistorDisable}\", config.PortNumber, inputPort.DisablePullUpResistor);\n\n            });\n\n            Feedbacks.Add(InputStateFeedback);\n            Feedbacks.Add(PartitionPresentFeedback);\n        }\n\n        void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)\n        {\n            this.LogDebug(\"Versiport change: {0}\", args.Event);\n\n            if (args.Event == eVersiportEvent.DigitalInChange)\n            {\n                InputStateFeedback.FireUpdate();\n                PartitionPresentFeedback.FireUpdate();\n            }\n        }\n\n\n        #region Bridge Linking\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new IDigitalInputJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<IDigitalInputJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                this.LogWarning(\"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            try\n            {\n                this.LogDebug(\"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n\n                // Link feedback for input state\n                InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]);\n            }\n            catch (Exception e)\n            {\n                this.LogError(\"Unable to link device {key}.  Input is null. {message}\", Key, e.Message);\n                this.LogDebug(e, \"Stack Trace: \");\n            }\n        }\n\n        #endregion\n\n\n        /// <summary>\n        /// GetVersiportDigitalInput method\n        /// </summary>\n        public static Versiport GetVersiportDigitalInput(IOPortConfig dc)\n        {\n            if (dc.PortDeviceKey.Equals(\"processor\"))\n            {\n                if (!Global.ControlSystem.SupportsVersiport)\n                {\n                    Debug.LogError(\"GetVersiportDigitalInput: Processor does not support Versiports\");\n                    return null;\n                }\n                return Global.ControlSystem.VersiPorts[dc.PortNumber];\n            }\n\n            if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))\n            {\n                Debug.LogError(\"GetVersiportDigitalInput: Device {key} is not a valid device\", dc.PortDeviceKey);\n                return null;\n            }\n\n            if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)\n            {\n                Debug.LogError(\"GetVersiportDigitalInput: Device {key} does not contain versiport {port}\", dc.PortDeviceKey, dc.PortNumber);\n                return null;\n            }\n\n            return ioPortDevice.VersiPorts[dc.PortNumber];\n        }\n    }\n\n\n    /// <summary>\n    /// Factory class for GenericVersiportDigitalInputDevice\n    /// </summary>\n    public class GenericVersiportDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice>\n    {\n        /// <summary>\n        /// Constructor for GenericVersiportDigitalInputDeviceFactory\n        /// </summary>\n        public GenericVersiportDigitalInputDeviceFactory()\n        {\n            TypeNames = new List<string>() { \"versiportinput\" };\n        }\n\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogDebug(\"Factory Attempting to create new Generic Versiport Device\");\n\n            var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());\n\n            if (props == null) return null;\n\n            var portDevice = new GenericVersiportDigitalInputDevice(dc.Key, dc.Name, GenericVersiportDigitalInputDevice.GetVersiportDigitalInput, props);\n\n            return portDevice;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportOutputDevice.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Represents a generic digital input deviced tied to a versiport\n    /// </summary>\n    public class GenericVersiportDigitalOutputDevice : EssentialsBridgeableDevice, IDigitalOutput, IHasFeedback\n    {\n        private Versiport outputPort;\n\n        /// <summary>\n        /// Gets or sets the OutputStateFeedback\n        /// </summary>\n        public BoolFeedback OutputStateFeedback { get; private set; }\n\n        /// <inheritdoc />\n        public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"GenericVersiportDigitalOutputDevice\"/> class.\n        /// </summary>\n        public GenericVersiportDigitalOutputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :\n            base(key, name)\n        {\n            OutputStateFeedback = new BoolFeedback(\"outputState\", () => outputPort.DigitalOut);\n\n            AddPostActivationAction(() =>\n            {\n                outputPort = postActivationFunc(config);\n\n                outputPort.Register();\n\n\n                if (!outputPort.SupportsDigitalOutput)\n                {\n                    this.LogError(\"Device does not support configuration as a Digital Output\");\n                    return;\n                }\n\n                outputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput);\n\n\n                outputPort.VersiportChange += OutputPort_VersiportChange;\n\n            });\n        }\n\n        void OutputPort_VersiportChange(Versiport port, VersiportEventArgs args)\n        {\n            this.LogDebug(\"Versiport change: {event}\", args.Event);\n\n            if (args.Event == eVersiportEvent.DigitalOutChange)\n                OutputStateFeedback.FireUpdate();\n        }\n\n        /// <summary>\n        /// Set value of the versiport digital output\n        /// </summary>\n        /// <param name=\"state\">value to set the output to</param>        \n        public void SetOutput(bool state)\n        {\n            if (!outputPort.SupportsDigitalOutput)\n            {\n                this.LogError(\"Versiport does not support Digital Output Mode\");\n                return;\n            }\n\n            outputPort.DigitalOut = state;\n        }\n\n        #region Bridge Linking\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new IDigitalOutputJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<IDigitalOutputJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                this.LogWarning(\"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            try\n            {\n                this.LogDebug(\"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n\n                // Link feedback for input state\n                OutputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.OutputState.JoinNumber]);\n                trilist.SetBoolSigAction(joinMap.OutputState.JoinNumber, SetOutput);\n            }\n            catch (Exception e)\n            {\n                this.LogError(\"Unable to link device: {message}\", e.Message);\n                this.LogDebug(e, \"Stack Trace: \");\n            }\n        }\n\n        #endregion\n\n\n        /// <summary>\n        /// GetVersiportDigitalOutput method\n        /// </summary>\n        public static Versiport GetVersiportDigitalOutput(IOPortConfig dc)\n        {\n            if (dc.PortDeviceKey.Equals(\"processor\"))\n            {\n                if (!Global.ControlSystem.SupportsVersiport)\n                {\n                    Debug.LogError(\"GetVersiportDigitalOutput: Processor does not support Versiports\");\n                    return null;\n                }\n                return Global.ControlSystem.VersiPorts[dc.PortNumber];\n            }\n\n            if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))\n            {\n                Debug.LogError(\"GetVersiportDigitalOutput: Device {key} is not a valid device\", dc.PortDeviceKey);\n                return null;\n            }\n\n            if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"GetVersiportDigitalOutput: Device {0} does not contain a port {1}\", dc.PortDeviceKey, dc.PortNumber);\n                return null;\n            }\n            return ioPortDevice.VersiPorts[dc.PortNumber];\n        }\n    }\n\n\n    /// <summary>\n    /// Represents a GenericVersiportDigitalOutputDeviceFactory\n    /// </summary>\n    public class GenericVersiportDigitalOutputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice>\n    {\n        /// <summary>\n        /// Initialize a new instance of the <see cref=\"GenericVersiportDigitalOutputDeviceFactory\"/> class.\n        /// </summary>\n        public GenericVersiportDigitalOutputDeviceFactory()\n        {\n            TypeNames = new List<string>() { \"versiportoutput\" };\n        }\n\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogDebug(\"Factory Attempting to create new Generic Versiport Device\");\n\n            var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());\n\n            if (props == null) return null;\n\n            var portDevice = new GenericVersiportDigitalOutputDevice(dc.Key, dc.Name, GenericVersiportDigitalOutputDevice.GetVersiportDigitalOutput, props);\n\n            return portDevice;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/IAnalogInput.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Defines the contract for IAnalogInput\n    /// </summary>\n    public interface IAnalogInput\n    {\n        /// <summary>\n        /// Get the InputValueFeedback.\n        /// </summary>\n        /// <remarks>\n        /// Updates when the analog input value changes\n        /// </remarks>\n        IntFeedback InputValueFeedback { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/IDigitalInput.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Represents a device that provides digital input\n    /// </summary>\n    public interface IDigitalInput\n    {\n        /// <summary>\n        /// Feedback to indicate the state of the input\n        /// </summary>\n        BoolFeedback InputStateFeedback { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/IDigitalOutput.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Represents a device that provides digital input\n    /// </summary>\n    public interface IDigitalOutput\n    {\n        /// <summary>\n        /// Feedback to indicate the state of the output\n        /// </summary>\n        BoolFeedback OutputStateFeedback { get; }\n\n        /// <summary>\n        /// Sets the output state\n        /// </summary>\n        /// <param name=\"state\">The desired state of the output</param>\n        void SetOutput(bool state);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/IHasCresnetBranches.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IHasCresnetBranches\n    /// </summary>\n    public interface IHasCresnetBranches\n    {\n        /// <summary>\n        /// Collection of Cresnet branches\n        /// </summary>\n        CrestronCollection<CresnetBranch> CresnetBranches { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Represents a IOPortConfig\n    /// </summary>\n    public class IOPortConfig\n    {\n        /// <summary>\n        /// Gets or sets the PortDeviceKey\n        /// </summary>\n        [JsonProperty(\"portDeviceKey\")]\n        public string PortDeviceKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the PortNumber\n        /// </summary>\n        [JsonProperty(\"portNumber\")]\n        public uint PortNumber { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DisablePullUpResistor\n        /// </summary>\n        [JsonProperty(\"disablePullUpResistor\")]\n        public bool DisablePullUpResistor { get; set; }\n\n        /// <summary>\n        /// Gets or sets the MinimumChange\n        /// </summary>\n        [JsonProperty(\"minimumChange\")]\n        public int MinimumChange { get; set; }\n\n        /// <summary>\n        /// Gets or sets the circuit type: \"NO\" (Normally Open) or \"NC\" (Normally Closed)\n        /// If set to \"NC\", the input state will be inverted. Defaults to \"NO\" if not specified.\n        /// </summary>\n        [JsonProperty(\"circuitType\")]\n        public string CircuitType { get; set; } = \"NO\";\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/CrestronIO/ISwitchedOutput.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.CrestronIO\n{\n    /// <summary>\n    /// Describes an output capable of switching on and off\n    /// </summary>\n    public interface ISwitchedOutput\n    {\n        /// <summary>\n        /// Feedback to indicate whether the output is on\n        /// </summary>\n        BoolFeedback OutputIsOnFeedback {get;}\n\n        /// <summary>\n        /// Turns the output on\n        /// </summary>\n        void On();\n\n        /// <summary>\n        /// Turns the output off\n        /// </summary>\n        void Off();\n    }\n\n    /// <summary>\n    /// Describes a collection of switched outputs\n    /// </summary>\n    public interface ISwitchedOutputCollection\n    {\n        /// <summary>\n        /// Dictionary of switched outputs by their port number\n        /// </summary>\n        Dictionary<uint, ISwitchedOutput> SwitchedOutputs { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Device Info/DeviceInfo.cs",
    "content": "﻿namespace PepperDash.Essentials.Core.DeviceInfo\n{\n    /// <summary>\n    /// Represents a DeviceInfo\n    /// </summary>\n    public class DeviceInfo\n    {\n        /// <summary>\n        /// Gets or sets the HostName\n        /// </summary>\n        public string HostName { get; set; } \n        /// <summary>\n        /// Gets or sets the IpAddress\n        /// </summary>\n        public string IpAddress { get; set; }\n        /// <summary>\n        /// Gets or sets the MacAddress\n        /// </summary>\n        public string MacAddress { get; set; }\n        /// <summary>\n        /// Gets or sets the SerialNumber\n        /// </summary>\n        public string SerialNumber { get; set; }\n        /// <summary>\n        /// Gets or sets the FirmwareVersion\n        /// </summary>\n        public string FirmwareVersion { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Device Info/DeviceInfoEventArgs.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.DeviceInfo\n{\n    /// <summary>\n    /// Represents a DeviceInfoEventArgs\n    /// </summary>\n    public class DeviceInfoEventArgs:EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the DeviceInfo\n        /// </summary>\n        public DeviceInfo DeviceInfo { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public DeviceInfoEventArgs()\n        {\n            \n        }\n\n        /// <summary>\n        /// Constructor with DeviceInfo\n        /// </summary>\n        /// <param name=\"devInfo\">the DeviceInfo instance</param>\n        public DeviceInfoEventArgs(DeviceInfo devInfo)\n        {\n            DeviceInfo = devInfo;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Device Info/IDeviceInfoProvider.cs",
    "content": "﻿using System;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceInfo\n{\n    /// <summary>\n    /// Defines the contract for IDeviceInfoProvider\n    /// </summary>\n    public interface IDeviceInfoProvider:IKeyed\n    {\n        /// <summary>\n        /// Gets the DeviceInfo\n        /// </summary>\n        DeviceInfo DeviceInfo { get; }\n\n        /// <summary>\n        /// Event fired when DeviceInfo changes\n        /// </summary>\n        event DeviceInfoChangeHandler DeviceInfoChanged;\n\n        /// <summary>\n        /// Updates the DeviceInfo\n        /// </summary>\n        void UpdateDeviceInfo();\n    }\n\n    /// <summary>\n    /// Delegate for DeviceInfoChangeHandler\n    /// </summary>\n    public delegate void DeviceInfoChangeHandler(IKeyed device, DeviceInfoEventArgs args);\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Device Info/NetworkDeviceHelpers.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing PepperDash.Core;\nusing Crestron.SimplSharp;\nusing PepperDash.Essentials.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.DeviceInfo\n{\n    /// <summary>\n    /// Static class NetworkDeviceHelpers\n    /// </summary>\n    public static class NetworkDeviceHelpers\n    {\n        /// <summary>\n        /// Event raised when ArpTable changes\n        /// </summary>\n        public static event ArpTableEventHandler ArpTableUpdated;\n\n        /// <summary>\n        /// Delegate called by ArpTableUpdated\n        /// </summary>\n        /// <param name=\"args\">contains the entire ARP table and a bool to note if there was an error in retrieving the data</param>\n        public delegate void ArpTableEventHandler(ArpTableEventArgs args);\n\n        private static readonly char NewLineSplitter = CrestronEnvironment.NewLine.ToCharArray().First();\n        private static readonly string NewLine = CrestronEnvironment.NewLine;\n\n        private static readonly CCriticalSection Lock = new CCriticalSection();\n\n        /// <summary>\n        /// Gets or sets the ArpTable\n        /// </summary>\n        public static List<ArpEntry> ArpTable { get; private set; }\n\n        /// <summary>\n        /// RefreshArp method\n        /// </summary>\n        public static void RefreshArp()\n        {\n            var error = false;\n            try\n            {\n                Lock.Enter();\n                var consoleResponse = string.Empty;\n                if (!CrestronConsole.SendControlSystemCommand(\"showarptable\", ref consoleResponse)) return;\n                if (string.IsNullOrEmpty(consoleResponse))\n                {\n                    error = true;\n                    return;\n                }\n                ArpTable.Clear();\n\n                Debug.LogMessage(LogEventLevel.Verbose, \"ConsoleResponse of 'showarptable' : {0}{1}\", NewLine, consoleResponse);\n\n                var myLines =\n                    consoleResponse.Split(NewLineSplitter)\n                        .ToList()\n                        .Where(o => (o.Contains(':') && !o.Contains(\"Type\", StringComparison.OrdinalIgnoreCase)))\n                        .ToList();\n                foreach (var line in myLines)\n                {\n                    var item = line;\n                    var seperator = item.Contains('\\t') ? '\\t' : ' ';\n                    var dataPoints = item.Split(seperator);\n                    if (dataPoints == null || dataPoints.Length < 2) continue;\n                    var ipAddress = SanitizeIpAddress(dataPoints.First().TrimAll());\n                    var macAddress = dataPoints.Last();\n                    ArpTable.Add(new ArpEntry(ipAddress, macAddress));\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Exception in \\\"RefreshArp\\\" : {0}\", ex.Message);\n                error = true;\n            }\n            finally\n            {\n                Lock.Leave();\n                OnArpTableUpdated(new ArpTableEventArgs(ArpTable, error));\n            }\n        }\n\n\n        private static void OnArpTableUpdated(ArpTableEventArgs args)\n        {\n            if (args == null) return;\n            var handler = ArpTableUpdated;\n            if (handler == null) return;\n            handler.Invoke(args);\n        }\n\n        static NetworkDeviceHelpers()\n        {\n            ArpTable = new List<ArpEntry>();\n        }\n\n        /// <summary>\n        /// Removes leading zeros, leading whitespace, and trailing whitespace from an IPAddress string\n        /// </summary>\n        /// <param name=\"ipAddressIn\">Ip Address to Santitize</param>\n        /// <returns>Sanitized Ip Address</returns>\n        /// <summary>\n        /// SanitizeIpAddress method\n        /// </summary>\n        public static string SanitizeIpAddress(string ipAddressIn)\n        {\n            try\n            {\n                var ipAddress = IPAddress.Parse(ipAddressIn.TrimStart('0'));\n                return ipAddress.ToString();\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Unable to Santize Ip : {0}\", ex.Message);\n                return ipAddressIn;\n            }\n        }\n\n        /// <summary>\n        /// Resolves a hostname by IP Address using DNS\n        /// </summary>\n        /// <param name=\"ipAddress\">IP Address to resolve from</param>\n        /// <returns>Resolved Hostname - on failure to determine hostname, will return IP Address</returns>\n        /// <summary>\n        /// ResolveHostnameFromIp method\n        /// </summary>\n        public static string ResolveHostnameFromIp(string ipAddress)\n        {\n            try\n            {\n                var santitizedIp = SanitizeIpAddress(ipAddress);\n                var hostEntry = Dns.GetHostEntry(santitizedIp);\n                return hostEntry == null ? ipAddress : hostEntry.HostName;\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Exception Resolving Hostname from IP Address : {0}\", ex.Message);\n                return ipAddress;\n            }\n        }\n\n        /// <summary>\n        /// Resolves an IP Address by hostname using DNS\n        /// </summary>\n        /// <param name=\"hostName\">Hostname to resolve from</param>\n        /// <returns>Resolved IP Address - on a failure to determine IP Address, will return hostname</returns>\n        /// <summary>\n        /// ResolveIpFromHostname method\n        /// </summary>\n        public static string ResolveIpFromHostname(string hostName)\n        {\n            try\n            {\n                var hostEntry = Dns.GetHostEntry(hostName);\n                return hostEntry == null ? hostName : hostEntry.AddressList.First().ToString();\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Exception Resolving IP Address from Hostname : {0}\", ex.Message);\n                return hostName;\n            }\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a ArpEntry\n    /// </summary>\n    public class ArpEntry\n    {\n        /// <summary>\n        /// The IP Address of the ARP Entry\n        /// </summary>\n        public readonly IPAddress IpAddress;\n\n        /// <summary>\n        /// The MAC Address of the ARP Entry\n        /// </summary>\n        public readonly string MacAddress;\n\n        /// <summary>\n        /// Constructs new ArpEntry object\n        /// </summary>\n        /// <param name=\"ipAddress\">string formatted as ipv4 address</param>\n        /// <param name=\"macAddress\">mac address string - format is unimportant</param>\n        public ArpEntry(string ipAddress, string macAddress)\n        {\n            if (string.IsNullOrEmpty(ipAddress))\n            {\n                throw new ArgumentException(\"\\\"ipAddress\\\" cannot be null or empty\");\n            }\n            if (string.IsNullOrEmpty(macAddress))\n            {\n                throw new ArgumentException(\"\\\"macAddress\\\" cannot be null or empty\");\n            }\n            IpAddress = IPAddress.Parse(ipAddress.TrimStart().TrimStart('0').TrimEnd());\n            MacAddress = macAddress;\n        }\n    }\n\n    /// <summary>\n    /// Represents a ArpTableEventArgs\n    /// </summary>\n    public class ArpTableEventArgs : EventArgs\n    {\n        /// <summary>\n        /// The retrieved ARP Table\n        /// </summary>\n        public readonly List<ArpEntry> ArpTable;\n        /// <summary>\n        /// True if there was a problem retrieving the ARP Table\n        /// </summary>\n        public readonly bool Error;\n\n        /// <summary>\n        /// Constructor for ArpTableEventArgs\n        /// </summary>\n        /// <param name=\"arpTable\">The entirety of the retrieved ARP table</param>\n        /// <param name=\"error\">True of an error was encountered updating the ARP table</param>\n        public ArpTableEventArgs(List<ArpEntry> arpTable, bool error)\n        {\n            ArpTable = arpTable;\n            Error = error;\n        }\n\n        /// <summary>\n        /// Constructor for ArpTableEventArgs - assumes no error encountered in retrieving ARP Table\n        /// </summary>\n        /// <param name=\"arpTable\">The entirety of the retrieved ARP table</param>\n        public ArpTableEventArgs(List<ArpEntry> arpTable)\n        {\n            ArpTable = arpTable;\n            Error = false;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceControlsParentInterfaces/IPresentationSource.cs",
    "content": "﻿//using System;\n//using System.Collections.Generic;\n//using Crestron.SimplSharpPro;\n//using Crestron.SimplSharpPro.DeviceSupport;\n\n//using PepperDash.Core;\n\n\n//namespace PepperDash.Essentials.Core\n//{\n//    public interface IPresentationSource : IKeyed\n//    {\n//        string Name { get; }\n//        PresentationSourceType Type { get; }\n//        string IconName { get; set; }\n//        BoolFeedback HasPowerOnFeedback { get; }\n\t\t\n//    }\n//}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IAudioZone.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines minimum functionality for an audio zone\n  /// </summary>\n  public interface IAudioZone : IBasicVolumeWithFeedback\n  {\n    /// <summary>\n    /// Selects the specified input\n    /// </summary>\n    /// <param name=\"input\">The input to select</param>\n    void SelectInput(ushort input);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IAudioZones.cs",
    "content": "using System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Identifies a device that contains audio zones\n  /// </summary>\n  public interface IAudioZones : IRouting\n  {\n    /// <summary>\n    /// Gets the collection of audio zones\n    /// </summary>\n    Dictionary<uint, IAudioZone> Zone { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IBasicVolumeControls.cs",
    "content": "﻿using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Defines minimal volume and mute control methods\n\t/// </summary>\n\tpublic interface IBasicVolumeControls : IKeyName\n\t{\n\t\t/// <summary>\n\t\t/// Increases the volume\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">Indicates whether the volume change is a press and hold action</param>\n\t\tvoid VolumeUp(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Decreases the volume\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">Indicates whether the volume change is a press and hold action</param>\n\t\tvoid VolumeDown(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Toggles the mute state\n\t\t/// </summary>\n\t\tvoid MuteToggle();\n\t}\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IBasicVolumeWithFeedback.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines the contract for IBasicVolumeWithFeedback\n  /// </summary>\n  public interface IBasicVolumeWithFeedback : IBasicVolumeControls\n  {\n    /// <summary>\n    /// Gets the mute feedback\n    /// </summary>\n    BoolFeedback MuteFeedback { get; }\n\n    /// <summary>\n    /// Mutes the volume\n    /// </summary>\n    void MuteOn();\n\n    /// <summary>\n    /// Unmutes the volume\n    /// </summary>\n    void MuteOff();\n\n    /// <summary>\n    /// Sets the volume to the specified level\n    /// </summary>\n    /// <param name=\"level\">The volume level to set</param>\n    void SetVolume(ushort level);\n\n    /// <summary>\n    /// Gets the mute feedback\n    /// </summary>\n    IntFeedback VolumeLevelFeedback { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IBasicVolumeWithFeedbackAdvanced.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines the contract for IBasicVolumeWithFeedbackAdvanced\n  /// </summary>\n  public interface IBasicVolumeWithFeedbackAdvanced : IBasicVolumeWithFeedback\n  {\n    /// <summary>\n    /// Gets the raw volume level\n    /// </summary>\n    int RawVolumeLevel { get; }\n\n    /// <summary>\n    /// Gets the volume level units\n    /// </summary>\n    eVolumeLevelUnits Units { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IChannel.cs",
    "content": "﻿using Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Defines the contract for IChannel\n\t/// </summary>\n\tpublic interface IChannel\n\t{\n\t\t/// <summary>\n\t\t/// Channel up\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\t/// \n\t\tvoid ChannelUp(bool pressRelease);\n\t\t/// <summary>\n\t\t/// Channel down\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\tvoid ChannelDown(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Last channel\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\tvoid LastChannel(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Guide\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\t/// \n\t\tvoid Guide(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Info\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\tvoid Info(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Exit\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\tvoid Exit(bool pressRelease);\n\t}\n\n\t/// <summary>\n\t/// IChannelExtensions class\n\t/// </summary>\n\tpublic static class IChannelExtensions\n\t{\n\t\t/// <summary>\n\t\t/// LinkButtons method\n\t\t/// </summary>\n\t\tpublic static void LinkButtons(this IChannel dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.SetBoolSigAction(123, dev.ChannelUp);\n\t\t\ttriList.SetBoolSigAction(124, dev.ChannelDown);\n\t\t\ttriList.SetBoolSigAction(125, dev.LastChannel);\n\t\t\ttriList.SetBoolSigAction(137, dev.Guide);\n\t\t\ttriList.SetBoolSigAction(129, dev.Info);\n\t\t\ttriList.SetBoolSigAction(134, dev.Exit);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// UnlinkButtons method\n\t\t/// </summary>\n\t\tpublic static void UnlinkButtons(this IChannel dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.ClearBoolSigAction(123);\n\t\t\ttriList.ClearBoolSigAction(124);\n\t\t\ttriList.ClearBoolSigAction(125);\n\t\t\ttriList.ClearBoolSigAction(137);\n\t\t\ttriList.ClearBoolSigAction(129);\n\t\t\ttriList.ClearBoolSigAction(134);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IColorFunctions.cs",
    "content": "﻿using Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// IColor interface\n\t/// </summary>\n\tpublic interface IColor\n\t{\n\t\t/// <summary>\n\t\t/// Red button\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\tvoid Red(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Green button\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\tvoid Green(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Yellow button\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\tvoid Yellow(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Blue button\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">indicates whether this is a press or release</param>\n\t\tvoid Blue(bool pressRelease);\n\t}\n\n\t/// <summary>\n\t/// IColorExtensions class\n\t/// </summary>\n\tpublic static class IColorExtensions\n\t{\n\t\t/// <summary>\n\t\t/// LinkButtons method\n\t\t/// </summary>\n\t\t/// <param name=\"dev\">The IColor device</param>\n\t\t/// <param name=\"TriList\">The BasicTriList to link</param>\n\t\tpublic static void LinkButtons(this IColor dev, BasicTriList TriList)\n\t\t{\n\t\t\tTriList.SetBoolSigAction(155, dev.Red);\n\t\t\tTriList.SetBoolSigAction(156, dev.Green);\n\t\t\tTriList.SetBoolSigAction(157, dev.Yellow);\n\t\t\tTriList.SetBoolSigAction(158, dev.Blue);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// UnlinkButtons method\n\t\t/// </summary>\n\t\t/// <param name=\"dev\">The IColor device</param>\n\t\t/// <param name=\"triList\">The BasicTriList to unlink</param>\n\t\tpublic static void UnlinkButtons(this IColor dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.ClearBoolSigAction(155);\n\t\t\ttriList.ClearBoolSigAction(156);\n\t\t\ttriList.ClearBoolSigAction(157);\n\t\t\ttriList.ClearBoolSigAction(158);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ICustomMobileControl.cs",
    "content": "using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n  /// <summary>\n  /// Use this interface on a device or room if it uses custom Mobile Control messengers\n  /// </summary>\n  public interface ICustomMobileControl : IKeyed\n  {\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDPad.cs",
    "content": "﻿using Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic interface IDPad : IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// Up button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid Up(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Down button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid Down(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Left button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid Left(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Right button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid Right(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Select button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid Select(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Menu button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid Menu(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Exit button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid Exit(bool pressRelease);\n\t}\n\n\t/// <summary>\n\t/// IDPadExtensions class\n\t/// </summary>\n\tpublic static class IDPadExtensions\n\t{\n\t\t/// <summary>\n\t\t/// LinkButtons method\n\t\t/// </summary>\n\t\tpublic static void LinkButtons(this IDPad dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.SetBoolSigAction(138, dev.Up);\n\t\t\ttriList.SetBoolSigAction(139, dev.Down);\n\t\t\ttriList.SetBoolSigAction(140, dev.Left);\n\t\t\ttriList.SetBoolSigAction(141, dev.Right);\n\t\t\ttriList.SetBoolSigAction(142, dev.Select);\n\t\t\ttriList.SetBoolSigAction(130, dev.Menu);\n\t\t\ttriList.SetBoolSigAction(134, dev.Exit);        \n\t\t}\n\n\t\t/// <summary>\n\t\t/// UnlinkButtons method\n\t\t/// </summary>\n\t\tpublic static void UnlinkButtons(this IDPad dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.ClearBoolSigAction(138);\n\t\t\ttriList.ClearBoolSigAction(139);\n\t\t\ttriList.ClearBoolSigAction(140);\n\t\t\ttriList.ClearBoolSigAction(141);\n\t\t\ttriList.ClearBoolSigAction(142);\n\t\t\ttriList.ClearBoolSigAction(130);\n\t\t\ttriList.ClearBoolSigAction(134);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDiscPlayerControls.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\nnamespace PepperDash.Essentials.Core\n{\n\n    /// <summary>\n    /// Defines the contract for IDiscPlayerControls\n    /// </summary>\n    public interface IDiscPlayerControls : IColor, IDPad, INumericKeypad, IHasPowerControl, ITransport, IUiDisplayInfo\n\t{\n\t}\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs",
    "content": "﻿using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Interface for display devices that can be controlled and monitored.\n    /// This interface combines functionality for feedback, routing, power control,\n    /// warming/cooling, usage tracking, and key name management.\n    /// It is designed to be implemented by devices that require these capabilities,\n    /// such as projectors, displays, and other visual output devices.\n    /// </summary>\n    public interface IDisplay : IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking, IKeyName\n    {\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplayBasic.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core.Devices.DeviceTypeInterfaces\n{\n /// <summary>\n /// Defines the contract for IDisplayBasic\n /// </summary>\n\tpublic interface IDisplayBasic\n\t{\n\t\t/// <summary>\n\t\t/// Sets the input to HDMI 1\n\t\t/// </summary>\n\t\tvoid InputHdmi1();\n\n\t\t/// <summary>\n\t\t/// Sets the input to HDMI 2\n\t\t/// </summary>\n\t\tvoid InputHdmi2();\n\n\t\t/// <summary>\n\t\t/// Sets the input to HDMI 3\n\t\t/// </summary>\n\t\tvoid InputHdmi3();\n\n\t\t/// <summary>\n\t\t/// Sets the input to HDMI 4\n\t\t/// </summary>\n\t\tvoid InputHdmi4();\n\n\t\t/// <summary>\n\t\t/// Sets the input to DisplayPort 1\n\t\t/// </summary>\n\t\tvoid InputDisplayPort1();\n\n\t\t/// <summary>\n\t\t/// Sets the input to DVI 1\n\t\t/// </summary>\n\t\tvoid InputDvi1();\n\n\t\t/// <summary>\n\t\t/// Sets the input to Video 1\n\t\t/// </summary>\n\t\tvoid InputVideo1();\n\n\t\t/// <summary>\n\t\t/// Sets the input to VGA 1\n\t\t/// </summary>\n\t\tvoid InputVga1();\n\t\t\n\t\t/// <summary>\n\t\t/// Sets the input to VGA 2\n\t\t/// </summary>\n\t\tvoid InputVga2();\n\n\t\t/// <summary>\n\t\t/// Sets the input to RGB 1\n\t\t/// </summary>\n\t\tvoid InputRgb1();\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDumbSource.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Defines the contract for IDumbSource\n /// </summary>\n\tpublic interface IDumbSource\n\t{\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDvr.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic interface IDvr : IDPad\n\t{\n\t\t/// <summary>\n\t\t/// DVR List button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid DvrList(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Record button press\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n\t\tvoid Record(bool pressRelease);\n\t}\n\n\t/// <summary>\n\t/// IDvrExtensions class\n\t/// </summary>\n\tpublic static class IDvrExtensions\n\t{\n\t\t/// <summary>\n\t\t/// LinkButtons method\n\t\t/// </summary>\n\t\t/// <param name=\"dev\">IDvr device</param>\n\t\t/// <param name=\"triList\">BasicTriList to link to</param>\n\t\tpublic static void LinkButtons(this IDvr dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.SetBoolSigAction(136, dev.DvrList);\n\t\t\ttriList.SetBoolSigAction(152, dev.Record);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// UnlinkButtons method\n\t\t/// </summary>\n\t\t/// <param name=\"dev\">IDvr device</param>\n\t\t/// <param name=\"triList\">BasicTriList to unlink from</param>\n\t\tpublic static void UnlinkButtons(this IDvr dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.ClearBoolSigAction(136);\n\t\t\ttriList.ClearBoolSigAction(152);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IEmergencyOSD.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for IEmergencyOSD\n    /// </summary>\n    public interface IEmergencyOSD\n    {\n        /// <summary>\n        /// Shows an emergency message on the OSD\n        /// </summary>\n        /// <param name=\"url\">The URL of the emergency message to display</param>\n        void ShowEmergencyMessage(string url);\n\n        /// <summary>\n        /// Hides the emergency message from the OSD\n        /// </summary>\n        void HideEmergencyMessage();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IFullAudioSettings.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines the contract for IFullAudioSettings\n  /// </summary>\n  public interface IFullAudioSettings : IBasicVolumeWithFeedback\n  {\n    /// <summary>\n    /// SetBalance method\n    /// </summary>\n    /// <param name=\"level\">level to set</param>\n    void SetBalance(ushort level);\n\n    /// <summary>\n    /// BalanceLeft method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void BalanceLeft(bool pressRelease);\n\n    /// <summary>\n    /// BalanceRight method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void BalanceRight(bool pressRelease);\n\n    /// <summary>\n    /// SetBass method\n    /// </summary>\n    /// <param name=\"level\">level to set</param>\n    void SetBass(ushort level);\n\n    /// <summary>\n    /// BassUp method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void BassUp(bool pressRelease);\n\n    /// <summary>\n    /// BassDown method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void BassDown(bool pressRelease);\n\n    /// <summary>\n    /// SetTreble method\n    /// </summary>\n    /// <param name=\"level\">level to set</param>\n    void SetTreble(ushort level);\n\n    /// <summary>\n    /// TrebleUp method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void TrebleUp(bool pressRelease);\n\n    /// <summary>\n    /// TrebleDown method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void TrebleDown(bool pressRelease);\n\n    /// <summary>\n    /// hasMaxVolume property\n    /// </summary>\n    bool hasMaxVolume { get; }\n\n    /// <summary>\n    /// SetMaxVolume method\n    /// </summary>\n    /// <param name=\"level\">level to set</param>\n    void SetMaxVolume(ushort level);\n\n    /// <summary>\n    /// MaxVolumeUp method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void MaxVolumeUp(bool pressRelease);\n\n    /// <summary>\n    /// MaxVolumeDown method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void MaxVolumeDown(bool pressRelease);\n\n    /// <summary>\n    /// hasDefaultVolume property\n    /// </summary>\n    bool hasDefaultVolume { get; }\n\n    /// <summary>\n    /// SetDefaultVolume method\n    /// </summary>\n    /// <param name=\"level\">level to set</param>\n    void SetDefaultVolume(ushort level);\n\n    /// <summary>\n    /// DefaultVolumeUp method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void DefaultVolumeUp(bool pressRelease);\n\n    /// <summary>\n    /// DefaultVolumeDown method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the button is pressed or released</param>\n    void DefaultVolumeDown(bool pressRelease);\n\n    /// <summary>\n    /// LoudnessToggle method\n    /// </summary>\n    void LoudnessToggle();\n\n    /// <summary>\n    /// MonoToggle method\n    /// </summary>\n    void MonoToggle();\n\n    /// <summary>\n    /// LoudnessFeedback property\n    /// </summary>\n    BoolFeedback LoudnessFeedback { get; }\n\n    /// <summary>\n    /// MonoFeedback property\n    /// </summary>\n    BoolFeedback MonoFeedback { get; }\n\n    /// <summary>\n    /// BalanceFeedback property\n    /// </summary>\n    IntFeedback BalanceFeedback { get; }\n\n    /// <summary>\n    /// BassFeedback property\n    /// </summary>\n    IntFeedback BassFeedback { get; }\n\n    /// <summary>\n    /// TrebleFeedback property\n    /// </summary>\n    IntFeedback TrebleFeedback { get; }\n\n    /// <summary>\n    /// MaxVolumeFeedback property\n    /// </summary>\n    IntFeedback MaxVolumeFeedback { get; }\n\n    /// <summary>\n    /// DefaultVolumeFeedback property\n    /// </summary>\n    IntFeedback DefaultVolumeFeedback { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasBranding.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for IHasBranding\n    /// </summary>\n    public interface IHasBranding\n    {\n        /// <summary>\n        /// Gets whether branding is enabled\n        /// </summary>\n        bool BrandingEnabled { get; }\n\n        /// <summary>\n        /// Initializes branding for the device\n        /// </summary>\n        /// <param name=\"roomKey\">The key identifying the room for branding purposes</param>\n        void InitializeBranding(string roomKey);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasCurrentVolumeControls.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines the contract for IHasCurrentVolumeControls\n  /// </summary>\n  public interface IHasCurrentVolumeControls\n  {\n    /// <summary>\n    /// CurrentVolumeControls property\n    /// </summary>\n    IBasicVolumeControls CurrentVolumeControls { get; }\n\n    /// <summary>\n    /// CurrentVolumeDeviceChange event\n    /// </summary>\n    event EventHandler<VolumeDeviceChangeEventArgs> CurrentVolumeDeviceChange;\n\n    /// <summary>\n    /// SetDefaultLevels method\n    /// </summary>\n    void SetDefaultLevels();\n\n    /// <summary>\n    /// ZeroVolumeWhenSwtichingVolumeDevices property\n    /// </summary>\n    bool ZeroVolumeWhenSwtichingVolumeDevices { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasFarEndContentStatus.cs",
    "content": "﻿namespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for IHasFarEndContentStatus\n    /// </summary>\n    public interface IHasFarEndContentStatus\n    {\n        /// <summary>\n        /// Gets whether far end content is being received\n        /// </summary>\n         BoolFeedback ReceivingContent { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasInputs.cs",
    "content": "﻿using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Describes a device that has selectable inputs\n    /// </summary>\n    /// <typeparam name=\"T\">the type to use as the key for each input item. Most likely an enum or string</typeparam>\\\n    /// <example>\n    /// See MockDisplay for example implemntation\n    /// </example>\n    public interface IHasInputs<T> : IKeyName\n    {\n        /// <summary>\n        /// Gets the collection of inputs\n        /// </summary>\n        ISelectableItems<T> Inputs { get; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasMuteControl.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines basic mute control methods\n  /// </summary>\n  public interface IHasMuteControl\n  {\n    /// <summary>\n    /// MuteToggle method\n    /// </summary>\n    void MuteToggle();\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasMuteControlWithFeedback.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines mute control methods and properties with feedback\n  /// </summary>\n  public interface IHasMuteControlWithFeedback : IHasMuteControl\n  {\n    /// <summary>\n    /// MuteFeedback property\n    /// </summary>\n    BoolFeedback MuteFeedback { get; }\n\n    /// <summary>\n    /// MuteOn method\n    /// </summary>\n    void MuteOn();\n\n    /// <summary>\n    /// MuteOff method\n    /// </summary>\n    void MuteOff();\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasPhoneDialing.cs",
    "content": "﻿using System;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for IHasPhoneDialing\n    /// </summary>\n    public interface IHasPhoneDialing\n    {\n        /// <summary>\n        /// Feedback that indicates whether the phone is off-hook\n        /// </summary>\n        BoolFeedback PhoneOffHookFeedback { get; }\n\n        /// <summary>\n        /// Feedback that provides the caller ID name\n        /// </summary>\n        StringFeedback CallerIdNameFeedback { get; }\n\n        /// <summary>\n        /// Feedback that provides the caller ID number\n        /// </summary>\n        StringFeedback CallerIdNumberFeedback { get; }\n\n        /// <summary>\n        /// Dials a phone call to the specified number\n        /// </summary>\n        /// <param name=\"number\">the number to dial</param>\n        void DialPhoneCall(string number);\n\n        /// <summary>\n        /// Ends the current phone call\n        /// </summary>\n        void EndPhoneCall();\n\n        /// <summary>\n        /// Sends a DTMF digit to the phone\n        /// </summary>\n        /// <param name=\"digit\">the DTMF digit to send</param>\n        void SendDtmfToPhone(string digit);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasScreensWithLayouts.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// This defines a device that has screens with layouts\n    /// Simply decorative\n    /// </summary>\n    public interface IHasScreensWithLayouts\n    {\n        /// <summary>\n        /// A dictionary of screens, keyed by screen ID, that contains information about each screen and its layouts.\n        /// </summary>\n        Dictionary<uint, ScreenInfo> Screens { get; }\n\n        /// <summary>\n        /// Applies a specific layout to a screen based on the provided screen ID and layout index.\n        /// </summary>\n        /// <param name=\"screenId\"></param>\n        /// <param name=\"layoutIndex\"></param>\n        void ApplyLayout(uint screenId, uint layoutIndex);\n    }\n\n    /// <summary>\n    /// Represents information about a screen and its layouts.\n    /// </summary>\n    public class ScreenInfo\n    {\n\n        /// <summary>\n        /// Indicates whether the screen is enabled or not.\n        /// </summary>\n        [JsonProperty(\"enabled\")]\n        public bool Enabled { get; set; }\n\n        /// <summary>\n        /// The name of the screen.\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// The index of the screen.\n        /// </summary>\n        [JsonProperty(\"screenIndex\")]\n        public int ScreenIndex { get; set; }\n\n        /// <summary>\n        /// A dictionary of layout information for the screen, keyed by layout ID.\n        /// </summary>\n        [JsonProperty(\"layouts\")]\n        public Dictionary<uint, LayoutInfo> Layouts { get; set; }\n    }\n\n    /// <summary>\n    /// Represents information about a layout on a screen.\n    /// </summary>\n    public class LayoutInfo\n    {\n        /// <summary>\n        /// The name of the layout.\n        /// </summary>\n        [JsonProperty(\"layoutName\")]\n        public string LayoutName { get; set; }\n\n        /// <summary>\n        /// The index of the layout.\n        /// </summary>\n        [JsonProperty(\"layoutIndex\")]\n        public int LayoutIndex { get; set; }\n\n        /// <summary>\n        /// The type of the layout, which can be \"single\", \"double\", \"triple\", or \"quad\".\n        /// </summary>\n        [JsonProperty(\"layoutType\")]\n        public string LayoutType { get; set; }\n\n        /// <summary>\n        /// A dictionary of window configurations for the layout, keyed by window ID.\n        /// </summary>\n        [JsonProperty(\"windows\")]\n        public Dictionary<uint, WindowConfig> Windows { get; set; }\n    }\n\n    /// <summary>\n    /// Represents the configuration of a window within a layout on a screen.\n    /// </summary>\n    public class WindowConfig\n    {\n        /// <summary>\n        /// The display label for the window\n        /// </summary>\n        [JsonProperty(\"label\")]\n        public string Label { get; set; }\n\n        /// <summary>\n        /// The input for the window\n        /// </summary>\n        [JsonProperty(\"input\")]\n        public string Input { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasSurroundSoundModes.cs",
    "content": "﻿using PepperDash.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Describes a device that has selectable surround sound modes\n    /// </summary>\n    /// <typeparam name=\"TKey\">the type to use as the key for each input item. Most likely an enum or string</typeparam>\n    /// <typeparam name=\"TSelector\">the type used to select an item. Most likely an enum or string</typeparam>\n    public interface IHasSurroundSoundModes<TKey, TSelector>: IKeyName\n    {\n        /// <summary>\n        /// The available surround sound modes\n        /// </summary>\n        ISelectableItems<TKey> SurroundSoundModes { get; }\n\n        /// <summary>\n        /// The currently selected surround sound mode\n        /// </summary>\n        /// <param name=\"selector\">the selector for the surround sound mode</param>\n        void SetSurroundSoundMode(TSelector selector);\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasVolumeControl.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines the contract for IHasVolumeControl\n  /// </summary>\n  public interface IHasVolumeControl\n  {\n    /// <summary>\n    /// VolumeUp method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the volume up command is a press or release action</param>\n    void VolumeUp(bool pressRelease);\n\n    /// <summary>\n    /// VolumeDown method\n    /// </summary>\n    /// <param name=\"pressRelease\">determines if the volume down command is a press or release action</param>\n    void VolumeDown(bool pressRelease);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasVolumeControlWithFeedback.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines volume control methods and properties with feedback\n  /// </summary>\n  public interface IHasVolumeControlWithFeedback : IHasVolumeControl\n  {\n    /// <summary>\n    /// SetVolume method\n    /// </summary>\n    /// <param name=\"level\">The volume level to set</param>\n    void SetVolume(ushort level);\n\n    /// <summary>\n    /// VolumeLevelFeedback property\n    /// </summary>\n    IntFeedback VolumeLevelFeedback { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasVolumeDevice.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines the contract for IHasVolumeDevice\n  /// </summary>\n  public interface IHasVolumeDevice\n  {\n    /// <summary>\n    /// VolumeDevice property\n    /// </summary>\n    IBasicVolumeControls VolumeDevice { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWebView.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n        /// <summary>\n        /// Defines the display mode for a webview event, with expected values of \"Fullscreen\", \"Modal\", or \"Unknown\".\n        /// </summary>\n         public enum eWebViewEventMode\n        {\n            /// <summary>\n            /// The display mode for the webview event is unknown or not specified.  This value can be used as a default or fallback when the display mode is not provided or cannot be parsed into a known value.\n            /// </summary>\n            Unknown,\n\n            /// <summary>\n            /// The webview event should be displayed in fullscreen mode, covering the entire screen and typically used for immersive experiences or when maximum screen real estate is needed.  When a webview event with this display mode is shown, it will typically trigger the WebViewStatusChanged event with a status of \"Fullscreen\", and when it is cleared/closed, it will trigger the WebViewStatusChanged event with a status of \"Cleared\".\n            /// </summary>\n            Fullscreen,\n\n            /// <summary>\n            /// The webview event should be displayed in modal mode, which typically means it will be shown as a dialog or overlay on top of the existing content, allowing the user to interact with it while still being able to see the underlying content.  This display mode is often used for alerts, confirmations, or when the webview content is related to the current context but does not require full immersion.  When a webview event with this display mode is shown, it will typically trigger the WebViewStatusChanged event with a status of \"Modal\", and when it is cleared/closed, it will trigger the WebViewStatusChanged event with a status of \"Cleared\".\n            /// </summary>\n            Modal,\n        }\n\n        /// <summary>\n        /// Defines the target for a webview event, with expected values of \"OSD\", \"Controller\", \"PersistentWebApp\", or \"RoomScheduler\".\n        /// </summary>\n\n        public enum eWebViewTarget\n        {\n            /// <summary>\n            /// The target for the webview event is unknown or not specified.  This value can be used as a default or fallback when the target is not provided or cannot be parsed into a known value.\n            /// </summary>\n            Unknown,\n\n            /// <summary>\n            /// The webview event should be displayed on the On-Screen Display (OSD).\n            /// </summary>\n            OSD,\n\n            /// <summary>\n            /// The webview event should be displayed on the controller.\n            /// </summary>\n            Controller,\n\n            /// <summary>\n            /// The webview event should be displayed on the persistent web application.\n            /// </summary>\n            PersistentWebApp,\n\n            /// <summary>\n            /// The webview event should be displayed on the room scheduler.\n            /// </summary>\n            RoomScheduler\n        }\n\n        /// <summary>\n        /// Represents the reason for an error in a webview event, which can provide additional information about what went wrong.  This class is typically only used in the Status property of a WebViewEvent when the status indicates an error, and may be null otherwise.\n        /// </summary>\n        public class Reason\n        {\n            /// <summary>\n            /// The reason for an error in a webview event as a string, which can provide additional information about what went wrong.  This property is typically only populated in case of an error, and may be null otherwise.\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents the XPath of a webview event, which can provide information about where an error occurred in the webview.  This class is typically only used in the Status property of a WebViewEvent when the status indicates an error, and may be null otherwise.\n        /// </summary>\n        public class XPath\n        {\n            /// <summary>\n            /// The XPath of a webview event as a string, which can provide information about where an error occurred in the webview.  This property is typically only populated in case of an error, and may be null otherwise.\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a base class for properties that have a string value and trigger an action when the value changes.  This class can be used as a base for properties like DisplayMode and Target in the WebViewEvent, which have string values that can be set directly or parsed into enums for easier handling of expected values.  The ValueChangedAction can be set to trigger any desired behavior when the value changes, such as updating the UI or triggering other events.\n        /// </summary>\n        public abstract class ValueProperty\n        {\n            /// <summary>\n            /// Triggered when Value is set\n            /// </summary>\n            public Action ValueChangedAction { get; set; }\n\n            /// <summary>\n            /// Triggers the ValueChangedAction if it is set.  This method should be called whenever the Value property is set to ensure that any desired behavior associated with a change in value is executed.\n            /// </summary>\n            protected void OnValueChanged()\n            {\n                var a = ValueChangedAction;\n                if (a != null)\n                    a();\n            }\n\n        }\n    \n        /// <summary>\n        /// Represents a webview event, which can include information about the status of the webview, the display parameters for the webview, and any error information if applicable.  This class can be used to represent both show and clear events for a webview, with the Status property indicating the current status of the webview (e.g., \"Fullscreen\", \"Modal\", \"Cleared\", \"Error\", or \"Unknown\"), the Display property providing details about how the webview is being displayed (e.g., mode, URL, target, title), and the Cleared property providing details about a cleared/closed webview event (e.g., target and ID).  The Id property can be used to correlate show and clear events for the same webview instance.\n        /// </summary>\n        public class WebViewEvent\n        {\n            /// <summary>\n            /// The unique identifier for the webview event, which can be used to correlate show and clear events for the same webview instance.  This property is typically included in both show and clear events for a webview, allowing you to track the lifecycle of a specific webview instance from when it is shown to when it is cleared/closed.  The Id can be any string value, but it should be unique for each webview instance to ensure proper correlation between show and clear events.\n            /// </summary>\n            [JsonProperty(\"id\")]\n            public string Id { get; set; }\n\n            /// <summary>\n            /// The status of the webview event, which can indicate the current state of the webview (e.g., \"Fullscreen\", \"Modal\", \"Cleared\", \"Error\", or \"Unknown\") as well as any error information if applicable (XPath and Reason).  The Value property can be used to get or set the current status of the webview, while the XPath and Reason properties can provide additional information in case of an error.  The StatusString property can be used to get or set the raw status string from the event, but it is recommended to use the Value property for easier handling of expected values.  Setting the Value property will trigger the ValueChangedAction if it is set, allowing you to respond to changes in the webview status as needed.\n            /// </summary>\n            [JsonProperty(\"status\")]\n            public Status Status { get; set; } // /Event/UserInterface/WebView/Status\n    \n            /// <summary>\n            /// The display parameters for the webview event, which can include the display mode (e.g., \"Fullscreen\", \"Modal\", or \"Unknown\"), the URL to display in the webview, the target for the webview (e.g., \"OSD\", \"Controller\", \"PersistentWebApp\", or \"RoomScheduler\"), and the title to display on the webview.  This property is typically included in show events for a webview, providing details about how the webview is being displayed.  When a webview event with these display parameters is shown, it will typically trigger the WebViewStatusChanged event with a status of \"Fullscreen\" or \"Modal\" depending on the specified display mode, and when it is cleared/closed, it will trigger the WebViewStatusChanged event with a status of \"Cleared\".\n            /// </summary>\n            [JsonProperty(\"display\")]\n            public WebViewDisplay Display { get; set; } // /Event/UserInterface/WebView/Display\n            \n            /// <summary>\n            /// The details for a cleared/closed webview event, which can include the target for the webview that was cleared (e.g., \"OSD\", \"Controller\", \"PersistentWebApp\", or \"RoomScheduler\") and the unique identifier for the webview event that was cleared.  This property is typically included in clear events for a webview, providing details about which webview instance was cleared/closed.  When a webview event with this property is cleared/closed, it will typically trigger the WebViewStatusChanged event with a status of \"Cleared\".\n            /// </summary>\n            [JsonProperty(\"cleared\")]\n            public WebViewClear Cleared { get; set; } // /Event/UserInterface/WebView/Cleared\n        }\n\n        /// <summary>\n        /// Represents the display parameters for a webview event, which can include the display mode (e.g., \"Fullscreen\", \"Modal\", or \"Unknown\"), the URL to display in the webview, the target for the webview (e.g., \"OSD\", \"Controller\", \"PersistentWebApp\", or \"RoomScheduler\"), and the title to display on the webview.  This class is typically used in the Display property of a WebViewEvent to provide details about how the webview is being displayed when a show event occurs.  When a webview event with these display parameters is shown, it will typically trigger the WebViewStatusChanged event with a status of \"Fullscreen\" or \"Modal\" depending on the specified display mode, and when it is cleared/closed, it will trigger the WebViewStatusChanged event with a status of \"Cleared\".\n        /// </summary>\n        public class WebViewDisplay\n        {\n            /// <summary>\n            /// The display mode for the webview event.  Expected values are \"Fullscreen\", \"Modal\", or \"Unknown\".  \n            /// </summary>\n            [JsonProperty(\"mode\")]\n            public DisplayMode Mode { get; set; }\n\n            /// <summary>\n            /// The URL to display in the webview.  \n            /// </summary>\n            [JsonProperty(\"url\")]\n            public string Url { get; set; }\n\n            /// <summary>\n            /// The target for the webview.  Expected values are \"OSD\", \"Controller\", \"PersistentWebApp\", or \"RoomScheduler\".\n            /// </summary>\n            [JsonProperty(\"target\")]\n            public Target Target { get; set; }\n\n            /// <summary>\n            /// The title to display on the webview.\n            /// </summary>\n            [JsonProperty(\"title\")]\n            public string Title { get; set; }\n\n            /// <summary>\n            /// The unique identifier for the webview event, used to correlate show and clear events for the same webview instance.\n            /// </summary>\n            [JsonProperty(\"id\")]\n            public string Id { get; set; }\n        }\n\n        /// <summary>\n        /// Represents the data for a webview cleared event, which indicates that a webview with the specified ID and target has been cleared/closed.\n        /// </summary>\n        public class WebViewClear\n        {\n            /// <summary>\n            /// The target for the webview that was cleared.  Expected values are \"OSD\", \"Controller\", \"PersistentWebApp\", or \"RoomScheduler\".\n            /// </summary>\n            [JsonProperty(\"target\")]\n            public Target Target { get; set; }\n\n            /// <summary>\n            /// The unique identifier for the webview event that was cleared, used to correlate show and clear events for the same webview instance.\n            /// </summary>\n            [JsonProperty(\"id\")]\n            public string Id { get; set; }\n\n            /// <summary>\n            /// The URL that was displayed in the webview that was cleared.  \n            /// </summary>\n            [JsonProperty(\"url\")]\n            public string Url { get; set; }\n        }\n\n        /// <summary>\n        /// Represents the display mode for a webview event, with a string value and a corresponding enum property for easier handling of expected values.\n        /// </summary>\n        public class DisplayMode : ValueProperty\n        {\n            private string _value;\n\n            /// <summary>\n            /// The id of the webview event.\n            /// </summary>\n            [JsonProperty(\"id\")]\n            public string Id { get; set; }\n\n            /// <summary>\n            /// The string value for the display mode, which can be set directly or parsed into the WebViewEventMode enum using the WebViewEventMode property.  Setting this property will also trigger the ValueChangedAction if it is set.\n            /// </summary>\n            public string Value { get { return _value; } set { _value = value; OnValueChanged(); } }\n\n            /// <summary>\n            /// The display mode for the webview event as an enum, which can be used for easier handling of expected values.  Expected values are Fullscreen, Modal, or Unknown.\n            /// </summary>\n            public eWebViewEventMode WebViewEventMode\n            {\n                get\n                {\n                    eWebViewEventMode mode;\n                    System.Enum.TryParse(Value, true, out mode);\n                    return mode;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Represents the target for a webview event, with a string value and a corresponding enum property for easier handling of expected values.  Setting the Value property will also trigger the ValueChangedAction if it is set.\n        /// </summary>\n        public class Target : ValueProperty\n        {\n            private string _value;\n\n            /// <summary>\n            /// The id of the webview event.\n            /// </summary>\n            [JsonProperty(\"id\")]\n            public string Id { get; set; }\n\n            /// <summary>\n            /// The string value for the target, which can be set directly or parsed into the eWebViewTarget enum using the WebViewTarget property.  Expected values are \"OSD\", \"Controller\", \"PersistentWebApp\", or \"RoomScheduler\".  Setting this property will also trigger the ValueChangedAction if it is set.\n            /// </summary>\n            public string Value { get { return _value; } set { _value = value; OnValueChanged(); } }\n\n            /// <summary>\n            /// The target for the webview event as an enum, which can be used for easier handling of expected values.  Expected values are OSD, Controller, PersistentWebApp, or RoomScheduler.\n            /// </summary>\n            public eWebViewTarget WebViewTarget\n            {\n                get\n                {\n                    eWebViewTarget target;\n                    System.Enum.TryParse(Value, true, out target);\n                    return target;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Represents the status of a webview event, which can include error information (XPath and Reason) as well as the current status of the webview.  The Value property can be used to get or set the current status of the webview, while the XPath and Reason properties can provide additional information in case of an error.  The StatusString property can be used to get or set the raw status string from the event.\n        /// </summary>\n        public class Status\n        {\n            /// <summary>\n            /// The XPath of the webview event, which can provide information about where an error occurred in the webview.  This property is typically only populated in case of an error, and may be null otherwise.\n            /// </summary>\n            [JsonProperty(\"XPath\", NullValueHandling = NullValueHandling.Ignore)]\n            public XPath XPath { get; set; }\n\n            /// <summary>\n            /// The reason for an error in the webview event, which can provide additional information about what went wrong.  This property is typically only populated in case of an error, and may be null otherwise.\n            /// </summary>\n            [JsonProperty(\"Reason\", NullValueHandling = NullValueHandling.Ignore)]\n            public Reason Reason { get; set; }\n\n            /// <summary>\n            /// The raw status string from the webview event, which can provide information about the current status of the webview.  This property can be used to get or set the status directly, but it is recommended to use the Value property for easier handling of expected values.  Setting this property will not trigger any actions, while setting the Value property will trigger the ValueChangedAction if it is set.\n            /// </summary>\n            [JsonProperty(\"status\", NullValueHandling = NullValueHandling.Ignore)]\n            public string StatusString { get; set; }\n\n            /// <summary>\n            /// The current status of the webview as a string, which can be set directly or parsed into a WebViewEventMode enum using the WebViewEventMode property.  Expected values are \"Fullscreen\", \"Modal\", \"Cleared\", \"Error\", or \"Unknown\".  Setting this property will trigger the ValueChangedAction if it is set.\n            /// </summary>\n            [JsonProperty(\"Value\", NullValueHandling = NullValueHandling.Ignore)]\n            public string Value { get; set; }\n        }\n\n    /// <summary>\n    /// Defines the contract for IHasWebView\n    /// </summary>\n    public interface IHasWebView\n    {\n        /// <summary>\n        /// Indicates whether the webview is currently visible\n        /// </summary>\n        bool WebviewIsVisible { get; }\n\n        /// <summary>\n        /// Shows the webview with the specified parameters\n        /// </summary>\n        /// <param name=\"url\">the URL to display in the webview</param>\n        /// <param name=\"mode\">the display mode for the webview</param>\n        /// <param name=\"title\">the title to display on the webview</param>\n        /// <param name=\"target\">the target for the webview</param>\n        void ShowWebView(string url, string mode, string title, string target);\n\n        /// <summary>\n        /// Hides the webview\n        /// </summary>\n        void HideWebView();\n\n        /// <summary>\n        /// Event raised when the webview status changes\n        /// </summary>\n        event EventHandler<WebViewStatusChangedEventArgs> WebViewStatusChanged;\n\n    }\n\n\n    /// <summary>\n    /// Defines the contract for IHasWebViewWithPwaMode\n    /// </summary>\n    public interface IHasWebViewWithPwaMode : IHasWebView\n    {\n        /// <summary>\n        /// Indicates whether the webview is currently in PWA mode\n        /// </summary>\n        bool IsInPwaMode { get; }\n\n        /// <summary>\n        /// Gets the BoolFeedback indicating whether the webview is currently in PWA mode\n        /// </summary>\n        BoolFeedback IsInPwaModeFeedback { get; }\n\n        /// <summary>\n        /// Sends navigators to the specified PWA URL.  Accepts an absolute URL or a relative URL for a mobile control app\n        /// </summary>\n        /// <param name=\"url\">The URL to navigate to</param>\n        void SendNavigatorsToPwaUrl(string url);\n\n        /// <summary> \n        /// Exits navigators from PWA mode\n        /// </summary>\n        void ExitNavigatorsPwaMode();\n    }\n\n\n    /// <summary>\n    /// Represents a WebViewStatusChangedEventArgs\n    /// </summary>\n    public class WebViewStatusChangedEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the Status\n        /// </summary>\n        public string Status { get; }\n\n        /// <summary>\n        /// Gets or sets the WebViewEvent associated with the status change, which can provide additional information about the webview event that triggered the status change, such as display parameters or error information.  This property allows you to include the full WebViewEvent in the event args, giving you access to all relevant details about the webview event when handling the WebViewStatusChanged event.\n        /// </summary>\n        public WebViewEvent WebView { get; }\n\n        /// <summary>\n        /// Constructor for WebViewStatusChangedEventArgs\n        /// </summary>\n        /// <param name=\"status\">the new status of the webview</param>\n        public WebViewStatusChangedEventArgs(string status)\n        {\n            Status = status;\n        }\n\n        /// <summary>\n        /// Constructor for WebViewStatusChangedEventArgs with WebViewEvent parameter, which can provide additional information about the webview event that triggered the status change, such as display parameters or error information.  This constructor allows you to include the full WebViewEvent in the event args, giving you access to all relevant details about the webview event when handling the WebViewStatusChanged event.\n        /// </summary>\n        /// <param name=\"status\">the new status of the webview</param>\n        /// <param name=\"webview\">the WebViewEvent associated with the status change</param>\n        public WebViewStatusChangedEventArgs(string status, WebViewEvent webview)\n        {\n            Status = status;\n            WebView = webview;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHumiditySensor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for IHumiditySensor\n    /// </summary>\n    public interface IHumiditySensor\n    {\n        /// <summary>\n        ///  Reports the relative humidity level. Level ranging from 0 to 100 (for 0% to 100%\n        ///  RH). EventIds: HumidityFeedbackFeedbackEventId will trigger to indicate change.\n        /// </summary>\n        IntFeedback HumidityFeedback { get; }\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ILanguageDefinition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for ILanguageDefinition\n    /// </summary>\n    public interface ILanguageDefinition\n    {\n        /// <summary>\n        /// The locale name for the language definition\n        /// </summary>\n        string LocaleName { get; set; }\n\n        /// <summary>\n        /// The friendly name for the language definition\n        /// </summary>\n        string FriendlyName { get; set; }\n\n        /// <summary>\n        /// Indicates whether the language definition is enabled\n        /// </summary>\n        bool Enable { get; set; }\n\n        /// <summary>\n        /// The UI labels for the language definition\n        /// </summary>\n        List<LanguageLabel> UiLabels { get; set; }\n\n        /// <summary>\n        /// The source and destination labels for the language definition\n        /// </summary>\n        List<LanguageLabel> Sources { get; set; }\n\n        /// <summary>\n        /// The destination labels for the language definition\n        /// </summary>\n        List<LanguageLabel> Destinations { get; set; }\n\n        /// <summary>\n        /// The source group names for the language definition\n        /// </summary>\n        List<LanguageLabel> SourceGroupNames { get; set; } \n\n        /// <summary>\n        /// The destination group names for the language definition\n        /// </summary>\n        List<LanguageLabel> DestinationGroupNames { get; set; }\n\n        /// <summary>\n        /// The room names for the language definition\n        /// </summary>\n        List<LanguageLabel> RoomNames { get; set; } \n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ILanguageProvider.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n   \n    /// <summary>\n    /// Defines the contract for ILanguageProvider\n    /// </summary>\n    public interface ILanguageProvider\n    {\n        /// <summary>\n        /// The current language definition\n        /// </summary>\n        ILanguageDefinition CurrentLanguage { get; set; }\n\n        /// <summary>\n        /// Event raised when the current language changes\n        /// </summary>\n        event EventHandler CurrentLanguageChanged;\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ILevelControls.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for ILevelControls\n    /// </summary>\n    public interface ILevelControls\n    {\n        /// <summary>\n        /// The level control points\n        /// </summary>\n        Dictionary<string, IBasicVolumeWithFeedback> LevelControlPoints { get; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMeterFeedback.cs",
    "content": "using System;\r\n\r\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\r\n{\r\n\r\n  /// <summary>\r\n  /// Interface for devices that provide audio meter feedback.\r\n  /// This interface is used to standardize access to meter feedback across different devices.\r\n  /// </summary>\r\n  public interface IMeterFeedback\r\n  {\r\n    /// <summary>\r\n    /// Gets the meter feedback for the device.\r\n    /// This property provides an IntFeedback that represents the current audio level or meter value.\r\n    /// </summary>\r\n    IntFeedback MeterFeedback { get; }\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n\n    /// <summary>\n    /// Defines the contract for IMobileControl\n    /// </summary>\n    public interface IMobileControl : IKeyed\n    {\n        /// <summary>\n        /// Gets the Host\n        /// </summary>\n        string Host { get; }\n\n        /// <summary>\n        /// Gets the Client App URL\n        /// </summary>\n        string ClientAppUrl { get; }\n\n        /// <summary>\n        /// Gets the System UUID\n        /// </summary>\n        string SystemUuid { get; }\n\n        /// <summary>\n        /// Gets the ApiOnlineAndAuthorized feedback\n        /// </summary>\n        BoolFeedback ApiOnlineAndAuthorized { get; }\n\n        /// <summary>\n        /// Sends the message object to the AppServer\n        /// </summary>\n        /// <param name=\"o\">Message to send</param>\n        void SendMessageObject(IMobileControlMessage o);\n\n        /// <summary>\n        /// Adds an action for a messenger\n        /// </summary>\n        /// <typeparam name=\"T\">Messenger type. Must implement IMobileControlMessenger</typeparam>\n        /// <param name=\"messenger\">messenger to register</param>\n        /// <param name=\"action\">action to add</param>\n        void AddAction<T>(T messenger, Action<string, string, JToken> action) where T : IMobileControlMessenger;\n\n        /// <summary>\n        /// Removes an action for a messenger\n        /// </summary>\n        /// <param name=\"key\">key for action</param>\n        void RemoveAction(string key);\n\n        /// <summary>\n        /// Adds a device messenger\n        /// </summary>\n        /// <param name=\"messenger\">Messenger to add</param>\n        void AddDeviceMessenger(IMobileControlMessenger messenger);\n\n        /// <summary>\n        /// Check if a device messenger exists\n        /// </summary>\n        /// <param name=\"key\">Messenger key to find</param>\n        bool CheckForDeviceMessenger(string key);\n\n        /// <summary>\n        /// Get a Room Messenger by key\n        /// </summary>\n        /// <param name=\"key\">messenger key to find</param>\n        /// <returns>Messenger if found, null otherwise</returns>\n        IMobileControlRoomMessenger GetRoomMessenger(string key);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlAction.cs",
    "content": "using System;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n  /// <summary>\n  /// Defines the contract for IMobileControlAction\n  /// </summary>\n  public interface IMobileControlAction\n  {\n    /// <summary>\n    /// The messenger to use for mobile control actions\n    /// </summary>\n    IMobileControlMessenger Messenger { get; }\n\n    /// <summary>\n    /// The action to perform for mobile control actions\n    /// </summary>\n    Action<string, string, JToken> Action { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlCrestronTouchpanelController.cs",
    "content": "using System.Collections.ObjectModel;\nusing Crestron.SimplSharpPro;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n  /// <summary>\n  /// Describes a MobileControl Crestron Touchpanel Controller\n  /// This interface extends the IMobileControlTouchpanelController to include connected IP information\n  /// </summary>\n  public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController\n  {\n    /// <summary>\n    /// Gets a collection of connected IP information for the touchpanel controller\n    /// </summary>\n    ReadOnlyCollection<ConnectedIpInformation> ConnectedIps { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessage.cs",
    "content": "using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n  /// <summary>\n  /// Defines the contract for IMobileControlMessage\n  /// </summary>\n  public interface IMobileControlMessage\n  {\n    /// <summary>\n    /// The type of mobile control message\n    /// </summary>\n    [JsonProperty(\"type\")]\n    string Type { get; }\n\n    /// <summary>\n    /// The client ID for the mobile control message\n    /// </summary>\n    [JsonProperty(\"clientId\", NullValueHandling = NullValueHandling.Ignore)]\n    string ClientId { get; }\n\n    /// <summary>\n    /// The content of the mobile control message\n    /// </summary>\n    [JsonProperty(\"content\", NullValueHandling = NullValueHandling.Ignore)]\n    JToken Content { get; }\n\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessenger.cs",
    "content": "using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n  /// <summary>\n  /// Defines the contract for IMobileControlMessenger\n  /// </summary>\n  public interface IMobileControlMessenger : IKeyed\n  {\n    /// <summary>\n    /// Parent controller for this messenger\n    /// </summary>\n    IMobileControl AppServerController { get; }\n\n    /// <summary>\n    /// Path to listen for messages\n    /// </summary>\n    string MessagePath { get; }\n\n    /// <summary>\n    /// Key of the device this messenger is associated with\n    /// </summary>\n    string DeviceKey { get; }\n\n    /// <summary>\n    /// Register this messenger with the AppServerController\n    /// </summary>\n    /// <param name=\"appServerController\"></param>\n    void RegisterWithAppServer(IMobileControl appServerController);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlMessengerWithSubscriptions.cs",
    "content": "using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n  /// <summary>\n  /// Defines the contract for IMobileControlMessenger\n  /// </summary>\n  public interface IMobileControlMessengerWithSubscriptions : IMobileControlMessenger\n  {\n    /// <summary>\n    /// Unsubscribe a client from this messenger\n    /// </summary>\n    /// <param name=\"clientId\"></param>\n    void UnsubscribeClient(string clientId);\n\n    /// <summary>\n    /// Register this messenger with the AppServerController\n    /// </summary>\n    /// <param name=\"appServerController\">parent for this messenger</param>\n    /// <param name=\"enableMessengerSubscriptions\">Enable messenger subscriptions</param>\n    void RegisterWithAppServer(IMobileControl appServerController, bool enableMessengerSubscriptions);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlRoomMessenger.cs",
    "content": "using System;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n  /// <summary>\n  /// Defines the contract for IMobileControlRoomMessenger\n  /// </summary>\n  public interface IMobileControlRoomMessenger : IKeyed\n  {\n    /// <summary>\n    /// Raised when the user code changes\n    /// </summary>\n    event EventHandler<EventArgs> UserCodeChanged;\n\n    /// <summary>\n    /// Raised when the user is prompted for the code\n    /// </summary>\n    event EventHandler<EventArgs> UserPromptedForCode;\n\n    /// <summary>\n    /// Raised when a client joins the room\n    /// </summary>\n    event EventHandler<EventArgs> ClientJoined;\n\n    /// <summary>\n    /// Raised when the app url changes\n    /// </summary>\n    event EventHandler<EventArgs> AppUrlChanged;\n\n    /// <summary>\n    /// The user code for joining the room\n    /// </summary>\n    string UserCode { get; }\n\n    /// <summary>\n    /// The QR code URL for joining the room\n    /// </summary>\n    string QrCodeUrl { get; }\n\n    /// <summary>\n    /// The QR code checksum\n    /// </summary>\n    string QrCodeChecksum { get; }\n\n    /// <summary>\n    /// The Mobile Control server URL\n    /// </summary>\n    string McServerUrl { get; }\n\n    /// <summary>\n    /// The name of the room\n    /// </summary>\n    string RoomName { get; }\n\n    /// <summary>\n    /// The Mobile Control app URL\n    /// </summary>\n    string AppUrl { get; }\n\n    /// <summary>\n    /// Updates the url of the Mobile Control app \n    /// </summary>\n    /// <param name=\"url\">The new URL of the Mobile Control app</param>\n    void UpdateAppUrl(string url);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControlTouchpanelController.cs",
    "content": "using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n  /// <summary>\n  /// Defines the contract for IMobileControlTouchpanelController\n  /// </summary>\n  public interface IMobileControlTouchpanelController : IKeyed\n  {\n    /// <summary>\n    /// The default room key for the controller\n    /// </summary>\n    string DefaultRoomKey { get; }\n\n    /// <summary>\n    /// Sets the application URL for the controller\n    /// </summary>\n    /// <param name=\"url\">The application URL</param>\n    void SetAppUrl(string url);\n\n    /// <summary>\n    /// Indicates whether the controller uses a direct server connection\n    /// </summary>\n    bool UseDirectServer { get; }\n\n    /// <summary>\n    /// Indicates whether the controller is a Zoom Room controller\n    /// </summary>\n    bool ZoomRoomController { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INumeric.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Defines the contract for INumericKeypad\n /// </summary>\n\tpublic interface INumericKeypad:IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// Digit buttons 0\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit0(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 1\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit1(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 2\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit2(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 3\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit3(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 4\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\"></param>\n\t\tvoid Digit4(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 5\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit5(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 6\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit6(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 7\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit7(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 8\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit8(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Digit buttons 9\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the digit button command is a press or release action</param>\n\t\tvoid Digit9(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Used to hide/show the button and/or text on the left-hand keypad button\n\t\t/// </summary>\n\t\tbool HasKeypadAccessoryButton1 { get; }\n\n\t\t/// <summary>\n\t\t/// Label for the left-hand keypad button\n\t\t/// </summary>\n\t\tstring KeypadAccessoryButton1Label { get; }\n\n\t\t/// <summary>\n\t\t/// Left-hand keypad button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button command is a press or release action</param>\n\t\tvoid KeypadAccessoryButton1(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Used to hide/show the button and/or text on the right-hand keypad button\n\t\t/// </summary>\n\t\tbool HasKeypadAccessoryButton2 { get; }\n\n\t\t/// <summary>\n\t\t/// Label for the right-hand keypad button\n\t\t/// </summary>\n\t\tstring KeypadAccessoryButton2Label { get; }\n\n\t\t/// <summary>\n\t\t/// Right-hand keypad button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button command is a press or release action</param>\n\t\tvoid KeypadAccessoryButton2(bool pressRelease);\n\t}\n\n\t/// <summary>\n\t/// Defines the contract for ISetTopBoxNumericKeypad\n\t/// </summary>\n\tpublic interface ISetTopBoxNumericKeypad : INumericKeypad\n\t{\n\t\t/// <summary>\n\t\t/// Dash button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button command is a press or release action</param>\n\t\tvoid Dash(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Keypad Enter button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button command is a press or release action</param>\n\t\tvoid KeypadEnter(bool pressRelease);\n\t}\n\n\t/// <summary>\n\t/// INumericExtensions class\n\t/// </summary>\n\tpublic static class INumericExtensions\n\t{\n\t\t/// <summary>\n\t\t/// Links to the smart object, and sets the misc button's labels on joins x and y\n\t\t/// </summary>\n\t\tpublic static void LinkButtons(this INumericKeypad dev, BasicTriList trilist)\n\t\t{\n\t\t\ttrilist.SetBoolSigAction(110, dev.Digit0);\n\t\t\ttrilist.SetBoolSigAction(111, dev.Digit1);\n\t\t\ttrilist.SetBoolSigAction(112, dev.Digit2);\n\t\t\ttrilist.SetBoolSigAction(113, dev.Digit3);\n\t\t\ttrilist.SetBoolSigAction(114, dev.Digit4);\n\t\t\ttrilist.SetBoolSigAction(115, dev.Digit5);\n\t\t\ttrilist.SetBoolSigAction(116, dev.Digit6);\n\t\t\ttrilist.SetBoolSigAction(117, dev.Digit7);\n\t\t\ttrilist.SetBoolSigAction(118, dev.Digit8);\n\t\t\ttrilist.SetBoolSigAction(119, dev.Digit9);\n\t\t\ttrilist.SetBoolSigAction(120, dev.KeypadAccessoryButton1);\n\t\t\ttrilist.SetBoolSigAction(121, dev.KeypadAccessoryButton2);\n\t\t\ttrilist.StringInput[111].StringValue = dev.KeypadAccessoryButton1Label;\n\t\t\ttrilist.StringInput[111].StringValue = dev.KeypadAccessoryButton2Label;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// UnlinkButtons method\n\t\t/// </summary>\n\t\tpublic static void UnlinkButtons(this INumericKeypad dev, BasicTriList trilist)\n\t\t{\n\t\t\ttrilist.ClearBoolSigAction(110);\n\t\t\ttrilist.ClearBoolSigAction(111);\n\t\t\ttrilist.ClearBoolSigAction(112);\n\t\t\ttrilist.ClearBoolSigAction(113);\n\t\t\ttrilist.ClearBoolSigAction(114);\n\t\t\ttrilist.ClearBoolSigAction(115);\n\t\t\ttrilist.ClearBoolSigAction(116);\n\t\t\ttrilist.ClearBoolSigAction(117);\n\t\t\ttrilist.ClearBoolSigAction(118);\n\t\t\ttrilist.ClearBoolSigAction(119);\n\t\t\ttrilist.ClearBoolSigAction(120);\n\t\t\ttrilist.ClearBoolSigAction(121);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/INvxNetworkPortInformation.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing Crestron.SimplSharpPro.DM.Streaming;\r\nusing PepperDash.Core;\r\n\r\nnamespace PepperDash.Essentials.Core\r\n{\r\n    /// <summary>\r\n    /// Represents a collection of network port information and provides notifications when the information changes.\r\n    /// </summary>\r\n    /// <remarks>This interface is designed to provide access to a list of network port details and to notify\r\n    /// subscribers when the port information is updated. Implementations of this interface should ensure that the  <see\r\n    /// cref=\"PortInformationChanged\"/> event is raised whenever the <see cref=\"NetworkPorts\"/> collection\r\n    /// changes.</remarks>\r\n    public interface INvxNetworkPortInformation : IKeyed\r\n    {\r\n        /// <summary>\r\n        /// Occurs when the port information changes.\r\n        /// </summary>\r\n        /// <remarks>This event is triggered whenever there is a change in the port information, such as\r\n        /// updates to port settings or status. Subscribers can handle this event to respond to such changes.</remarks>\r\n        event EventHandler PortInformationChanged;\r\n\r\n        /// <summary>\r\n        /// Gets the collection of network port information associated with the current instance.\r\n        /// </summary>\r\n        /// <remarks>The collection provides information about the network ports, such as their status,\r\n        /// configuration, or other relevant details. The returned list is read-only and cannot be modified\r\n        /// directly.</remarks>\r\n        List<NvxNetworkPortInformation> NetworkPorts { get; }\r\n    }\r\n\r\n    /// <summary>\r\n    /// Represents information about a network port, including its configuration and associated system details.\r\n    /// </summary>\r\n    /// <remarks>This class provides properties to describe various attributes of a network port, such as its\r\n    /// name, description, VLAN configuration, and management IP address. It is typically used to store and retrieve \r\n    /// metadata about network ports in a managed environment.</remarks>\r\n    public class NvxNetworkPortInformation\r\n    {\r\n        private readonly DmNvxBaseClass.DmNvx35xNetwork.DmNvxNetworkLldpPort port;\r\n\r\n        /// <summary>\r\n        /// Gets or sets the index of the device port.\r\n        /// </summary>\r\n        public uint DevicePortIndex { get; }\r\n\r\n        /// <summary>\r\n        /// Gets or sets the name of the port used for communication.\r\n        /// </summary>        \r\n        public string PortName => port.PortNameFeedback.StringValue;\r\n\r\n        /// <summary>\r\n        /// Gets or sets the description of the port.\r\n        /// </summary>\r\n        public string PortDescription => port.PortNameDescriptionFeedback.StringValue;\r\n\r\n        /// <summary>\r\n        /// Gets or sets the name of the VLAN (Virtual Local Area Network).\r\n        /// </summary>\r\n        public string VlanName => port.VlanNameFeedback.StringValue;\r\n\r\n        /// <summary>\r\n        /// Gets the IP management address associated with the port.\r\n        /// </summary>\r\n        public string IpManagementAddress => port.IpManagementAddressFeedback.StringValue;\r\n\r\n        /// <summary>\r\n        /// Gets the name of the system as reported by the associated port.\r\n        /// </summary>\r\n        public string SystemName => port.SystemNameFeedback.StringValue;\r\n\r\n        /// <summary>\r\n        /// Gets the description of the system name.\r\n        /// </summary>\r\n        public string SystemNameDescription => port.SystemNameDescriptionFeedback.StringValue;\r\n\r\n        /// <summary>\r\n        /// Initializes a new instance of the <see cref=\"NvxNetworkPortInformation\"/> class with the specified network port\r\n        /// and device port index.\r\n        /// </summary>\r\n        /// <param name=\"port\">The network port associated with the device. Cannot be <see langword=\"null\"/>.</param>\r\n        /// <param name=\"devicePortIndex\">The index of the device port.</param>\r\n        /// <exception cref=\"ArgumentNullException\">Thrown if <paramref name=\"port\"/> is <see langword=\"null\"/>.</exception>\r\n        public NvxNetworkPortInformation(DmNvxBaseClass.DmNvx35xNetwork.DmNvxNetworkLldpPort port, uint devicePortIndex)\r\n        {\r\n            this.port = port ?? throw new ArgumentNullException(nameof(port), \"Port cannot be null\");\r\n            DevicePortIndex = devicePortIndex;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IPasswordPrompt.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Describes the functionality required to prompt a user to enter a password\n    /// </summary>\n    public interface IPasswordPrompt\n    {\n        /// <summary>\n        /// Notifies when a password is required or is entered incorrectly\n        /// </summary>\n        event EventHandler<PasswordPromptEventArgs> PasswordRequired;\n\n        /// <summary>\n        /// Submits the password\n        /// </summary>\n        /// <param name=\"password\">The password to submit</param>\n        void SubmitPassword(string password);\n    }\n\n    /// <summary>\n    /// PasswordPromptEventArgs class\n    /// </summary>\n    public class PasswordPromptEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Indicates if the last submitted password was incorrect\n        /// </summary>\n        public bool LastAttemptWasIncorrect { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the LoginAttemptFailed\n        /// </summary>\n        public bool LoginAttemptFailed { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the LoginAttemptCancelled\n        /// </summary>\n        public bool LoginAttemptCancelled { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Message\n        /// </summary>\n        public string Message { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"lastAttemptIncorrect\">indicates if the last submitted password was incorrect</param>\n        /// <param name=\"loginFailed\">indicates if the login attempt failed</param>\n        /// <param name=\"loginCancelled\">indicates if the login attempt was cancelled</param>\n        /// <param name=\"message\">provides a message related to the password prompt</param>\n        public PasswordPromptEventArgs(bool lastAttemptIncorrect, bool loginFailed, bool loginCancelled, string message)\n        {\n            LastAttemptWasIncorrect = lastAttemptIncorrect;\n            LoginAttemptFailed = loginFailed;\n            LoginAttemptCancelled = loginCancelled;\n            Message = message;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IPower.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Crestron.SimplSharpPro.Fusion;\n\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\n    /// <summary>\n    /// Adds feedback for current power state\n    /// </summary>\n    public interface IHasPowerControlWithFeedback : IHasPowerControl\n    {\n        /// <summary>\n        /// Feedback indicating whether the device is powered on\n        /// </summary>\n        BoolFeedback PowerIsOnFeedback { get; }\n    }\n\n    /// <summary>\n    /// Defines the ability to power a device on and off\n    /// </summary>\n    public interface IHasPowerControl\n    {\n        /// <summary>\n        /// Powers the device on\n        /// </summary>\n        void PowerOn();\n\n        /// <summary>\n        /// Powers the device off\n        /// </summary>\n        void PowerOff();\n\n        /// <summary>\n        /// Toggles the power state of the device\n        /// </summary>\n        void PowerToggle();\n    }\n\n\t/// <summary>\n\t/// IHasPowerControlExtensions class\n\t/// </summary>\n\tpublic static class IHasPowerControlExtensions\n\t{\n        /// <summary>\n        /// LinkButtons method\n        /// </summary>\n        public static void LinkButtons(this IHasPowerControl dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.SetSigFalseAction(101, dev.PowerOn);\n\t\t\ttriList.SetSigFalseAction(102, dev.PowerOff);\n\t\t\ttriList.SetSigFalseAction(103, dev.PowerToggle);\n\n            var fbdev = dev as IHasPowerControlWithFeedback;\n            if (fbdev != null)\n            {\n                fbdev.PowerIsOnFeedback.LinkInputSig(triList.BooleanInput[101]);\n            }\n\t\t}\n\n        /// <summary>\n        /// UnlinkButtons method\n        /// </summary>\n        public static void UnlinkButtons(this IHasPowerControl dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.ClearBoolSigAction(101);\n\t\t\ttriList.ClearBoolSigAction(102);\n\t\t\ttriList.ClearBoolSigAction(103);\n\n            var fbdev = dev as IHasPowerControlWithFeedback;\n            if (fbdev != null)\n            {\n                fbdev.PowerIsOnFeedback.UnlinkInputSig(triList.BooleanInput[101]);\n            }\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IProjectorScreenLiftControl.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing System;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n\t/// <summary>\n\t/// Defines the contract for IProjectorScreenLiftControl\n\t/// </summary>\n\tpublic interface IProjectorScreenLiftControl\n\t{\n\t\t/// <summary>\n\t\t/// Raises the screen/lift\n\t\t/// </summary>\n\t\tvoid Raise();\n\n\t\t/// <summary>\n\t\t/// Lowers the screen/lift\n\t\t/// </summary>\n\t\tvoid Lower();\n\n\t\t/// <summary>\n\t\t/// Stops the screen/lift\n\t\t/// </summary>\n\t\tBoolFeedback IsInUpPosition { get; }\n\n\t\t/// <summary>\n\t\t/// Gets whether the screen/lift is in the up position\n\t\t/// </summary>\n\t\tbool InUpPosition { get; }\n\n\t\t/// <summary>\n\t\t/// Gets whether the screen/lift is in the down position\n\t\t/// </summary>\n\t\tevent EventHandler<EventArgs> PositionChanged;\n\n\t\t/// <summary>\n\t\t/// The device key of the display associated with this screen/lift\n\t\t/// </summary>\n\t\tstring DisplayDeviceKey { get; }\n\n\t\t/// <summary>\n\t\t/// The type of device\n\t\t/// </summary>\n\t\teScreenLiftControlType Type { get; } // screen/lift\n\t}\n\n\t/// <summary>\n\t/// Enumeration of eScreenLiftControlType values\n\t/// </summary>\n\tpublic enum eScreenLiftControlType\n\t{\n\t\t/// <summary>\n\t\t/// Lift type device\n\t\t/// </summary>\n\t\tlift,\n\n\t\t/// <summary>\n\t\t/// Screen type device\n\t\t/// </summary>\n\t\tscreen\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ISelectableItem.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n\n    /// <summary>\n    /// Defines the contract for ISelectableItem\n    /// </summary>\n    public interface ISelectableItem : IKeyName\n    {\n        /// <summary>\n        /// Raised when the item is updated\n        /// </summary>\n        event EventHandler ItemUpdated;\n\n        /// <summary>\n        /// Gets or sets whether the item is selected\n        /// </summary>\n        [JsonProperty(\"isSelected\")]\n        bool IsSelected { get; set;  }\n\n        /// <summary>\n        /// Selects the item\n        /// </summary>\n        void Select();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ISelectableItems.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for ISelectableItems\n    /// </summary>\n    public interface ISelectableItems<TKey, TValue> where TValue : ISelectableItem\n    {\n        /// <summary>\n        /// Raised when the items are updated\n        /// </summary>\n        event EventHandler ItemsUpdated;\n\n        /// <summary>\n        /// Raised when the current item changes\n        /// </summary>\n        event EventHandler CurrentItemChanged;\n\n        /// <summary>\n        /// Gets or sets the collection of selectable items\n        /// </summary>\n        [JsonProperty(\"items\")]\n        Dictionary<TKey, TValue> Items { get; set; }\n\n        /// <summary>\n        /// Gets or sets the current selected item key\n        /// </summary>\n        [JsonProperty(\"currentItem\")]\n        TKey CurrentItem { get; set; }\n\n    }\n\n    /// <summary>\n    /// Describes a collection of items that can be selected\n    /// </summary>\n    /// <typeparam name=\"TKey\">type for the keys in the collection.  Probably a string or enum</typeparam>\n    public interface ISelectableItems<TKey> : ISelectableItems<TKey, ISelectableItem>\n    {        \n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ISetTopBoxControls.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Defines the contract for ISetTopBoxControls\n\t/// </summary>\n\tpublic interface ISetTopBoxControls : IChannel, IColor, IDPad, ISetTopBoxNumericKeypad, \n\t\tITransport, IUiDisplayInfo\n\t{\n\t\t/// <summary>\n\t\t/// Show DVR controls?\n\t\t/// </summary>\n\t\tbool HasDvr { get; }\n\n\t\t/// <summary>\n\t\t/// Show presets controls?\n\t\t/// </summary>\n\t\tbool HasPresets { get; }\n\n\t\t/// <summary>\n\t\t/// Show number pad controls?\n\t\t/// </summary>\n\t\tbool HasNumeric { get; }\n\n\t\t/// <summary>\n\t\t/// Show D-pad controls?\n\t\t/// </summary>\n\t\tbool HasDpad { get; }\n\n\t\t/// <summary>\n\t\t/// TV Presets model\n\t\t/// </summary>\n\t\tPepperDash.Essentials.Core.Presets.DevicePresetsModel TvPresets { get; }\n\n\t\t/// <summary>\n\t\t/// LoadPresets method\n\t\t/// </summary>\n\t\t/// <param name=\"filePath\">path to file that contains the presets</param>\n\t\tvoid LoadPresets(string filePath);\n\n\t\t/// <summary>\n\t\t/// DvrList button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid DvrList(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Replay button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid Replay(bool pressRelease);\n\t}\n\n\t/// <summary>\n\t/// ISetTopBoxControlsExtensions class\n\t/// </summary>\n\tpublic static class ISetTopBoxControlsExtensions\n\t{\n\t\t/// <summary>\n\t\t/// LinkButtons method\n\t\t/// </summary>\n\t\t/// <param name=\"dev\">The ISetTopBoxControls device</param>\n\t\t/// <param name=\"triList\">The BasicTriList to link buttons to</param>\n\t\tpublic static void LinkButtons(this ISetTopBoxControls dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.SetBoolSigAction(136, dev.DvrList);\n\t\t\ttriList.SetBoolSigAction(152, dev.Replay);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// UnlinkButtons method\n\t\t/// </summary>\n\t\t/// <param name=\"dev\">The ISetTopBoxControls device</param>\n\t\t/// <param name=\"triList\">The BasicTriList to unlink buttons from</param>\n\t\tpublic static void UnlinkButtons(this ISetTopBoxControls dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.ClearBoolSigAction(136);\n\t\t\ttriList.ClearBoolSigAction(152);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IStateFeedback.cs",
    "content": "using System;\r\n\r\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\r\n{\r\n\r\n  /// <summary>\r\n  /// Interface for devices that provide state feedback.\r\n  /// This interface is used to standardize access to state feedback across different devices.\r\n  /// </summary>\r\n  public interface IStateFeedback\r\n  {\r\n    /// <summary>\r\n    /// Gets the state feedback for the device.\r\n    /// This property provides a BoolFeedback that represents the current state (on/off) of the device.\r\n    /// </summary>\r\n    BoolFeedback StateFeedback { get; }\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ITemperatureSensor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for ITemperatureSensor\n    /// </summary>\n    public interface ITemperatureSensor\n    {\n        /// <summary>\n        ///  The values will range from -400 to +1760 (for -40° to +176° F) or -400 to +800\n        ///    (for -40° to +80° C)in tenths of a degree.\n        /// </summary>\n        IntFeedback TemperatureFeedback { get; }\n\n        /// <summary>\n        /// The temperature in Celsius format\n        /// </summary>\n        BoolFeedback TemperatureInCFeedback { get; }\n\n        /// <summary>\n        /// Sets the temperature format to Celsius or Fahrenheit\n        /// </summary>\n        /// <param name=\"setToC\">If true, sets the format to Celsius; otherwise, sets it to Fahrenheit.</param>\n        void SetTemperatureFormat(bool setToC);\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ITransport.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Defines the contract for ITransport\n /// </summary>\n\tpublic interface ITransport\n\t{\n\t\t/// <summary>\n\t\t/// Play button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid Play(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Pause button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid Pause(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Rewind button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid Rewind(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Fast Forward button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid FFwd(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Chapter Minus button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid ChapMinus(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Chapter Plus button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid ChapPlus(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Stop button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid Stop(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Record button action\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\">determines if the button action is a press or release</param>\n\t\tvoid Record(bool pressRelease);\n\t}\n\n\t/// <summary>\n\t/// ITransportExtensions class\n\t/// </summary>\n\tpublic static class ITransportExtensions\n\t{\n\t\t/// <summary>\n\t\t/// Attaches to trilist joins: Play:145, Pause:146, Stop:147, ChapPlus:148, ChapMinus:149, Rewind:150, Ffwd:151, Record:154\n\t\t/// </summary>\n\t\t/// <param name=\"dev\">The ITransport device</param>\n\t\t/// <param name=\"triList\">The BasicTriList to link buttons to</param>\n\t\tpublic static void LinkButtons(this ITransport dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.SetBoolSigAction(145, dev.Play);\n\t\t\ttriList.SetBoolSigAction(146, dev.Pause);\n\t\t\ttriList.SetBoolSigAction(147, dev.Stop);\n\t\t\ttriList.SetBoolSigAction(148, dev.ChapPlus);\n\t\t\ttriList.SetBoolSigAction(149, dev.ChapMinus);\n\t\t\ttriList.SetBoolSigAction(150, dev.Rewind);\n\t\t\ttriList.SetBoolSigAction(151, dev.FFwd);\n\t\t\ttriList.SetBoolSigAction(154, dev.Record);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// UnlinkButtons method\n\t\t/// </summary>\n\t\t/// <param name=\"dev\">The ITransport device</param>\n\t\t/// <param name=\"triList\">The BasicTriList to unlink buttons from</param>\n\t\tpublic static void UnlinkButtons(this ITransport dev, BasicTriList triList)\n\t\t{\n\t\t\ttriList.ClearBoolSigAction(145);\n\t\t\ttriList.ClearBoolSigAction(146);\n\t\t\ttriList.ClearBoolSigAction(147);\n\t\t\ttriList.ClearBoolSigAction(148);\n\t\t\ttriList.ClearBoolSigAction(149);\n\t\t\ttriList.ClearBoolSigAction(150);\n\t\t\ttriList.ClearBoolSigAction(151);\n\t\t\ttriList.ClearBoolSigAction(154);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ITvPresetsProvider.cs",
    "content": "﻿using PepperDash.Essentials.Core.Presets;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for ITvPresetsProvider\n    /// </summary>\n    public interface ITvPresetsProvider\n    {\n        /// <summary>\n        /// The TV presets model\n        /// </summary>\n        DevicePresetsModel TvPresets { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IUiDisplayInfo.cs",
    "content": "﻿using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Defines the contract for IUiDisplayInfo\n\t/// </summary>\n\tpublic interface IUiDisplayInfo : IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// Display UI Type\n\t\t/// </summary>\n\t\tuint DisplayUiType { get; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IWarmingCooling.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Defines a class that has warm up and cool down\n\t/// </summary>\n\tpublic interface IWarmingCooling\n\t{\n\t\t/// <summary>\n\t\t/// Feedback indicating whether the device is warming up\n\t\t/// </summary>\n\t\tBoolFeedback IsWarmingUpFeedback { get; }\n\n\t\t/// <summary>\n\t\t/// Feedback indicating whether the device is cooling down\n\t\t/// </summary>\n\t\tBoolFeedback IsCoolingDownFeedback { get; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/LanguageLabel.cs",
    "content": "﻿using System;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Represents a LanguageLabel\n    /// </summary>\n    public class LanguageLabel\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; set; }\n        /// <summary>\n        /// Gets or sets the Description\n        /// </summary>\n        public string Description { get; set; }\n        /// <summary>\n        /// Gets or sets the DisplayText\n        /// </summary>\n        public string DisplayText { get; set; }\n        /// <summary>\n        /// Gets or sets the JoinNumber\n        /// </summary>\n        public uint JoinNumber { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/Template.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.SmartObjects;\n\nnamespace PepperDash.Essentials.Core\n{\n\t\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/DeviceTypeInterfaces/eVolumeLevelUnits.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Volume level units\n  /// </summary>\n  public enum eVolumeLevelUnits\n  {\n    /// <summary>\n    /// Decibels\n    /// </summary>\n    Decibels,\n\n    /// <summary>\n    /// Percent\n    /// </summary>\n    Percent,\n\n    /// <summary>\n    /// Relative\n    /// </summary>\n    Relative,\n\n    /// <summary>\n    /// Absolute\n    /// </summary>\n    Absolute\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/AudioControlListItemBase.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Base class for audio control list items\n    /// </summary>\n    public abstract class AudioControlListItemBase\n    {\n        /// <summary>\n        /// Key of the parent device in the DeviceManager\n        /// </summary>\n        [JsonProperty(\"parentDeviceKey\")]\n        public string ParentDeviceKey { get; set; }\n\n        /// <summary>\n        /// Optional key of the item in the parent device\n        /// </summary>\n        [JsonProperty(\"itemKey\")]\n        public string ItemKey { get; set; }\n\n        /// <summary>\n        /// A name that will override the items's name on the UI\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Indicates if the item should be included in the user accessible list\n        /// </summary>\n        [JsonProperty(\"includeInUserList\")]\n        public bool IncludeInUserList { get; set; }\n\n        /// <summary>\n        /// Used to specify the order of the items in the source list when displayed\n        /// </summary>\n        [JsonProperty(\"order\")]\n        public int Order { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/AudioInterfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core\n{\n\n /// <summary>\n /// Enumeration of AudioChangeType values\n /// </summary>\n\tpublic enum AudioChangeType\n\t{\n\t\t/// <summary>\n\t\t/// Mute change\n\t\t/// </summary>\n\t\tMute, \n\t\t\n\t\t/// <summary>\n\t\t/// Volume change\n\t\t/// </summary>\n\t\tVolume\n\t}\n\n\t/// <summary>\n\t/// Represents a AudioChangeEventArgs\n\t/// </summary>\n\tpublic class AudioChangeEventArgs\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the ChangeType\n\t\t/// </summary>\n\t\tpublic AudioChangeType ChangeType { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the AudioDevice\n\t\t/// </summary>\n\t\tpublic IBasicVolumeControls AudioDevice { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"device\">device that changed</param>\n\t\t/// <param name=\"changeType\">type of change</param>\n\t\tpublic AudioChangeEventArgs(IBasicVolumeControls device, AudioChangeType changeType)\n\t\t{\n\t\t\tChangeType = changeType;\n\t\t\tAudioDevice = device;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/CameraListItem.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a CameraListItem\n    /// </summary>\n    public class CameraListItem\n    {\n        /// <summary>\n        /// Key of the camera device\n        /// </summary>\n        [JsonProperty(\"deviceKey\")]\n        public string DeviceKey { get; set; }\n\n        /// <summary>\n        /// Returns the source Device for this, if it exists in DeviceManager\n        /// </summary>\n        [JsonIgnore]\n        public Device CameraDevice\n        {\n            get\n            {\n                if (_cameraDevice == null)\n                    _cameraDevice = DeviceManager.GetDeviceForKey(DeviceKey) as Device;\n                return _cameraDevice;\n            }\n        }\n        Device _cameraDevice;\n\n        /// <summary>\n        /// Gets either the source's Name or this AlternateName property, if \n        /// defined.  If source doesn't exist, returns \"Missing source\"\n        /// </summary>\n        [JsonProperty(\"preferredName\")]\n        public string PreferredName\n        {\n            get\n            {\n                if (string.IsNullOrEmpty(Name))\n                {\n                    if (CameraDevice == null)\n                        return \"---\";\n                    return CameraDevice.Name;\n                }\n                return Name;\n            }\n        }\n\n        /// <summary>\n        /// A name that will override the source's name on the UI\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n\n        /// <summary>\n        /// Specifies and icon for the source list item\n        /// </summary>\n        [JsonProperty(\"icon\")]\n        public string Icon { get; set; }\n\n        /// <summary>\n        /// Alternate icon\n        /// </summary>\n\t\t[JsonProperty(\"altIcon\", NullValueHandling = NullValueHandling.Ignore)]\n        public string AltIcon { get; set; }\n\n        /// <summary>\n        /// Indicates if the item should be included in the user facing list\n        /// </summary>\n\t\t[JsonProperty(\"includeInUserList\")]\n        public bool IncludeInUserList { get; set; }\n\n        /// <summary>\n        /// Used to specify the order of the items in the source list when displayed\n        /// </summary>\n\t\t[JsonProperty(\"order\")]\n        public int Order { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/CodecInterfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Adds control of codec receive volume\n    /// </summary>\n    public interface IReceiveVolume\n    {\n        // Break this out into 3 interfaces\n\n        /// <summary>\n        /// Sets the receive volume level\n        /// </summary>\n        /// <param name=\"level\">volume level to set</param>\n        void SetReceiveVolume(ushort level);\n\n        /// <summary>\n        /// Mutes the receive audio\n        /// </summary>\n        void ReceiveMuteOn();\n\n        /// <summary>\n        /// Unmutes the receive audio\n        /// </summary>\n        void ReceiveMuteOff();\n\n        /// <summary>\n        /// Toggles the receive mute state\n        /// </summary>\n        void ReceiveMuteToggle();\n\n        /// <summary>\n        /// Feedback for the receive volume level\n        /// </summary>\n        IntFeedback ReceiveLevelFeedback { get; }\n\n        /// <summary>\n        /// Feedback for the receive mute state\n        /// </summary>\n        BoolFeedback ReceiveMuteIsOnFeedback { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for ITransmitVolume\n    /// </summary>\n    public interface ITransmitVolume\n    {\n        /// <summary>\n        /// Sets the transmit volume level\n        /// </summary>\n        /// <param name=\"level\">volume level to set</param>\n        void SetTransmitVolume(ushort level);\n\n        /// <summary>\n        /// Mutes the transmit audio\n        /// </summary>\n        void TransmitMuteOn();\n\n        /// <summary>\n        /// Unmutes the transmit audio\n        /// </summary>\n        void TransmitMuteOff();\n\n        /// <summary>\n        /// Toggles the transmit mute state\n        /// </summary>\n        void TransmitMuteToggle();\n\n        /// <summary>\n        /// Feedback for the transmit volume level\n        /// </summary>\n        IntFeedback TransmitLevelFeedback { get; }\n\n        /// <summary>\n        /// Feedback for the transmit mute state\n        /// </summary>\n        BoolFeedback TransmitMuteIsOnFeedback { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IPrivacy\n    /// </summary>\n    public interface IPrivacy\n    {\n        /// <summary>\n        /// Enables privacy mode\n        /// </summary>\n        void PrivacyModeOn();\n\n        /// <summary>\n        /// Disables privacy mode\n        /// </summary>\n        void PrivacyModeOff();\n\n        /// <summary>\n        /// Toggles privacy mode\n        /// </summary>\n        void PrivacyModeToggle();\n\n        /// <summary>\n        /// Feedback for the privacy mode state\n        /// </summary>\n        BoolFeedback PrivacyModeIsOnFeedback { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/ConfigSnippetAttribute.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Represents a ConfigSnippetAttribute\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]\n  public class ConfigSnippetAttribute : Attribute\n  {\n    /// <summary>\n    /// Represents a configuration snippet for the device.\n    /// </summary>\n    /// <param name=\"configSnippet\"></param>\n    public ConfigSnippetAttribute(string configSnippet)\n    {\n      ConfigSnippet = configSnippet;\n    }\n\n    /// <summary>\n    /// Gets the configuration snippet for the device.\n    /// This snippet can be used in the DeviceConfig to instantiate the device.\n    /// </summary>\n    public string ConfigSnippet { get; }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/CrestronProcessor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.CrestronIO;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Devices\n{\n    /// <summary>\n    /// This wrapper class is meant to allow interfaces to be applied to any Crestron processor\n    /// </summary>\n    public class CrestronProcessor : Device, ISwitchedOutputCollection\n    {\n        /// <summary>\n        /// Collection of switched outputs (relays) on the processor\n        /// </summary>\n        public Dictionary<uint, ISwitchedOutput> SwitchedOutputs { get; private set; }\n\n        /// <summary>\n        /// The underlying CrestronControlSystem processor\n        /// </summary>\n        public Crestron.SimplSharpPro.CrestronControlSystem Processor { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key for the processor</param>\n        public CrestronProcessor(string key)\n            : base(key)\n        {\n            SwitchedOutputs = new Dictionary<uint, ISwitchedOutput>();\n            Processor = Global.ControlSystem;\n\n            GetRelays();\n        }\n\n        /// <summary>\n        /// Creates a GenericRelayDevice for each relay on the processor and adds them to the SwitchedOutputs collection\n        /// </summary>\n        void GetRelays()\n        {\n            try\n            {\n                if (Processor.SupportsRelay)\n                {\n                    for (uint i = 1; i <= Processor.NumberOfRelayPorts; i++)\n                    {\n                        var relay = new GenericRelayDevice(string.Format(\"{0}-relay-{1}\", this.Key, i), Processor.RelayPorts[i]);\n                        SwitchedOutputs.Add(i, relay);\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Error Getting Relays from processor:\\n '{0}'\", e);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/DescriptionAttribute.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Represents a description attribute for a device.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]\n  public class DescriptionAttribute : Attribute\n  {\n    /// <summary>\n    /// Represents a description attribute for a device.\n    /// </summary>\n    /// <param name=\"description\"></param>\n    public DescriptionAttribute(string description)\n    {\n      Description = description;\n    }\n\n    /// <summary>\n    /// Gets the description for the device.\n    /// </summary>\n    public string Description { get; }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/DestinationListItem.cs",
    "content": "﻿\n\nusing Newtonsoft.Json;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a destination item in a routing system that can receive audio/video signals.\n    /// Contains information about the destination device, its properties, and location settings.\n    /// </summary>\n    public class DestinationListItem\n    {\n        /// <summary>\n        /// Gets or sets the key identifier for the sink device that this destination represents.\n        /// </summary>\n        [JsonProperty(\"sinkKey\")]\n        public string SinkKey { get; set; }\n\n        private EssentialsDevice _sinkDevice;\n\n        /// <summary>\n        /// Gets the actual device instance for this destination. \n        /// Lazily loads the device from the DeviceManager using the SinkKey.\n        /// </summary>\n        [JsonIgnore]\n        public EssentialsDevice SinkDevice\n        {\n            get { return _sinkDevice ?? (_sinkDevice = DeviceManager.GetDeviceForKey(SinkKey) as EssentialsDevice); }\n        }\n\n        /// <summary>\n        /// Gets the preferred display name for this destination.\n        /// Returns the custom Name if set, otherwise returns the SinkDevice name, or \"---\" if no device is found.\n        /// </summary>\n        [JsonProperty(\"preferredName\")]\n        public string PreferredName\n        {\n            get\n            {\n                if (!string.IsNullOrEmpty(Name))\n                {\n                    return Name;\n                }\n\n                return SinkDevice == null ? \"---\" : SinkDevice.Name;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the custom name for this destination. \n        /// If set, this name will be used as the PreferredName instead of the device name.\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether this destination should be included in destination lists.\n        /// </summary>\n        [JsonProperty(\"includeInDestinationList\")]\n        public bool IncludeInDestinationList { get; set; }\n\n        /// <summary>\n        /// Gets or sets the display order for this destination in lists.\n        /// Lower values appear first in sorted lists.\n        /// </summary>\n        [JsonProperty(\"order\")]\n        public int Order { get; set; }\n\n        /// <summary>\n        /// Gets or sets the surface location identifier for this destination.\n        /// Used to specify which surface or screen this destination is located on.\n        /// </summary>\n        [JsonProperty(\"surfaceLocation\")]\n        public int SurfaceLocation { get; set; }\n\n        /// <summary>\n        /// Gets or sets the vertical location position for this destination.\n        /// Used for spatial positioning in multi-display configurations.\n        /// </summary>\n        [JsonProperty(\"verticalLocation\")]\n        public int VerticalLocation { get; set; }\n\n        /// <summary>\n        /// Gets or sets the horizontal location position for this destination.\n        /// Used for spatial positioning in multi-display configurations.\n        /// </summary>\n        [JsonProperty(\"horizontalLocation\")]\n        public int HorizontalLocation { get; set; }\n\n        /// <summary>\n        /// Gets or sets the signal type that this destination can receive (Audio, Video, AudioVideo, etc.).\n        /// </summary>\n        [JsonProperty(\"sinkType\")]\n        public eRoutingSignalType SinkType { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether this destination is used for codec content sharing.\n        /// </summary>\n        [JsonProperty(\"isCodecContentDestination\")]\n        public bool isCodecContentDestination { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether this destination is used for program audio output.\n        /// </summary>\n        [JsonProperty(\"isProgramAudioDestination\")]\n        public bool isProgramAudioDestination { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether this destination supports USB connections.\n        /// Indicates if the destination can handle USB functionality, such as USB signal routing or device connections.\n        /// This property is used to determine compatibility with USB-based devices or systems.\n        /// </summary>\n        [JsonProperty(\"supportsUsb\")]\n        public bool SupportsUsb { get; set; }\n\n        /// <summary>\n        /// The key of the destination port associated with this destination item\n        /// This is used to identify the specific port on the destination device that this item refers to for advanced routing\n        /// </summary>\n        [JsonProperty(\"destinationPortKey\")]\n        public string DestinationPortKey { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/DeviceApiBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core.Devices\n{\n    /// <summary>\n    /// Base class for all Device APIs\n    /// </summary>\n    public abstract class DeviceApiBase\n    {\n        /// <summary>\n        /// Action API dictionary\n        /// </summary>\n        public Dictionary<string, Object> ActionApi { get; protected set; }\n\n        /// <summary>\n        /// Feedback API dictionary\n        /// </summary>\n        public Dictionary<string, Feedback> FeedbackApi { get; protected set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/DeviceFeedbackExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// DeviceFeedbackExtensions class\n    /// </summary>\n    public static class DeviceFeedbackExtensions\n    {\n        /// <summary>\n        /// Attempts to get and return a feedback property from a device by name.\n        /// If unsuccessful, returns null.\n        /// </summary>\n        /// <param name=\"device\">device to get feedback from</param>\n        /// <param name=\"propertyName\">name of the feedback property</param>\n        /// <returns>Feedback property if found, otherwise null</returns>\n        public static Feedback GetFeedbackProperty(this Device device, string propertyName)\n        {\n            var feedback = DeviceJsonApi.GetPropertyByName(device.Key, propertyName) as Feedback;\n\n            if (feedback != null)\n            {\n                return feedback;\n            }\n\n            return null;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs",
    "content": "using Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing Serilog.Events;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a DeviceJsonApi\n    /// </summary>\n    public class DeviceJsonApi\n    {\n        /// <summary>\n        /// DoDeviceActionWithJson method\n        /// </summary>\n        /// <param name=\"json\">json method</param>\n        public static void DoDeviceActionWithJson(string json)\n        {\n            if (String.IsNullOrEmpty(json))\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"Please provide a JSON object matching the format {\\\"deviceKey\\\":\\\"myDevice\\\", \\\"methodName\\\":\\\"someMethod\\\", \\\"params\\\": [\\\"param1\\\", true]}.\\r\\nIf the method has no parameters, the \\\"params\\\" object may be omitted.\");\n                return;\n            }\n            try\n            {\n                var action = JsonConvert.DeserializeObject<DeviceActionWrapper>(json);\n\n                DoDeviceAction(action);\n            }\n            catch (Exception)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Incorrect format for JSON. Please check that the format matches {\\\"deviceKey\\\":\\\"myDevice\\\", \\\"methodName\\\":\\\"someMethod\\\", \\\"params\\\": [\\\"param1\\\", true]}\");\n            }\n\n        }\n\n\n        /// <summary>\n        /// DoDeviceAction method\n        /// </summary>\n        /// <param name=\"action\">action method</param>\n        public static void DoDeviceAction(DeviceActionWrapper action)\n        {\n            var key = action.DeviceKey;\n            var obj = FindObjectOnPath(key);\n            if (obj == null)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Unable to find object at path {0}\", key);\n                return;\n            }\n\n            if (action.Params == null)\n            {\n                //no params, so setting action.Params to empty array\n                action.Params = new object[0];\n            }\n\n            Type t = obj.GetType();\n            try\n            {\n                var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();\n\n                var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length);\n\n                if (method == null)\n                {\n                    CrestronConsole.ConsoleCommandResponse(\n                        \"Unable to find method with name {0} and that matches parameters {1}\", action.MethodName,\n                        action.Params);\n                    return;\n                }\n                var mParams = method.GetParameters();\n\n                var convertedParams = mParams\n                                    .Select((p, i) => ConvertType(action.Params[i], p.ParameterType))\n                                    .ToArray();\n\n                Task.Run(() =>\n                    {\n                        try\n                        {\n                            Debug.LogMessage(LogEventLevel.Verbose, \"Calling method {methodName} on device {deviceKey}\", null, method.Name, action.DeviceKey);\n                            method.Invoke(obj, convertedParams);\n                        }\n                        catch (Exception e)\n                        {\n                            Debug.LogMessage(e, \"Error invoking method {methodName} on device {deviceKey}\", null, method.Name, action.DeviceKey);\n                        }\n                    });\n\n                CrestronConsole.ConsoleCommandResponse(\"Method {0} successfully called on device {1}\", method.Name,\n                    action.DeviceKey);\n            }\n            catch (Exception ex)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Unable to call method with name {0}. {1}\", action.MethodName,\n                    ex.Message);\n            }\n        }\n\n        /// <summary>\n        /// DoDeviceActionAsync method\n        /// </summary>\n        /// <param name=\"action\">action method</param>\n        public static async Task DoDeviceActionAsync(DeviceActionWrapper action)\n        {\n            var key = action.DeviceKey;\n            var obj = FindObjectOnPath(key);\n            if (obj == null)\n            {\n                Debug.LogMessage(LogEventLevel.Warning, \"Unable to find object at path {deviceKey}\", null, key);\n                return;\n            }\n\n            if (action.Params == null)\n            {\n                //no params, so setting action.Params to empty array\n                action.Params = new object[0];\n            }\n\n            Type t = obj.GetType();\n            try\n            {\n                var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();\n\n                var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length);\n\n                if (method == null)\n                {\n                    Debug.LogMessage(LogEventLevel.Warning,\n                        \"Unable to find method with name {methodName} and that matches parameters {@parameters}\", null, action.MethodName,\n                        action.Params);\n                    return;\n                }\n                var mParams = method.GetParameters();\n\n                var convertedParams = mParams\n                                    .Select((p, i) => ConvertType(action.Params[i], p.ParameterType))\n                                    .ToArray();\n\n                try\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Calling method {methodName} on device {deviceKey} with {@params}\", null, method.Name, action.DeviceKey, action.Params);\n                    var result = method.Invoke(obj, convertedParams);\n                    \n                    // If the method returns a Task, await it\n                    if (result is Task task)\n                    {\n                        await task;\n                    }\n                    // If the method returns a Task<T>, await it\n                    else if (result != null && result.GetType().IsGenericType && result.GetType().GetGenericTypeDefinition() == typeof(Task<>))\n                    {\n                        await (Task)result;\n                    }\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(e, \"Error invoking method {methodName} on device {deviceKey}\", null, method.Name, action.DeviceKey);\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Unable to call method with name {methodName} with {@parameters}\", null, action.MethodName, action.Params);\n            }\n        }\n\n        private static object ConvertType(object value, Type conversionType)\n        {\n            if (!conversionType.IsEnum)\n            {\n                return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);\n            }\n\n            var stringValue = Convert.ToString(value);\n\n            if (String.IsNullOrEmpty(stringValue))\n            {\n                throw new InvalidCastException(\n                    String.Format(\"{0} cannot be converted to a string prior to conversion to enum\"));\n            }\n            return Enum.Parse(conversionType, stringValue, true);\n        }\n\n        /// <summary>\n        /// Gets the properties on a device\n        /// </summary>\n        /// <param name=\"deviceObjectPath\">The path to the device object</param>\n        /// <returns>A JSON string representing the properties of the device</returns>\n        public static string GetProperties(string deviceObjectPath)\n        {\n            var obj = FindObjectOnPath(deviceObjectPath);\n            if (obj == null)\n                return \"{ \\\"error\\\":\\\"No Device\\\"}\";\n\n            Type t = obj.GetType();\n            // get the properties and set them into a new collection of NameType wrappers\n            var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));\n            return JsonConvert.SerializeObject(props, Formatting.Indented);\n        }\n\n        /// <summary>\n        /// Gets a property from a device path by name\n        /// </summary>\n        /// <param name=\"deviceObjectPath\">The path to the device object</param>\n        /// <param name=\"propertyName\">The name of the property to get</param>\n        /// <returns>The value of the property</returns>\n        public static object GetPropertyByName(string deviceObjectPath, string propertyName)\n        {\n            var dev = FindObjectOnPath(deviceObjectPath);\n            if (dev == null)\n                return \"{ \\\"error\\\":\\\"No Device\\\"}\";\n\n            object prop = dev.GetType().GetProperty(propertyName).GetValue(dev, null);\n\n            // var prop = t.GetProperty(propertyName);\n            if (prop != null)\n            {\n                return prop;\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"Unable to find Property: {0} on Device with path: {1}\", propertyName, deviceObjectPath);\n                return null;\n            }\n        }\n\n        /// <summary>\n        /// Gets the methods on a device\n        /// </summary>\n        /// <param name=\"deviceObjectPath\">The path to the device object</param>\n        /// <returns>A JSON string representing the methods of the device</returns>\n        public static string GetMethods(string deviceObjectPath)\n        {\n            var obj = FindObjectOnPath(deviceObjectPath);\n            if (obj == null)\n                return \"{ \\\"error\\\":\\\"No Device\\\"}\";\n\n            // Package up method names using helper objects\n            Type t = obj.GetType();\n            var methods = t.GetMethods()\n                .Where(m => !m.IsSpecialName)\n                .Select(p => new MethodNameParams(p));\n            return JsonConvert.SerializeObject(methods, Formatting.Indented);\n        }\n\n        /// <summary>\n        /// Gets the API methods on a device\n        /// </summary>\n        /// <param name=\"deviceObjectPath\">The path to the device object</param>\n        /// <returns>A JSON string representing the API methods of the device</returns>\n        public static string GetApiMethods(string deviceObjectPath)\n        {\n            var obj = FindObjectOnPath(deviceObjectPath);\n            if (obj == null)\n                return \"{ \\\"error\\\":\\\"No Device\\\"}\";\n\n            // Package up method names using helper objects\n            Type t = obj.GetType();\n            var methods = t.GetMethods()\n                .Where(m => !m.IsSpecialName)\n                .Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any())\n                .Select(p => new MethodNameParams(p));\n            return JsonConvert.SerializeObject(methods, Formatting.Indented);\n        }\n\n\n        /// <summary>\n        /// FindObjectOnPath method\n        /// </summary>\n        /// <param name=\"deviceObjectPath\">The path to the device object</param>\n        /// <returns>The object found at the specified path</returns>\n        public static object FindObjectOnPath(string deviceObjectPath)\n        {\n            var path = deviceObjectPath.Split('.');\n\n            var dev = DeviceManager.GetDeviceForKey(path[0]);\n            if (dev == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Device {0} not found\", path[0]);\n                return null;\n            }\n\n            // loop through any dotted properties\n            object obj = dev;\n            if (path.Length > 1)\n            {\n                for (int i = 1; i < path.Length; i++)\n                {\n                    var objName = path[i];\n                    string indexStr = null;\n                    var indexOpen = objName.IndexOf('[');\n                    if (indexOpen != -1)\n                    {\n                        var indexClose = objName.IndexOf(']');\n                        if (indexClose == -1)\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, dev, \"ERROR Unmatched index brackets\");\n                            return null;\n                        }\n                        // Get the index and strip quotes if any\n                        indexStr = objName.Substring(indexOpen + 1, indexClose - indexOpen - 1).Replace(\"\\\"\", \"\");\n                        objName = objName.Substring(0, indexOpen);\n                        Debug.LogMessage(LogEventLevel.Information, dev, \"  Checking for collection '{0}', index '{1}'\", objName, indexStr);\n                    }\n\n                    Type oType = obj.GetType();\n                    var prop = oType.GetProperty(objName);\n                    if (prop == null)\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, dev, \"Property {0} not found on {1}\", objName, path[i - 1]);\n                        return null;\n                    }\n                    // if there's an index, try to get the property\n                    if (indexStr != null)\n                    {\n                        if (!typeof(ICollection).IsAssignableFrom(prop.PropertyType))\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, dev, \"Property {0} is not collection\", objName);\n                            return null;\n                        }\n                        var collection = prop.GetValue(obj, null) as ICollection;\n                        // Get the indexed items \"property\"\n                        var indexedPropInfo = prop.PropertyType.GetProperty(\"Item\");\n                        // These are the parameters for the indexing. Only care about one\n                        var indexParams = indexedPropInfo.GetIndexParameters();\n                        if (indexParams.Length > 0)\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, \"  Indexed, param type: {0}\", indexParams[0].ParameterType.Name);\n                            var properParam = Convert.ChangeType(indexStr, indexParams[0].ParameterType,\n                                System.Globalization.CultureInfo.InvariantCulture);\n                            try\n                            {\n                                obj = indexedPropInfo.GetValue(collection, new object[] { properParam });\n                            }\n                            // if the index is bad, catch it here.\n                            catch (TargetInvocationException e)\n                            {\n                                if (e.InnerException is ArgumentOutOfRangeException)\n                                    Debug.LogMessage(LogEventLevel.Information, \"  Index Out of range\");\n                                else if (e.InnerException is KeyNotFoundException)\n                                    Debug.LogMessage(LogEventLevel.Information, \"  Key not found\");\n                                return null;\n                            }\n                        }\n\n                    }\n                    else\n                        obj = prop.GetValue(obj, null);\n                }\n            }\n            return obj;\n        }\n\n        /// <summary>\n        /// Sets a property on an object.\n        /// </summary>\n        /// <param name=\"deviceObjectPath\">The path to the device object</param>\n        /// <returns>A JSON string representing the result of setting the property</returns>\n        public static string SetProperty(string deviceObjectPath)\n        {\n            throw new NotImplementedException(\"This could be really useful. Finish it please\");\n\n            //var obj = FindObjectOnPath(deviceObjectPath);\n            //if (obj == null)\n            //    return \"{\\\"error\\\":\\\"No object found\\\"}\";\n\n            //Type t = obj.GetType();\n\n\n            //// get the properties and set them into a new collection of NameType wrappers\n            //var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));\n            //return JsonConvert.SerializeObject(props, Formatting.Indented);\n        }\n\n\n    }\n\n    /// <summary>\n    /// Represents a DeviceActionWrapper\n    /// </summary>\n    public class DeviceActionWrapper\n    {\n        /// <summary>\n        /// Gets or sets the DeviceKey\n        /// </summary>\n        public string DeviceKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the MethodName\n        /// </summary>\n        public string MethodName { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the Params\n        /// </summary>\n        public object[] Params { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a PropertyNameType\n    /// </summary>\n    public class PropertyNameType\n    {\n        private object Parent;\n\n        /// <summary>\n        /// Gets or sets the PropInfo\n        /// </summary>\n        [JsonIgnore]\n        public PropertyInfo PropInfo { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get { return PropInfo.Name; } }\n\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public string Type { get { return PropInfo.PropertyType.Name; } }\n\n        /// <summary>\n        /// Gets or sets the Value\n        /// </summary>\n        public string Value\n        {\n            get\n            {\n                if (PropInfo.CanRead)\n                {\n                    try\n                    {\n                        return PropInfo.GetValue(Parent, null).ToString();\n                    }\n                    catch (Exception)\n                    {\n                        return null;\n                    }\n                }\n                else\n                    return null;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the CanRead\n        /// </summary>\n        public bool CanRead { get { return PropInfo.CanRead; } }\n\n        /// <summary>\n        /// Gets or sets the CanWrite\n        /// </summary>\n        public bool CanWrite { get { return PropInfo.CanWrite; } }\n\n        /// <summary>\n        /// PropertyNameType constructor\n        /// </summary>\n        /// <param name=\"info\">property info</param>\n        /// <param name=\"parent\">parent object</param>\n        public PropertyNameType(PropertyInfo info, object parent)\n        {\n            PropInfo = info;\n            Parent = parent;\n        }\n    }\n\n    /// <summary>\n    /// Represents a MethodNameParams\n    /// </summary>\n    public class MethodNameParams\n    {\n        /// <summary>\n        /// Gets or sets the MethodInfo\n        /// </summary>\n        [JsonIgnore]\n        public MethodInfo MethodInfo { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get { return MethodInfo.Name; } }\n\n        /// <summary>\n        /// Gets or sets the Params\n        /// </summary>\n        public IEnumerable<NameType> Params\n        {\n            get\n            {\n                return MethodInfo.GetParameters().Select(p =>\n                    new NameType { Name = p.Name, Type = p.ParameterType.Name });\n            }\n        }\n\n        /// <summary>\n        /// MethodNameParams constructor\n        /// </summary>\n        /// <param name=\"info\">method info</param>\n        public MethodNameParams(MethodInfo info)\n        {\n            MethodInfo = info;\n        }\n    }\n\n    /// <summary>\n    /// Represents a NameType\n    /// </summary>\n    public class NameType\n    {\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; set; }\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public string Type { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a ApiAttribute\n    /// </summary>\n    [AttributeUsage(AttributeTargets.All)]\n    public class ApiAttribute : Attribute\n    {\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/DeviceManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Manages the devices in the system\n    /// </summary>\n    public static class DeviceManager\n    {\n        /// <summary>\n        /// Raised when all devices have been activated\n        /// </summary>\n        public static event EventHandler<EventArgs> AllDevicesActivated;\n\n        /// <summary>\n        /// Raised when all devices have been registered\n        /// </summary>\n        public static event EventHandler<EventArgs> AllDevicesRegistered;\n\n        /// <summary>\n        /// Raised when all devices have been initialized\n        /// </summary>\n        public static event EventHandler<EventArgs> AllDevicesInitialized;\n\n        private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection();\n\n        private static readonly CEvent AllowAddDevicesCEvent = new CEvent(false, true);\n\n        private static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);\n\n        /// <summary>\n        /// Gets or sets the AllDevices\n        /// </summary>\n        public static List<IKeyed> AllDevices { get { return new List<IKeyed>(Devices.Values); } }\n\n        /// <summary>\n        /// Gets or sets the AddDeviceEnabled\n        /// </summary>\n        public static bool AddDeviceEnabled;\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public static void Initialize(CrestronControlSystem cs)\n        {\n            AddDeviceEnabled = true;\n            CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, \"devcommstatus\", \"Lists the communication status of all devices\",\n                ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, \"devfb\", \"Lists current feedbacks\",\n                ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(ListDevices, \"devlist\", \"Lists current managed devices\",\n                ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, \"devjson\", \"\",\n                ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s).Replace(Environment.NewLine, \"\\r\\n\")), \"devprops\", \"\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s).Replace(Environment.NewLine, \"\\r\\n\")), \"devmethods\", \"\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s).Replace(Environment.NewLine, \"\\r\\n\")), \"apimethods\", \"\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, \"devsimreceive\",\n                \"Simulates incoming data on a com device\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(SetDeviceStreamDebugging, \"setdevicestreamdebug\", \"set comm debug [deviceKey] [off/rx/tx/both] ([minutes])\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(s => DisableAllDeviceStreamDebugging(), \"disableallstreamdebug\", \"disables stream debugging on all devices\", ConsoleAccessLevelEnum.AccessOperator);\n        }\n\n        /// <summary>\n        /// ActivateAll method\n        /// </summary>\n        public static void ActivateAll()\n        {\n            try\n            {\n                OnAllDevicesRegistered();\n\n                DeviceCriticalSection.Enter();\n                AddDeviceEnabled = false;\n                // PreActivate all devices\n                Debug.LogMessage(LogEventLevel.Information, \"****PreActivation starting...****\");\n                foreach (var d in Devices.Values)\n                {\n                    try\n                    {\n                        if (d is Device)\n                            (d as Device).PreActivate();\n                    }\n                    catch (Exception e)\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, d, \"ERROR: Device {1} PreActivation failure: {0}\", e.Message, d.Key);\n                        Debug.LogMessage(LogEventLevel.Debug, d, \"Stack Trace: {0}\", e.StackTrace);\n                    }\n                }\n                Debug.LogMessage(LogEventLevel.Information, \"****PreActivation complete****\");\n                Debug.LogMessage(LogEventLevel.Information, \"****Activation starting...****\");\n\n                // Activate all devices\n                foreach (var d in Devices.Values)\n                {\n                    try\n                    {\n                        if (d is Device)\n                            (d as Device).Activate();\n                    }\n                    catch (Exception e)\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, d, \"ERROR: Device {1} Activation failure: {0}\", e.Message, d.Key);\n                        Debug.LogMessage(LogEventLevel.Debug, d, \"Stack Trace: {0}\", e.StackTrace);\n                    }\n                }\n\n                Debug.LogMessage(LogEventLevel.Information, \"****Activation complete****\");\n                Debug.LogMessage(LogEventLevel.Information, \"****PostActivation starting...****\");\n\n                // PostActivate all devices\n                foreach (var d in Devices.Values)\n                {\n                    try\n                    {\n                        if (d is Device)\n                            (d as Device).PostActivate();\n                    }\n                    catch (Exception e)\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, d, \"ERROR: Device {1} PostActivation failure: {0}\", e.Message, d.Key);\n                        Debug.LogMessage(LogEventLevel.Debug, d, \"Stack Trace: {0}\", e.StackTrace);\n                    }\n                }\n\n                Debug.LogMessage(LogEventLevel.Information, \"****PostActivation complete****\");\n\n                OnAllDevicesActivated();\n            }\n            finally\n            {\n                DeviceCriticalSection.Leave();\n            }\n        }\n\n        private static void DeviceManager_Initialized(object sender, EventArgs e)\n        {\n            var allInitialized = Devices.Values.OfType<EssentialsDevice>().All(d => d.IsInitialized);\n\n            if (allInitialized)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"****All Devices Initalized****\");\n\n                OnAllDevicesInitialized();\n            }\n        }\n\n        private static void OnAllDevicesActivated()\n        {\n            var handler = AllDevicesActivated;\n            if (handler != null)\n            {\n                handler(null, new EventArgs());\n            }\n        }\n\n        private static void OnAllDevicesRegistered()\n        {\n            var handler = AllDevicesRegistered;\n            if (handler != null)\n            {\n                handler(null, new EventArgs());\n            }\n        }\n\n        private static void OnAllDevicesInitialized()\n        {\n            var handler = AllDevicesInitialized;\n            if (handler != null)\n            {\n                handler(null, new EventArgs());\n            }\n        }\n\n        /// <summary>\n        /// DeactivateAll method\n        /// </summary>\n        public static void DeactivateAll()\n        {\n            try\n            {\n                DeviceCriticalSection.Enter();\n                foreach (var d in Devices.Values.OfType<Device>())\n                {\n                    d.Deactivate();\n                }\n            }\n            finally\n            {\n                DeviceCriticalSection.Leave();\n            }\n        }\n\n        private static void ListDevices(string s)\n        {\n            CrestronConsole.ConsoleCommandResponse($\"{Devices.Count} Devices registered with Device Manager:\\r\\n\");\n\n            var sorted = Devices.Values.ToList();\n            sorted.Sort((a, b) => a.Key.CompareTo(b.Key));\n\n            foreach (var d in sorted)\n            {\n                var name = d is IKeyName ? (d as IKeyName).Name : \"---\";\n                CrestronConsole.ConsoleCommandResponse($\"  [{d.Key}] {name}\\r\\n\");\n            }\n        }\n\n        private static void ListDeviceFeedbacks(string devKey)\n        {\n            var dev = GetDeviceForKey(devKey);\n            if (dev == null)\n            {\n                CrestronConsole.ConsoleCommandResponse($\"Device '{devKey}' not found\\r\\n\");\n                return;\n            }\n            if (!(dev is IHasFeedback statusDev))\n            {\n                CrestronConsole.ConsoleCommandResponse($\"Device '{devKey}' does not have visible feedbacks\\r\\n\");\n                return;\n            }\n            statusDev.DumpFeedbacksToConsole(true);\n        }\n\n        private static void ListDeviceCommStatuses(string input)\n        {\n\n            foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>())\n            {\n                CrestronConsole.ConsoleCommandResponse($\"{dev}: {dev.CommunicationMonitor.Status}\\r\\n\");\n            }\n        }\n\n        /// <summary>\n        /// AddDevice method\n        /// </summary>\n        public static void AddDevice(IKeyed newDev)\n        {\n            try\n            {\n                if (!DeviceCriticalSection.TryEnter())\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Currently unable to add devices to Device Manager. Please try again\");\n                    return;\n                }\n                // Check for device with same key\n                //var existingDevice = _Devices.FirstOrDefault(d => d.Key.Equals(newDev.Key, StringComparison.OrdinalIgnoreCase));\n                ////// If it exists, remove or warn??\n                //if (existingDevice != null)\n\n                if (!AddDeviceEnabled)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"All devices have been activated. Adding new devices is not allowed.\");\n                    return;\n                }\n\n                if (Devices.ContainsKey(newDev.Key))\n                {\n                    Debug.LogMessage(LogEventLevel.Information, newDev, \"WARNING: A device with this key already exists.  Not added to manager\");\n                    return;\n                }\n                Devices.Add(newDev.Key, newDev);\n                //if (!(_Devices.Contains(newDev)))\n                //    _Devices.Add(newDev);\n\n                if (newDev is EssentialsDevice essentialsDev)\n                    essentialsDev.Initialized += DeviceManager_Initialized;\n            }\n            finally\n            {\n                DeviceCriticalSection.Leave();\n            }\n        }\n\n        /// <summary>\n        /// AddDevice method\n        /// </summary>\n        public static void AddDevice(IEnumerable<IKeyed> devicesToAdd)\n        {\n            try\n            {\n                if (!DeviceCriticalSection.TryEnter())\n                {\n                    Debug.LogMessage(LogEventLevel.Information,\n                        \"Currently unable to add devices to Device Manager. Please try again\");\n                    return;\n                }\n                if (!AddDeviceEnabled)\n                {\n                    Debug.LogMessage(LogEventLevel.Information,\n                        \"All devices have been activated. Adding new devices is not allowed.\");\n                    return;\n                }\n\n                foreach (var dev in devicesToAdd)\n                {\n                    try\n                    {\n                        Devices.Add(dev.Key, dev);\n                    }\n                    catch (ArgumentException ex)\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, \"Error adding device with key {0} to Device Manager: {1}\\r\\nStack Trace: {2}\",\n                            dev.Key, ex.Message, ex.StackTrace);\n                    }\n                }\n            }\n            finally\n            {\n                DeviceCriticalSection.Leave();\n            }\n        }\n\n        /// <summary>\n        /// RemoveDevice method\n        /// </summary>\n        public static void RemoveDevice(IKeyed newDev)\n        {\n            try\n            {\n                DeviceCriticalSection.Enter();\n                if (newDev == null)\n                    return;\n                if (Devices.ContainsKey(newDev.Key))\n                    Devices.Remove(newDev.Key);\n                //if (_Devices.Contains(newDev))\n                //    _Devices.Remove(newDev);\n                else\n                    Debug.LogMessage(LogEventLevel.Information, \"Device manager: Device '{0}' does not exist in manager.  Cannot remove\", newDev.Key);\n            }\n            finally\n            {\n                DeviceCriticalSection.Leave();\n            }\n        }\n\n        /// <summary>\n        /// GetDeviceKeys method\n        /// </summary>\n        public static IEnumerable<string> GetDeviceKeys()\n        {\n            //return _Devices.Select(d => d.Key).ToList();\n            return Devices.Keys;\n        }\n\n        /// <summary>\n        /// GetDevices method\n        /// </summary>\n        public static IEnumerable<IKeyed> GetDevices()\n        {\n            //return _Devices.Select(d => d.Key).ToList();\n            return Devices.Values;\n        }\n\n        /// <summary>\n        /// GetDeviceForKey method\n        /// </summary>\n        public static IKeyed GetDeviceForKey(string key)\n        {\n            //return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));\n            if (key != null && Devices.ContainsKey(key))\n                return Devices[key];\n\n            return null;\n        }\n\n        /// <summary>\n        /// GetDeviceForKey method\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        public static T GetDeviceForKey<T>(string key)\n        {\n            //return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));\n            if (key == null || !Devices.ContainsKey(key))\n                return default;\n\n            if (!(Devices[key] is T))\n            {\n                Debug.LogMessage(LogEventLevel.Error, \"Device with key '{0}' is not of type '{1}'\", key, typeof(T).Name);\n                return default;\n            }\n\n            return (T)Devices[key];\n        }\n\n        /// <summary>\n        /// Console handler that simulates com port data receive \n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <summary>\n        /// SimulateComReceiveOnDevice method\n        /// </summary>\n        public static void SimulateComReceiveOnDevice(string s)\n        {\n            // devcomsim:1 xyzabc\n            var match = Regex.Match(s, @\"(\\S*)\\s*(.*)\");\n            if (match.Groups.Count < 3)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"  Format: devsimreceive:P <device key> <string to send>\");\n                return;\n            }\n            //Debug.LogMessage(LogEventLevel.Verbose, \"**** {0} - {1} ****\", match.Groups[1].Value, match.Groups[2].Value);\n\n            if (!(GetDeviceForKey(match.Groups[1].Value) is ComPortController com))\n            {\n                CrestronConsole.ConsoleCommandResponse(\"'{0}' is not a comm port device\", match.Groups[1].Value);\n                return;\n            }\n            com.SimulateReceive(match.Groups[2].Value);\n        }\n\n        /// <summary>\n        /// Prints a list of routing inputs and outputs by device key.\n        /// </summary>\n        /// <param name=\"s\">Device key from which to report data</param>\n        /// <summary>\n        /// GetRoutingPorts method\n        /// </summary>\n        public static void GetRoutingPorts(string s)\n        {\n            var device = GetDeviceForKey(s);\n\n            if (device == null) return;\n            var inputPorts = ((device as IRoutingInputs) != null) ? (device as IRoutingInputs).InputPorts : null;\n            var outputPorts = ((device as IRoutingOutputs) != null) ? (device as IRoutingOutputs).OutputPorts : null;\n            if (inputPorts != null)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Device {0} has {1} Input Ports:{2}\", s, inputPorts.Count, CrestronEnvironment.NewLine);\n                foreach (var routingInputPort in inputPorts)\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"key: {0} signalType: {1}{2}\", routingInputPort.Key, routingInputPort.Type, CrestronEnvironment.NewLine);\n                }\n            }\n            if (outputPorts == null) return;\n            CrestronConsole.ConsoleCommandResponse(\"Device {0} has {1} Output Ports:{2}\", s, outputPorts.Count, CrestronEnvironment.NewLine);\n            foreach (var routingOutputPort in outputPorts)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"key: {0} signalType: {1}{2}\", routingOutputPort.Key, routingOutputPort.Type, CrestronEnvironment.NewLine);\n            }\n        }\n\n        /// <summary>\n        /// Attempts to set the debug level of a device\n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <summary>\n        /// SetDeviceStreamDebugging method\n        /// </summary>\n        public static void SetDeviceStreamDebugging(string s)\n        {\n            if (String.IsNullOrEmpty(s) || s.Contains(\"?\"))\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"SETDEVICESTREAMDEBUG [{deviceKey}] [OFF |TX | RX | BOTH] [timeOutInMinutes]\\r\\n\" +\n                    \"    {deviceKey} [OFF | TX | RX | BOTH] - Device to set stream debugging on, and which setting to use\\r\\n\" +\n                    \"    timeOutInMinutes - Set timeout for stream debugging. Default is 30 minutes\");\n                return;\n            }\n\n            var args = s.Split(' ');\n\n            var deviceKey = args[0];\n            var setting = args[1];\n\n            var timeout = String.Empty;\n\n            if (args.Length >= 3)\n            {\n                timeout = args[2];\n            }\n\n\n            if (!(GetDeviceForKey(deviceKey) is IStreamDebugging device))\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Unable to get device with key: {0}\", deviceKey);\n                return;\n            }\n\n            eStreamDebuggingSetting debugSetting;\n\n            try\n            {\n                debugSetting = (eStreamDebuggingSetting)Enum.Parse(typeof(eStreamDebuggingSetting), setting, true);\n            }\n            catch\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Unable to convert setting value.  Please use off/rx/tx/both\");\n                return;\n            }\n\n            if (!string.IsNullOrEmpty(timeout))\n            {\n                try\n                {\n                    var min = Convert.ToUInt32(timeout);\n\n                    device.StreamDebugging.SetDebuggingWithSpecificTimeout(debugSetting, min);\n                    CrestronConsole.ConsoleCommandResponse(\"Device: '{0}' debug level set to {1} for {2} minutes\", deviceKey, debugSetting, min);\n\n                }\n                catch (Exception e)\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"Unable to convert minutes or settings value.  Please use an integer value for minutes. Error: {0}\", e);\n                }\n            }\n            else\n            {\n                device.StreamDebugging.SetDebuggingWithDefaultTimeout(debugSetting);\n                CrestronConsole.ConsoleCommandResponse(\"Device: '{0}' debug level set to {1} for default time (30 minutes)\", deviceKey, debugSetting);\n            }\n        }\n\n        /// <summary>\n        /// DisableAllDeviceStreamDebugging method\n        /// </summary>\n        public static void DisableAllDeviceStreamDebugging()\n        {\n            foreach (var device in AllDevices)\n            {\n                if (device is IStreamDebugging streamDevice)\n                {\n                    streamDevice.StreamDebugging.SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting.Off);\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/DisplayUiConstants.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Integers that represent the \"source type number\" for given sources.\n\t/// Primarily used by the UI to calculate subpage join offsets\n\t/// Note, for UI, only values 1-49 are valid.\n\t/// </summary>\n\tpublic class DisplayUiConstants\n\t{\n\t\t/// <summary>\n\t\t/// TypeRadio constant\n\t\t/// </summary>\n\t\tpublic const uint TypeRadio = 1;\n\n\t\t/// <summary>\n\t\t/// TypeTv constant\n\t\t/// </summary>\n\t\tpublic const uint TypeDirecTv = 9;\n\n\t\t/// <summary>\n\t\t/// TypeBluray constant\n\t\t/// </summary>\n\t\tpublic const uint TypeBluray = 13;\n\n\t\t/// <summary>\n\t\t/// TypeStreamingDevice constant\n\t\t/// </summary>\n\t\tpublic const uint TypeChromeTv = 15;\n\n\t\t/// <summary>\n\t\t/// TypeStreamingDevice constant\n\t\t/// </summary>\n\t\tpublic const uint TypeFireTv = 16;\n\n\t\t/// <summary>\n\t\t/// TypeStreamingDevice constant\n\t\t/// </summary>\n\t\tpublic const uint TypeAppleTv = 17;\n\n\t\t/// <summary>\n\t\t/// TypeStreamingDevice constant\n\t\t/// </summary>\n\t\tpublic const uint TypeRoku = 18;\n\n\t\t/// <summary>\n\t\t/// TypeLaptop constant\n\t\t/// </summary>\n\t\tpublic const uint TypeLaptop = 31;\n\n\t\t/// <summary>\n\t\t/// TypePc constant\n\t\t/// </summary>\n\t\tpublic const uint TypePc = 32;\n\n\t\t/// <summary>\n\t\t/// TypeNoControls constant\n\t\t/// </summary>\n\t\tpublic const uint TypeNoControls = 49;\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/EssentialsBridgeableDevice.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Essentials.Core.Bridges;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Base class for devices that can be bridged to an EISC API.\n    /// </summary>\n    public abstract class EssentialsBridgeableDevice : EssentialsDevice, IBridgeAdvanced\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"EssentialsBridgeableDevice\"/> class with the specified key.\n        /// </summary>\n        /// <param name=\"key\">The unique key for the device.</param>\n        protected EssentialsBridgeableDevice(string key) : base(key)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"EssentialsBridgeableDevice\"/> class with the specified key and name.\n        /// </summary>\n        /// <param name=\"key\">The unique key for the device.</param>\n        /// <param name=\"name\">The display name for the device.</param>\n        protected EssentialsBridgeableDevice(string key, string name) : base(key, name)\n        {\n        }\n\n        /// <inheritdoc />\n        public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the basic needs for an EssentialsDevice to enable it to be build by an IDeviceFactory class\n    /// </summary>\n    [Description(\"The base Essentials Device Class\")]\n    public abstract class EssentialsDevice : Device\n    {\n        /// <summary>\n        /// Event raised when the device is initialized.\n        /// </summary>\n        public event EventHandler Initialized;\n\n        private bool _isInitialized;\n\n        /// <summary>\n        /// Gets a value indicating whether the device is initialized.\n        /// </summary>\n        public bool IsInitialized\n        {\n            get { return _isInitialized; }\n            private set\n            {\n                if (_isInitialized == value) return;\n\n                _isInitialized = value;\n\n                if (_isInitialized)\n                {\n                    Initialized?.Invoke(this, new EventArgs());\n                }\n            }\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the EssentialsDevice class.\n        /// </summary>\n        /// <param name=\"key\">The unique identifier for the device.</param>\n        protected EssentialsDevice(string key)\n            : base(key)\n        {\n            SubscribeToActivateComplete();\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the EssentialsDevice class.\n        /// </summary>\n        /// <param name=\"key\">The unique identifier for the device.</param>\n        /// <param name=\"name\">The name of the device.</param>\n        protected EssentialsDevice(string key, string name)\n            : base(key, name)\n        {\n            SubscribeToActivateComplete();\n        }\n\n        private void SubscribeToActivateComplete()\n        {\n            DeviceManager.AllDevicesActivated += DeviceManagerOnAllDevicesActivated;\n        }\n\n        private void DeviceManagerOnAllDevicesActivated(object sender, EventArgs eventArgs)\n        {\n            Task.Run(() =>\n            {\n                try\n                {\n                    Initialize();\n\n                    IsInitialized = true;\n                }\n                catch (Exception ex)\n                {\n                    Debug.LogMessage(LogEventLevel.Error, this, \"Exception initializing device: {0}\", ex.Message);\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Stack Trace: {0}\", ex.StackTrace);\n                }\n            });\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            CreateMobileControlMessengers();\n\n            return base.CustomActivate();\n        }\n\n        /// <summary>\n        /// Override this method to build and create custom Mobile Control Messengers during the Activation phase\n        /// </summary>\n        protected virtual void CreateMobileControlMessengers()\n        {\n\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/EssentialsDeviceFactory.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing PepperDash.Essentials.Core.Config;\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Provides the basic needs for a Device Factory\n  /// </summary>\n  public abstract class EssentialsDeviceFactory<T> : IDeviceFactory where T : EssentialsDevice\n  {\n    /// <inheritdoc />\n    public Type FactoryType => typeof(T);\n\n    /// <summary>\n    /// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device\n    /// </summary>\n    public List<string> TypeNames { get; protected set; }\n\n    /// <summary>\n    /// Build the device using the configuration\n    /// </summary>\n    /// <param name=\"dc\">The device config</param>\n    /// <returns>An instance of the device</returns>\n    public abstract EssentialsDevice BuildDevice(DeviceConfig dc);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/EssentialsPluginDevelopmentDeviceFactory.cs",
    "content": "using System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// EssentialsPluginDevelopmentDeviceFactory class\n  /// </summary>\n  /// <typeparam name=\"T\"></typeparam>\n  public abstract class EssentialsPluginDevelopmentDeviceFactory<T> : EssentialsDeviceFactory<T>, IPluginDevelopmentDeviceFactory where T : EssentialsDevice\n  {\n    /// <summary>\n    /// Specifies the minimum version of Essentials required for a plugin to run.  Must use the format Major.Minor.Build (ex. \"1.4.33\")\n    /// </summary>\n    public string MinimumEssentialsFrameworkVersion { get; protected set; }\n\n    /// <summary>\n    /// Gets or sets the DevelopmentEssentialsFrameworkVersions\n    /// </summary>\n    public List<string> DevelopmentEssentialsFrameworkVersions { get; protected set; }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/EssentialsPluginDeviceFactory.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Devices the basic needs for a Device Factory\n  /// </summary>\n  public abstract class EssentialsPluginDeviceFactory<T> : EssentialsDeviceFactory<T>, IPluginDeviceFactory where T : EssentialsDevice\n  {\n    /// <summary>\n    /// Specifies the minimum version of Essentials required for a plugin to run.  Must use the format Major.Minor.Build (ex. \"1.4.33\")\n    /// </summary>\n    public string MinimumEssentialsFrameworkVersion { get; protected set; }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/GenericIRController.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Bridges.JoinMaps;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Devices\n{\n    /// <summary>\n    /// Represents a GenericIrController\n    /// </summary>\n    public class GenericIrController: EssentialsBridgeableDevice\n    {\n        //data storage for bridging\n        private BasicTriList _trilist;\n        private uint _joinStart;\n        private string _joinMapKey;\n        private EiscApiAdvanced _bridge;\n\n        private readonly IrOutputPortController _port; \n\n        /// <summary>\n        /// Gets or sets the IrCommands\n        /// </summary>\n        public string[] IrCommands {get { return _port.IrFileCommands; }}\t    \n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key for the device</param>\n        /// <param name=\"name\">name of the device</param>\n        /// <param name=\"irPort\">IR output port controller</param>\n        public GenericIrController(string key, string name, IrOutputPortController irPort) : base(key, name)\n        {\n            _port = irPort;\n            if (_port == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"IR Port is null, device will not function\");\n                return;\n            }\n            DeviceManager.AddDevice(_port);\n\n            _port.DriverLoaded.OutputChange += DriverLoadedOnOutputChange;\n        }\n\n        private void DriverLoadedOnOutputChange(object sender, FeedbackEventArgs args)\n        {\n            if (!args.BoolValue)\n            {\n                return;\n            }\n\n            if (_trilist == null || _bridge == null)\n            {\n                return;\n            }\n\n            LinkToApi(_trilist, _joinStart, _joinMapKey, _bridge);\n        }\n\n        #region Overrides of EssentialsBridgeableDevice\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            //if driver isn't loaded yet, store the variables until it is loaded, then call the LinkToApi method again\n            if (!_port.DriverIsLoaded)\n            {\n                _trilist = trilist;\n                _joinStart = joinStart;\n                _joinMapKey = joinMapKey;\n                _bridge = bridge;\n                return;\n            }\n\n            var joinMap = new GenericIrControllerJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<GenericIrControllerJoinMap>(joinMapSerialized);\n\n\t        if (_port.UseBridgeJoinMap)\n\t        {\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"Using new IR bridge join map\");\n\n\t\t        var bridgeJoins = joinMap.Joins.Where((kv) => _port.IrFileCommands.Any(cmd => cmd == kv.Key)).ToDictionary(kv => kv.Key);\n\t\t        if (bridgeJoins == null)\n\t\t        {\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"Failed to link new IR bridge join map\");\n\t\t\t        return;\n\t\t        }\n\n\t\t\t\tjoinMap.Joins.Clear();\n\n\t\t        foreach (var bridgeJoin in bridgeJoins)\n\t\t        {\n\t\t\t        var key = bridgeJoin.Key;\n\t\t\t        var joinDataKey = bridgeJoin.Value.Key;\n\t\t\t        var joinDataValue = bridgeJoin.Value.Value;\n\t\t\t        var joinNumber = bridgeJoin.Value.Value.JoinNumber;\t\t\t\t\t\n\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, @\"bridgeJoin: Key-'{0}'\n                                                                    Value.Key-'{1}'\n                                                                    Value.JoinNumber-'{2}'\n                                                                    Value.Metadata.Description-'{3}'\", \n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tjoinDataKey,\n\t\t\t\t\t\tjoinNumber,\n\t\t\t\t\t\tjoinDataValue.Metadata.Description);\n\n\n\t\t\t\t\tjoinMap.Joins.Add(key, joinDataValue);\n\n\t\t\t        trilist.SetBoolSigAction(joinNumber, (b) => Press(key, b));\n\t\t        }\n\t        }\n\t        else\n\t        {\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"Using legacy IR join mapping based on available IR commands\");\n\n\t\t\t\tjoinMap.Joins.Clear();\n\n\t\t\t\tfor (uint i = 0; i < _port.IrFileCommands.Length; i++)\n\t\t\t\t{\n\t\t\t\t\tvar cmd = _port.IrFileCommands[i];\n\t\t\t\t\tvar joinData = new JoinDataComplete(new JoinData { JoinNumber = i, JoinSpan = 1 },\n\t\t\t\t\t\tnew JoinMetadata\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tDescription = cmd,\n\t\t\t\t\t\t\tJoinCapabilities = eJoinCapabilities.FromSIMPL,\n\t\t\t\t\t\t\tJoinType = eJoinType.Digital\n\t\t\t\t\t\t});\n\n\t\t\t\t\tjoinData.SetJoinOffset(joinStart);\n\n\t\t\t\t\tjoinMap.Joins.Add(cmd, joinData);\n\n\t\t\t\t\ttrilist.SetBoolSigAction(joinData.JoinNumber, (b) => Press(cmd, b));\n\t\t\t\t}   \n\t        }            \n\n            joinMap.PrintJoinMapInfo();\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n        }\n\n        #endregion\n\n        /// <summary>\n        /// Press method\n        /// </summary>\n        public void Press(string command, bool pressRelease)\n        {\n            _port.PressRelease(command, pressRelease);\n        }\n    }\n\n    /// <summary>\n    /// Represents a GenericIrControllerFactory\n    /// </summary>\n    public class GenericIrControllerFactory : EssentialsDeviceFactory<GenericIrController>\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public GenericIrControllerFactory()\n        {\n            TypeNames = new List<string> {\"genericIrController\"};\n        }\n        #region Overrides of EssentialsDeviceFactory<GenericIRController>\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Generic IR Controller Device\");\n\n            var irPort = IRPortHelper.GetIrOutputPortController(dc);\n\n            return new GenericIrController(dc.Key, dc.Name, irPort);\n        }\n\n        #endregion\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/GenericMonitoredTcpDevice.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core.Devices\n{\n\t/// <summary>\n\t/// Represents a GenericCommunicationMonitoredDevice\n\t/// </summary>\n\tpublic class GenericCommunicationMonitoredDevice : Device, ICommunicationMonitor\n\t{\n\t\tIBasicCommunication Client;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the CommunicationMonitor\n\t\t/// </summary>\n\t\tpublic StatusMonitorBase CommunicationMonitor { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\">key of the device</param>\n\t\t/// <param name=\"name\">name of the device</param>\n\t\t/// <param name=\"comm\">communication client</param>\n\t\t/// <param name=\"pollString\">poll string</param>\n\t\t/// <param name=\"pollTime\">poll time</param>\n\t\t/// <param name=\"warningTime\">warning time</param>\n\t\t/// <param name=\"errorTime\">error time</param>\n\t\tpublic GenericCommunicationMonitoredDevice(string key, string name, IBasicCommunication comm, string pollString,\n\t\t\tlong pollTime, long warningTime, long errorTime)\n\t\t\t: base(key, name)\n\t\t{\n\t\t\tClient = comm;\n\t\t\tCommunicationMonitor = new GenericCommunicationMonitor(this, Client, pollTime, warningTime, errorTime, pollString);\n\n\t\t\t// ------------------------------------------------------DELETE THIS\n\t\t\tCommunicationMonitor.StatusChange += (o, a) =>\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Communication monitor status change: {0}\", a.Status);\n\t\t\t};\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor with default times\n\t\t/// </summary>\n\t\t/// <param name=\"key\">key of the device</param>\n\t\t/// <param name=\"name\">name of the device</param>\n\t\t/// <param name=\"comm\">communication client</param>\n\t\t/// <param name=\"pollString\">poll string</param>\n\t\tpublic GenericCommunicationMonitoredDevice(string key, string name, IBasicCommunication comm, string pollString)\n\t\t\t: this(key, name, comm, pollString, 30000, 120000, 300000)\n\t\t{\n\t\t}\n\n  /// <summary>\n  /// CustomActivate method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic override bool CustomActivate()\n\t\t{\n\t\t\tCommunicationMonitor.Start();\n\t\t\treturn true;\n\t\t}\n\n  /// <summary>\n  /// Deactivate method\n  /// </summary>\n\t\tpublic override bool Deactivate()\n\t\t{\n\t\t\tCommunicationMonitor.Stop();\n\t\t\treturn true;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IAttachVideoStatus.cs",
    "content": "using PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines the contract for IAttachVideoStatus\n  /// </summary>\n  public interface IAttachVideoStatus : IKeyed\n  {\n    // Extension methods will depend on this\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IAttachVideoStatusExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// IAttachVideoStatusExtensions class\n\t/// </summary>\n\tpublic static class IAttachVideoStatusExtensions\n\t{\n\t\t/// <summary>\n\t\t/// Gets the VideoStatusOutputs for the device\n\t\t/// </summary>\n\t\t/// <param name=\"attachedDev\"></param>\n\t\t/// <returns>Attached VideoStatusOutputs or the default if none attached</returns>\n\t\tpublic static VideoStatusOutputs GetVideoStatuses(this IAttachVideoStatus attachedDev)\n\t\t{\n\t\t\t// See if this device is connected to a status-providing port\n\t\t\tvar tl = TieLineCollection.Default.FirstOrDefault(t =>\n\t\t\t\tt.SourcePort.ParentDevice == attachedDev\n\t\t\t\t&& t.DestinationPort is RoutingInputPortWithVideoStatuses);\n\t\t\tif (tl != null)\n\t\t\t{\n\t\t\t\t// if so, and it's got status, return it -- or null\n\t\t\t\tvar port = tl.DestinationPort as RoutingInputPortWithVideoStatuses;\n\t\t\t\tif (port != null)\n\t\t\t\t\treturn port.VideoStatus;\n\t\t\t}\n\t\t\treturn VideoStatusOutputs.NoStatus;\n\t\t}\n\n  /// <summary>\n  /// HasVideoStatuses method\n  /// </summary>\n\t\tpublic static bool HasVideoStatuses(this IAttachVideoStatus attachedDev)\n\t\t{\n\t\t\treturn TieLineCollection.Default.FirstOrDefault(t =>\n\t\t\t\tt.SourcePort.ParentDevice == attachedDev\n\t\t\t\t&& t.DestinationPort is RoutingInputPortWithVideoStatuses) != null;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IDisplayUsage.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// For display classes that can provide usage data\n  /// </summary>\n  public interface IDisplayUsage\n  {\n    /// <summary>\n    /// Gets the LampHours\n    /// </summary>\n    IntFeedback LampHours { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IDspPresets.cs",
    "content": "﻿using PepperDash.Core;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IDspPresets\n    /// </summary>\n    public interface IDspPresets\n    {\n        /// <summary>\n        /// Gets the Presets\n        /// </summary>\n        Dictionary<string, IKeyName> Presets { get; }\n\n        /// <summary>\n        /// Recalls the preset by key\n        /// </summary>\n        /// <param name=\"key\">key of preset to recall</param>\n        void RecallPreset(string key);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IHasFeedbacks.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Defines the contract for IHasFeedback\n\t/// </summary>\n\tpublic interface IHasFeedback : IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// This method returns a list of all Output objects on a device,\n\t\t/// including all \"aggregate\" devices.\n\t\t/// </summary>\n\t\tFeedbackCollection<Feedback> Feedbacks { get; }\n\n\t}\n\n\t/// <summary>\n\t/// Extension methods for IHasFeedback\n\t/// </summary>\n\tpublic static class IHasFeedbackExtensions\n\t{\n\t\t/// <summary>\n\t\t/// Gets the feedback type name for sorting purposes\n\t\t/// </summary>\n\t\t/// <param name=\"feedback\">The feedback to get the type name for</param>\n\t\t/// <returns>A string representing the feedback type</returns>\n\t\tprivate static string GetFeedbackTypeName(Feedback feedback)\n\t\t{\n\t\t\tif (feedback is BoolFeedback)\n\t\t\t\treturn \"boolean\";\n\t\t\telse if (feedback is IntFeedback)\n\t\t\t\treturn \"integer\";\n\t\t\telse if (feedback is StringFeedback)\n\t\t\t\treturn \"string\";\n\t\t\telse\n\t\t\t\treturn feedback.GetType().Name;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Dumps the feedbacks to the console\n\t\t/// </summary>\n\t\t/// <param name=\"source\"></param>\n\t\t/// <param name=\"getCurrentStates\"></param>\n\t\tpublic static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates)\n\t\t{\n\t\t\tvar feedbacks = source.Feedbacks;\n\n\t\t\tif (feedbacks == null || feedbacks.Count == 0)\n\t\t\t{\n\t\t\t\tCrestronConsole.ConsoleCommandResponse(\"No available feedbacks\\r\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tCrestronConsole.ConsoleCommandResponse(\"Available feedbacks:\\r\\n\");\n\n\t\t\t// Sort feedbacks by type first, then by key\n\t\t\tvar sortedFeedbacks = feedbacks.OrderBy(f => GetFeedbackTypeName(f)).ThenBy(f => string.IsNullOrEmpty(f.Key) ? \"\" : f.Key);\n\n\t\t\tforeach (var feedback in sortedFeedbacks)\n\t\t\t{\n\t\t\t\tstring value = \"\";\n\t\t\t\tstring type = \"\";\n\t\t\t\tif (getCurrentStates)\n\t\t\t\t{\n\t\t\t\t\tif (feedback is BoolFeedback)\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue = feedback.BoolValue.ToString();\n\t\t\t\t\t\ttype = \"boolean\";\n\t\t\t\t\t}\n\t\t\t\t\telse if (feedback is IntFeedback)\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue = feedback.IntValue.ToString();\n\t\t\t\t\t\ttype = \"integer\";\n\t\t\t\t\t}\n\t\t\t\t\telse if (feedback is StringFeedback)\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue = feedback.StringValue;\n\t\t\t\t\t\ttype = \"string\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tCrestronConsole.ConsoleCommandResponse($\"  {type,-12} {(string.IsNullOrEmpty(feedback.Key) ? \"-no key-\" : feedback.Key),-25} {value}\\r\\n\");\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IMakeModel.cs",
    "content": "﻿using PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\n\t/// <summary>\n\t/// Defines the contract for device make and model information\n\t/// </summary>\n\tpublic interface IMakeModel : IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// Gets the make of the device\n\t\t/// </summary>\n\t\tstring DeviceMake { get; }\n\n\t\t/// <summary>\n\t\t/// Gets the model of the device\n\t\t/// </summary>\n\t\tstring DeviceModel { get; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IOnline.cs",
    "content": "namespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Defines the contract for IOnline\n  /// </summary>\n  public interface IOnline\n  {\n    /// <summary>\n    /// Gets a value indicating whether the device is online.\n    /// </summary>\n    BoolFeedback IsOnline { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IProjectorInterfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IBasicVideoMute\n    /// </summary>\n    public interface IBasicVideoMute\n    {\n        /// <summary>\n        /// Toggles the video mute\n        /// </summary>\n        void VideoMuteToggle();\n    }\n\n    /// <summary>\n    /// Defines the contract for IBasicVideoMuteWithFeedback\n    /// </summary>\n    public interface IBasicVideoMuteWithFeedback : IBasicVideoMute\n    {\n        /// <summary>\n        /// Gets the VideoMuteIsOn feedback\n        /// </summary>\n        BoolFeedback VideoMuteIsOn { get; }\n\n        /// <summary>\n        /// Sets the video mute on\n        /// </summary>\n        void VideoMuteOn();\n\n        /// <summary>\n        /// Sets the video mute off\n        /// </summary>\n        void VideoMuteOff();\n \n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IReconfigurableDevice.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Essentials.Core.Config;\n\n\nnamespace PepperDash.Essentials.Core.Devices\n{\n    /// <summary>\n    /// Defines the contract for IReconfigurableDevice\n    /// </summary>\n    public interface IReconfigurableDevice\n    {\n        /// <summary>\n        /// Event fired when the configuration changes\n        /// </summary>\n        event EventHandler<EventArgs> ConfigChanged;\n\n        /// <summary>\n        /// Gets the current DeviceConfig\n        /// </summary>\n        DeviceConfig Config { get; }\n\n        /// <summary>\n        /// Sets the DeviceConfig\n        /// </summary>\n        /// <param name=\"config\">config to set</param>\n        void SetConfig(DeviceConfig config);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IUsageTracking.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IUsageTracking\n    /// </summary>\n    public interface IUsageTracking\n    {\n        /// <summary>\n        /// Gets or sets the UsageTracker\n        /// </summary>\n        UsageTracking UsageTracker { get; set; }\n    }\n\n    //public static class IUsageTrackingExtensions\n    //{\n    //    public static void EnableUsageTracker(this IUsageTracking device)\n    //    {\n    //        device.UsageTracker = new UsageTracking();\n    //    }\n    //}\n\n    /// <summary>\n    /// Represents a UsageTracking\n    /// </summary>\n    public class UsageTracking\n    {\n        /// <summary>\n        /// Event fired when device usage ends\n        /// </summary>\n        public event EventHandler<DeviceUsageEventArgs> DeviceUsageEnded;\n\n        /// <summary>\n        /// Gets or sets the InUseTracker\n        /// </summary>\n        public InUseTracking InUseTracker { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the UsageIsTracked\n        /// </summary>\n        public bool UsageIsTracked { get; set; }\n\n        /// <summary>\n        /// Gets or sets the UsageTrackingStarted\n        /// </summary>\n        public bool UsageTrackingStarted { get; protected set; }\n        /// <summary>\n        /// Gets or sets the UsageStartTime\n        /// </summary>\n        public DateTime UsageStartTime { get; protected set; }\n        /// <summary>\n        /// Gets or sets the UsageEndTime\n        /// </summary>\n        public DateTime UsageEndTime { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the Parent\n        /// </summary>\n        public Device Parent { get; private set; }\n\n        /// <summary>\n        /// Constructor for UsageTracking class\n        /// </summary>\n        /// <param name=\"parent\">The parent device</param>\n        public UsageTracking(Device parent)\n        {\n            Parent = parent;\n   \n            InUseTracker = new InUseTracking();\n\n            InUseTracker.InUseFeedback.OutputChange += InUseFeedback_OutputChange; //new EventHandler<EventArgs>();\n        }\n\n        void  InUseFeedback_OutputChange(object sender, EventArgs e)\n        {\n \t        if(InUseTracker.InUseFeedback.BoolValue)\n            {\n                StartDeviceUsage();\n            }\n            else\n            {\n                EndDeviceUsage();\n            }\n        }\n\n\n        /// <summary>\n        /// StartDeviceUsage method\n        /// </summary>\n        public void StartDeviceUsage()\n        {\n            UsageTrackingStarted = true;\n            UsageStartTime = DateTime.Now;\n        }\n\n        /// <summary>\n        /// Calculates the difference between the usage start and end times, gets the total minutes used and fires an event to pass that info to a consumer\n        /// </summary>\n        public void EndDeviceUsage()\n        {\n            try\n            {\n                UsageTrackingStarted = false;\n\n                UsageEndTime = DateTime.Now;\n\n                if (UsageStartTime != null)\n                {\n                    var timeUsed = UsageEndTime - UsageStartTime;\n\n                    var handler = DeviceUsageEnded;\n\n                    if (handler != null)\n                    {\n                        Debug.LogMessage(LogEventLevel.Debug, \"Device Usage Ended for: {0} at {1}.  In use for {2} minutes.\", Parent.Name, UsageEndTime, timeUsed.Minutes);\n                        handler(this, new DeviceUsageEventArgs() { UsageEndTime = UsageEndTime, MinutesUsed = timeUsed.Minutes });\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"Error ending device usage: {0}\", e);\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a DeviceUsageEventArgs\n    /// </summary>\n    public class DeviceUsageEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the UsageEndTime\n        /// </summary>\n        public DateTime UsageEndTime { get; set; }\n        /// <summary>\n        /// Gets or sets the MinutesUsed\n        /// </summary>\n        public int MinutesUsed { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/IrOutputPortController.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Core.Config;\n\n\nusing PepperDash.Core;\nusing Serilog.Events;\nusing System.IO;\nusing PepperDash.Core.Logging;\n\nnamespace PepperDash.Essentials.Core\n{\n\n\t/// <summary>\n\t/// IR port wrapper. May act standalone\n\t/// </summary>\n\tpublic class IrOutputPortController : Device\n\t{\t\n\t\tuint IrPortUid;\n\t\tIROutputPort IrPort;\n\n\t\t/// <summary>\n\t\t/// Gets the DriverLoaded feedback\n\t\t/// </summary>\n        public BoolFeedback DriverLoaded { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the StandardIrPulseTime\n\t\t/// </summary>\n\t\tpublic ushort StandardIrPulseTime { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the DriverFilepath\n\t\t/// </summary>\n\t\tpublic string DriverFilepath { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the DriverIsLoaded\n\t\t/// </summary>\n\t\tpublic bool DriverIsLoaded { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IrFileCommands\n        /// </summary>\n        public string[] IrFileCommands { get { return IrPort.AvailableStandardIRCmds(IrPortUid); } }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the UseBridgeJoinMap\n\t\t/// </summary>\n\t\tpublic bool UseBridgeJoinMap { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor for IrDevice base class.  If a null port is provided, this class will \n\t\t/// still function without trying to talk to a port.\n\t\t/// </summary>\n\t\tpublic IrOutputPortController(string key, IROutputPort port, string irDriverFilepath)\n\t\t\t: base(key)\n\t\t{\n\t\t\t//if (port == null) throw new ArgumentNullException(\"port\");\n\n\t\t    DriverLoaded = new BoolFeedback(() => DriverIsLoaded);\n\t\t\tIrPort = port;\n\t\t\tif (port == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"WARNING No valid IR Port assigned to controller. IR will not function\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tLoadDriver(irDriverFilepath);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor for IrDevice base class using post activation function to get port\n\t\t/// </summary>\n\t\t/// <param name=\"key\">key of the device</param>\n\t\t/// <param name=\"postActivationFunc\">function to call post activation</param>\n\t\t/// <param name=\"config\">config of the device</param>\n\t    public IrOutputPortController(string key, Func<DeviceConfig, IROutputPort> postActivationFunc,\n\t        DeviceConfig config)\n\t        : base(key)\n\t    {\n            DriverLoaded = new BoolFeedback(() => DriverIsLoaded);\n\t\t\tUseBridgeJoinMap = config.Properties[\"control\"].Value<bool>(\"useBridgeJoinMap\");\n            AddPostActivationAction(() =>\n            {\n\t            IrPort = postActivationFunc(config);\n\n                if (IrPort == null)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, this, \"WARNING No valid IR Port assigned to controller. IR will not function\");\n                    return;\n                }\n                \n                // var filePath = Global.FilePathPrefix + \"ir\" + Global.DirectorySeparator + config.Properties[\"control\"][\"irFile\"].Value<string>();\n\n                var fileName = config.Properties[\"control\"][\"irFile\"].Value<string>();\n\n                var files = Directory.GetFiles(Global.FilePathPrefix, fileName, SearchOption.AllDirectories);\n\n                if(files.Length == 0)\n                {\n                    this.LogError(\"IR file {fileName} not found in {path}\", fileName, Global.FilePathPrefix);\n                    return;\n                }\n\n                if(files.Length > 1)\n                {\n                    this.LogError(\"IR file {fileName} found in multiple locations: {files}\", fileName, files);\n                    return;\n                }\n\n                var filePath = files[0];\n\n                Debug.LogMessage(LogEventLevel.Debug, \"*************Attempting to load IR file: {0}***************\", filePath);\n\n                LoadDriver(filePath);\n                \n\t\t\t\tPrintAvailableCommands();\n            });\n\t    }\n\n\t\t/// <summary>\n\t\t/// PrintAvailableCommands method\n\t\t/// </summary>\n\t    public void PrintAvailableCommands()\n\t    {\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Available IR Commands in IR File {0}\", IrPortUid);\n            foreach (var cmd in IrPort.AvailableIRCmds())\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"{0}\", cmd);\n            }\n\t    }\n\t    \n\n\t    /// <summary>\n\t\t/// Loads the IR driver at path\n\t\t/// </summary>\n\t\t/// <param name=\"path\">path of the IR driver file</param>\n\t\tpublic void LoadDriver(string path)\n\t\t{\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"***Loading IR File***\");\n\t\t\tif (string.IsNullOrEmpty(path)) path = DriverFilepath;\n\t        try\n\t        {\n\t            IrPortUid = IrPort.LoadIRDriver(path);\n\t            DriverFilepath = path;\n\t            StandardIrPulseTime = 200;\n\t            DriverIsLoaded = true;\n\n                DriverLoaded.FireUpdate();\n\t        }\n\t\t\tcatch\n\t\t\t{\n\t\t\t\tDriverIsLoaded = false;\n\t\t\t\tvar message = string.Format(\"WARNING IR Driver '{0}' failed to load\", path);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, message);\n                DriverLoaded.FireUpdate();\n\t\t\t}\n\t\t}\n\n\n\t\t/// <summary>\n\t\t/// PressRelease method\n\t\t/// </summary>\n\t\t/// <param name=\"command\">IR command to send</param>\n\t\t/// <param name=\"state\">true to press, false to release</param>\n\t\t/// <inheritdoc />\n\t\tpublic virtual void PressRelease(string command, bool state)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"IR:'{0}'={1}\", command, state);\n\t\t\tif (IrPort == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"WARNING No IR Port assigned to controller\");\n\t\t\t\treturn;\n\t\t\t} \n\t\t\tif (!DriverIsLoaded)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"WARNING IR driver is not loaded\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (state)\n\t\t\t{\n\t\t\t\tif (IrPort.IsIRCommandAvailable(IrPortUid, command))\n\t\t\t\t\tIrPort.Press(IrPortUid, command);\n\t\t\t\telse\n\t\t\t\t\tNoIrCommandError(command);\n\t\t\t}\n\t\t\telse\n\t\t\t\tIrPort.Release();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Pulse method\n\t\t/// </summary>\n\t\t/// <param name=\"command\">IR command to send</param>\n\t\t/// <param name=\"time\">time to pulse the command</param>\n\t\t/// <inheritdoc />\n\t\tpublic virtual void Pulse(string command, ushort time)\n\t\t{\n\t\t\tif (IrPort == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"WARNING No IR Port assigned to controller\");\n\t\t\t\treturn;\n\t\t\t} \n\t\t\tif (!DriverIsLoaded)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"WARNING IR driver is not loaded\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (IrPort.IsIRCommandAvailable(IrPortUid, command))\n\t\t\t\tIrPort.PressAndRelease(IrPortUid, command, time);\n\t\t\telse\n\t\t\t\tNoIrCommandError(command);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Notifies the console when a bad command is used.\n\t\t/// </summary>\n\t\t/// <param name=\"command\">command that was not found</param>\n\t\tprotected void NoIrCommandError(string command)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Device {0}: IR Driver {1} does not contain command {2}\",\n\t\t\t\tKey, IrPort.IRDriverFileNameByIRDriverId(IrPortUid), command);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/LevelControlListItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Devices;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a level control item in a list, which can be used to control volume or mute functionality.\n    /// </summary>\n    public class LevelControlListItem : AudioControlListItemBase\n    {\n\n        /// <summary>\n        /// A reference to the IBasicVolumeWithFeedback device for control.\n        /// </summary>\n        [JsonIgnore]\n        public IBasicVolumeWithFeedback LevelControl\n        {\n            get\n            {\n                if (_levelControl == null)\n                    _levelControl = DeviceManager.GetDeviceForKey(ParentDeviceKey) as IBasicVolumeWithFeedback;\n                return _levelControl;\n            }\n        }\n        IBasicVolumeWithFeedback _levelControl;\n\n        /// <summary>\n        /// Gets the name from the device if it implements IKeyName or else returns the Name property\n        /// </summary>\n        [JsonProperty(\"preferredName\")]\n        public string PreferredName\n        {\n            get\n            {\n                if (!string.IsNullOrEmpty(Name)) return Name;\n                else\n                {\n                    if (LevelControl is IKeyName namedLevelControl)\n                    {\n                        if (namedLevelControl == null)\n                            return \"---\";\n                        return namedLevelControl.Name;\n                    }\n                    else return \"---\";\n                }\n            }\n        }\n\n        /// <summary>\n        /// The key of the device in the DeviceManager for control\n        /// </summary>\n        [JsonProperty(\"deviceKey\")]\n        public string DeviceKey\n        {\n            get\n            {\n                if (string.IsNullOrEmpty(ItemKey)) return ParentDeviceKey;\n                else\n                {\n                    return DeviceManager.AllDevices.\n                    Where(d => d.Key.Contains(ParentDeviceKey) && d.Key.Contains(ItemKey)).FirstOrDefault()?.Key ?? $\"{ParentDeviceKey}--{ItemKey}\";\n                }\n            }\n        }\n\n        /// <summary>\n        /// Indicates if the item is a level, mute , or both\n        /// </summary>\n        [JsonProperty(\"type\")]\n        [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]\n        public eLevelControlType Type { get; set; }\n\n\n        /// <summary>\n        /// Indicates if the item is a mic or not.\n        /// </summary>\n        [JsonProperty(\"isMic\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsMic { get; set; }\n\n        /// <summary>\n        /// Indicates if the item should show the raw level in the UI.\n        /// </summary>\n        [JsonProperty(\"showRawLevel\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? ShowRawLevel { get; set; }\n    }\n\n    /// <summary>\n    /// Indicates the type of level control item.\n    /// </summary>\n    [Flags]\n    public enum eLevelControlType\n    {\n        /// <summary>\n        /// Indicates that the item is a level control only\n        /// </summary>\n        Level = 1,\n        /// <summary>\n        /// Indicates that the item is a mute control only\n        /// </summary>\n        Mute = 2,\n        /// <summary>\n        /// Indicates that the item is both a level and mute control\n        /// </summary>\n        LevelAndMute = Level | Mute,\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/PduInterfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.Devices\n{\n    /// <summary>\n    /// Interface for any device that is able to control it'spower and has a configurable reboot time\n    /// </summary>    \n    public interface IHasPowerCycle : IKeyName, IHasPowerControlWithFeedback\n    {\n        /// <summary>\n        /// Delay between power off and power on for reboot\n        /// </summary>\n        int PowerCycleTimeMs { get;}\n\n        /// <summary>\n        /// Reboot outlet\n        /// </summary>\n        void PowerCycle();\n    }\n\n    /// <summary>\n    /// Interface for any device that contains a collection of IHasPowerReboot Devices\n    /// </summary>    \n    public interface IHasControlledPowerOutlets : IKeyName\n    {\n        /// <summary>\n        /// Collection of IPduOutlets\n        /// </summary>\n        ReadOnlyDictionary<int, IHasPowerCycle> PduOutlets { get; }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/PowerInterfaces.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IHasBatteryStats\n    /// </summary>\n    public interface IHasBatteryStats : IKeyName\n    {\n        /// <summary>\n        /// Gets the BatteryPercentage\n        /// </summary>\n        int BatteryPercentage { get; }\n\n        /// <summary>\n        /// Gets the BatteryCautionThresholdPercentage\n        /// </summary>\n        int BatteryCautionThresholdPercentage { get;  }\n\n        /// <summary>\n        /// Gets the BatteryWarningThresholdPercentage\n        /// </summary>\n        int BatteryWarningThresholdPercentage { get; }\n\n        /// <summary>\n        /// Gets the BatteryIsWarningFeedback\n        /// </summary>\n        BoolFeedback BatteryIsWarningFeedback { get; }\n\n        /// <summary>\n        /// Gets the BatteryIsCautionFeedback\n        /// </summary>\n        BoolFeedback BatteryIsCautionFeedback { get; }\n\n        /// <summary>\n        /// Gets the BatteryIsOkFeedback\n        /// </summary>\n        BoolFeedback BatteryIsOkFeedback { get; }\n\n        /// <summary>\n        /// Gets the BatteryPercentageFeedback\n        /// </summary>\n        IntFeedback BatteryPercentageFeedback { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IHasBatteryCharging\n    /// </summary>\n    public interface IHasBatteryCharging : IHasBatteryStats\n    {\n        /// <summary>\n        /// Gets the BatteryIsCharging\n        /// </summary>\n        BoolFeedback BatteryIsCharging { get; }\n    }\n\n    /// <summary>\n    /// Interface for any device that has multiple batteries that can be monitored\n    /// </summary>\n    public interface IHasBatteries : IKeyName\n    {\n        /// <summary>\n        /// Collection of batteries\n        /// </summary>\n        ReadOnlyDictionary<string, IHasBatteryStats> Batteries { get; }   \n    }\n\n    /// <summary>\n    /// Defines the contract for IHasBatteryStatsExtended\n    /// </summary>\n    public interface IHasBatteryStatsExtended : IHasBatteryStats\n    {\n        /// <summary>\n        /// Gets the InputVoltage in millivolts\n        /// </summary>\n        int InputVoltage { get; }\n\n        /// <summary>\n        /// Gets the OutputVoltage in millivolts\n        /// </summary>\n        int OutputVoltage { get; }\n\n        /// <summary>\n        /// Gets the InputCurrent in milliamps\n        /// </summary>\n        int InptuCurrent { get; }\n\n        /// <summary>\n        /// Gets the OutputCurrent in milliamps\n        /// </summary>\n        int OutputCurrent { get; }\n\n        /// <summary>\n        /// Gets the InputVoltageFeedback\n        /// </summary>\n        IntFeedback InputVoltageFeedback { get; }\n\n        /// <summary>\n        /// Gets the OutputVoltageFeedback\n        /// </summary>\n        IntFeedback OutputVoltageFeedback { get; }\n\n        /// <summary>\n        /// Gets the InputCurrentFeedback\n        /// </summary>\n        IntFeedback InputCurrentFeedback { get; }\n\n        /// <summary>\n        /// Gets the OutputCurrentFeedback\n        /// </summary>\n        IntFeedback OutputCurrentFeedback { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IHasPowerCycleWithBattery\n    /// </summary>\n    public interface IHasPowerCycleWithBattery : IHasPowerCycle, IHasBatteryStats\n    {\n        \n    }\n\n    /// <summary>\n    /// Interface for any device that is able to control it's power and has a configurable reboot time\n    /// </summary>\n    public interface IHasPowerCycle : IKeyName, IHasPowerControlWithFeedback\n    {\n        /// <summary>\n        /// Delay between power off and power on for reboot\n        /// </summary>\n        int PowerCycleTimeMs { get; }\n\n        /// <summary>\n        /// Reboot outlet\n        /// </summary>\n        void PowerCycle();\n    }\n\n    /// <summary>\n    /// Interface for any device that contains a collection of IHasPowerReboot Devices\n    /// </summary>\n    public interface IHasControlledPowerOutlets : IKeyName\n    {\n        /// <summary>\n        /// Collection of IPduOutlets\n        /// </summary>\n        ReadOnlyDictionary<int, IHasPowerCycle> PduOutlets { get; }\n\n    }\n\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/PresentationDeviceType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Crestron.SimplSharpPro.EthernetCommunication;\nusing Crestron.SimplSharpPro.UI;\n\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Enumeration of PresentationSourceType values\n /// </summary>\n\tpublic enum PresentationSourceType\n\t{\n\t\t/// <summary>\n\t\t/// No source type assigned\n\t\t/// </summary>\n\t\tNone, \n\t\t\n\t\t/// <summary>\n\t\t/// DVD source type\n\t\t/// </summary>\n\t\tDvd, \n\t\t\n\t\t/// <summary>\n\t\t/// Document Camera source type\n\t\t/// </summary>\n\t\tLaptop, \n\t\t\n\t\t/// <summary>\n\t\t/// PC source type\n\t\t/// </summary>\n\t\tPC, \n\t\t\n\t\t/// <summary>\n\t\t/// Set Top Box source type\n\t\t/// </summary>\n\t\tSetTopBox, \n\t\t\n\t\t/// <summary>\n\t\t/// VCR source type\n\t\t/// </summary>\n\t\tVCR\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/PresetListItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a PresetListItem\n    /// </summary>\n    public class PresetListItem : AudioControlListItemBase\n    {\n        /// <summary>\n        /// Gets the preset associated with this list item\n        /// </summary>\n        [JsonIgnore]\n        public IKeyName Preset\n        {\n            get\n            {\n                if (_preset == null)\n                {\n                    var parent = DeviceManager.GetDeviceForKey(ParentDeviceKey) as IDspPresets;\n                    if (parent == null || !parent.Presets.ContainsKey(ItemKey))\n                        return null;\n                    _preset = parent.Presets[ItemKey];\n                }\n                return _preset;\n            }\n        }\n        private IKeyName _preset;\n\n        /// <summary>\n        /// Gets the name from the device if it implements IKeyName or else returns the Name property\n        /// </summary>\n        [JsonProperty(\"preferredName\")]\n        public string PreferredName\n        {\n            get\n            {\n                if (!string.IsNullOrEmpty(Name)) return Name;\n\n                else return Preset.Name;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/ProcessorExtensionDeviceFactory.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing PepperDash.Essentials.Core.Config;\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Represents a factory for creating processor extension devices.\n  /// </summary>\n  /// <typeparam name=\"T\">The type of the processor extension device.</typeparam>\n  [Obsolete(\"will be removed in a future version\")]\n  public abstract class ProcessorExtensionDeviceFactory<T> : IProcessorExtensionDeviceFactory where T : EssentialsDevice\n  {\n    #region IProcessorExtensionDeviceFactory Members\n\n    /// <summary>\n    /// Gets or sets the TypeNames\n    /// </summary>\n    public List<string> TypeNames { get; protected set; }\n\n    /// <summary>\n    /// LoadFactories method\n    /// </summary>\n    public void LoadFactories()\n    {\n      foreach (var typeName in TypeNames)\n      {\n        string description = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0\n            ? descriptionAttribute[0].Description\n            : \"No description available\";\n\n        ProcessorExtensionDeviceFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildDevice);\n      }\n    }\n\n    /// <summary>\n    /// The method that will build the device\n    /// </summary>\n    /// <param name=\"dc\">The device config</param>\n    /// <returns>An instance of the device</returns>\n    public abstract EssentialsDevice BuildDevice(DeviceConfig dc);\n\n    #endregion\n\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/ReconfigurableDevice.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Essentials.Core.Devices\n{\n    /// <summary>\n    /// \n    /// </summary>\n    public abstract class ReconfigurableDevice : EssentialsDevice, IReconfigurableDevice\n    {\n        /// <summary>\n        /// Event fired when the configuration changes\n        /// </summary>\n        public event EventHandler<EventArgs> ConfigChanged;\n\n        /// <summary>\n        /// Gets the current DeviceConfig\n        /// </summary>\n        public DeviceConfig Config { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"config\">config of the device</param>\n        protected ReconfigurableDevice(DeviceConfig config)\n            : base(config.Key)\n        {\n            SetNameHelper(config);\n\n            Config = config;\n        }\n\n        /// <summary>\n        /// Sets the Config, calls CustomSetConfig and fires the ConfigChanged event\n        /// </summary>\n        /// <param name=\"config\"></param>\n        /// <summary>\n        /// SetConfig method\n        /// </summary>\n        public void SetConfig(DeviceConfig config)\n        {\n            Config = config;\n\n            SetNameHelper(config);\n\n            CustomSetConfig(config);\n\n            var handler = ConfigChanged;\n            if (handler != null)\n            {\n                handler(this, new EventArgs());\n            }\n        }\n\n        void SetNameHelper(DeviceConfig config)\n        {\n            if (!string.IsNullOrEmpty(config.Name))\n                Name = config.Name;\n        }\n\n\n\n        /// <summary>\n        /// Used by the extending class to allow for any custom actions to be taken (tell the ConfigWriter to write config, etc)\n        /// </summary>\n        /// <param name=\"config\">config of the device</param>\n        protected virtual void CustomSetConfig(DeviceConfig config)\n        {\n            ConfigWriter.UpdateDeviceConfig(config);\n        }\n    }\n\n    /// <summary>\n    /// A ReconfigurableDevice that is also bridgeable\n    /// </summary>\n    public abstract class ReconfigurableBridgableDevice : ReconfigurableDevice, IBridgeAdvanced\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"config\">config of the device</param>\n        protected ReconfigurableBridgableDevice(DeviceConfig config) : base(config)\n        {\n        }\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <param name=\"trilist\">trilist to link</param>\n        /// <param name=\"joinStart\">the join to start at</param>\n        /// <param name=\"joinMapKey\">key to the join map</param>\n        /// <param name=\"bridge\">the bridge to use</param>\n        public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/SmartObjectBaseTypes.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Represents a SmartObjectJoinOffsets\n /// </summary>\n\tpublic class SmartObjectJoinOffsets\n\t{\n\t\t/// <summary>\n\t\t/// Dpad Join Offset\n\t\t/// </summary>\n\t\tpublic const ushort Dpad = 1;\n\n\t\t/// <summary>\n\t\t/// Numpad Join Offset\n\t\t/// </summary>\n\t\tpublic const ushort Numpad = 2;\n\n\t\t/// <summary>\n\t\t/// PresetList Join Offset\n\t\t/// </summary>\n\t\tpublic const ushort PresetList = 6;\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/SourceListItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the type of source list item, which can be a route, off, or other.\n    /// This is used to categorize the source list items in a room.\n    /// The type is serialized to JSON and can be used to determine how the item should be displayed or handled in the UI.\n    /// </summary>\n    public enum eSourceListItemType\n    {\n        /// <summary>\n        /// Represents a typical route.\n        /// </summary>\n        Route,\n        /// <summary>\n        /// Represents an off route.\n        /// </summary>\n        Off,\n        /// <summary>\n        /// Represents some other type of route\n        /// </summary>\n        Other,\n    }\n\n    /// <summary>\n    /// Represents a SourceListItem\n    /// </summary>\n    public class SourceListItem\n    {\n        /// <summary>\n        /// The key of the source item, which is used to identify it in the DeviceManager\n        /// </summary>\n        [JsonProperty(\"sourceKey\")]\n        public string SourceKey { get; set; }\n\n        /// <summary>\n        /// Returns the source Device for this, if it exists in DeviceManager\n        /// </summary>\n        [JsonIgnore]\n        public Device SourceDevice\n        {\n            get\n            {\n                if (_SourceDevice == null)\n                    _SourceDevice = DeviceManager.GetDeviceForKey(SourceKey) as Device;\n                return _SourceDevice;\n            }\n        }\n\n        private Device _SourceDevice;\n\n        /// <summary>\n        /// Gets either the source's Name or this AlternateName property, if \n        /// defined.  If source doesn't exist, returns \"Missing source\"\n        /// </summary>\n        [JsonProperty(\"preferredName\")]\n        public string PreferredName\n        {\n            get\n            {\n                if (string.IsNullOrEmpty(Name))\n                {\n                    if (SourceDevice == null)\n                        return \"---\";\n                    return SourceDevice.Name;\n                }\n                return Name;\n            }\n        }\n\n        /// <summary>\n        /// A name that will override the source's name on the UI\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Specifies and icon for the source list item\n        /// </summary>\n\t\t[JsonProperty(\"icon\")]\n        public string Icon { get; set; }\n\n        /// <summary>\n        /// Alternate icon\n        /// </summary>\n\t\t[JsonProperty(\"altIcon\")]\n        public string AltIcon { get; set; }\n\n        /// <summary>\n        /// Indicates if the item should be included in the source list\n        /// </summary>\n\t\t[JsonProperty(\"includeInSourceList\")]\n        public bool IncludeInSourceList { get; set; }\n\n        /// <summary>\n        /// Used to specify the order of the items in the source list when displayed\n        /// </summary>\n\t\t[JsonProperty(\"order\")]\n        public int Order { get; set; }\n\n        /// <summary>\n        /// The key of the device for volume control\n        /// </summary>\n\t\t[JsonProperty(\"volumeControlKey\")]\n        public string VolumeControlKey { get; set; }\n\n        /// <summary>\n        /// The type of source list item\n        /// </summary>\n\t\t[JsonProperty(\"type\")]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public eSourceListItemType Type { get; set; }\n\n        /// <summary>\n        /// The list of routes to execute for this source list item\n        /// </summary>\n\t\t[JsonProperty(\"routeList\")]\n        public List<SourceRouteListItem> RouteList { get; set; }\n\n        /// <summary>\n        /// Indicates if this source should be disabled for sharing to the far end call participants via codec content\n        /// </summary>\n\t\t[JsonProperty(\"disableCodecSharing\")]\n        public bool DisableCodecSharing { get; set; }\n\n        /// <summary>\n        /// Indicates if this source should be disabled for routing to a shared output\n        /// </summary>\n\t\t[JsonProperty(\"disableRoutedSharing\")]\n        public bool DisableRoutedSharing { get; set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        [JsonProperty(\"destinations\")]\n        public List<eSourceListItemDestinationTypes> Destinations { get; set; }\n        /// <summary>\n        /// A means to reference a source list for this source item, in the event that this source has an input that can have sources routed to it\n        /// </summary>\n        [JsonProperty(\"sourceListKey\")]\n        public string SourceListKey { get; set; }\n\n        /// <summary>\n        /// Indicates if the device associated with this source is controllable\n        /// </summary>\n        [JsonProperty(\"isControllable\")]\n        public bool IsControllable { get; set; }\n\n        /// <summary>\n        /// Indicates that the device associated with this source has audio available\n        /// </summary>\n        [JsonProperty(\"isAudioSource\")]\n        public bool IsAudioSource { get; set; }\n\n        /// <summary>\n        /// Hide source on UI when Avanced Sharing is enabled\n        /// </summary>\n        [JsonProperty(\"disableAdvancedRouting\")]\n        public bool DisableAdvancedRouting { get; set; }\n\n        /// <summary>\n        /// Hide source on UI when Simpl Sharing is enabled\n        /// </summary>\n        [JsonProperty(\"disableSimpleRouting\")]\n        public bool DisableSimpleRouting { get; set; }\n\n        /// <summary>\n        /// The key of the device that provides video sync for this source item\n        /// </summary>\n        [JsonProperty(\"syncProviderDeviceKey\")]\n        public string SyncProviderDeviceKey { get; set; }\n\n        /// <summary>\n        /// Indicates if the source supports USB connections\n        /// </summary>\n        [JsonProperty(\"supportsUsb\")]\n        public bool SupportsUsb { get; set; }\n\n        /// <summary>\n        /// The key of the source port associated with this source item\n        /// This is used to identify the specific port on the source device that this item refers to for advanced routing\n        /// </summary>\n        [JsonProperty(\"sourcePortKey\")]\n        public string SourcePortKey { get; set; }\n\n\n        /// <summary>\n        /// Default constructor for SourceListItem, initializes the Icon to \"Blank\"\n        /// </summary>\n        public SourceListItem()\n        {\n            Icon = \"Blank\";\n        }\n\n        /// <summary>\n        /// Returns a string representation of the SourceListItem, including the SourceKey and Name\n        /// </summary>\n        /// <returns> A string representation of the SourceListItem</returns>\n        public override string ToString()\n        {\n            return $\"{SourceKey}:{Name}\";\n        }\n    }\n\n    /// <summary>\n    /// Represents a route in a source list item, which defines the source and destination keys and the type of signal being routed\n    /// </summary>\n    public class SourceRouteListItem\n    {\n        /// <summary>\n        /// The key of the source device to route from\n        /// </summary>\n        [JsonProperty(\"sourceKey\")]\n        public string SourceKey { get; set; }\n\n        /// <summary>\n        /// The key of the source port to route from\n        /// </summary>\n        [JsonProperty(\"sourcePortKey\")]\n        public string SourcePortKey { get; set; }\n\n        /// <summary>\n        /// The key of the destination device to route to\n        /// </summary>\n        [JsonProperty(\"destinationKey\")]\n        public string DestinationKey { get; set; }\n\n        /// <summary>\n        /// The key of the destination port to route to\n        /// </summary>\n        [JsonProperty(\"destinationPortKey\")]\n        public string DestinationPortKey { get; set; }\n\n        /// <summary>\n        /// The type of signal being routed, such as audio or video\n        /// </summary>\n        [JsonProperty(\"type\")]\n        public eRoutingSignalType Type { get; set; }\n\n        /// <summary>\n        /// Key for a destination list item. If BOTH SourceListItemKey AND DestinationListItemKey are defined,\n        /// then the direct route method should be used.\n        /// </summary>\n        [JsonProperty(\"destinationListItemKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string DestinationListItemKey { get; set; }\n\n        /// <summary>\n        /// Key for a source list item. If BOTH SourceListItemKey AND DestinationListItemKey are defined,\n        /// then the direct route method should be used.\n        /// </summary>\n        [JsonProperty(\"sourceListItemKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string SourceListItemKey { get; set; }\n    }\n\n    /// <summary>\n    /// Defines the valid destination types for SourceListItems in a room\n    /// </summary>\n    [Obsolete]\n    public enum eSourceListItemDestinationTypes\n    {\n        /// <summary>\n        /// Default display, used for the main video output in a room\n        /// </summary>\n        defaultDisplay,\n        /// <summary>\n        /// Left display\n        /// </summary>\n        leftDisplay,\n        /// <summary>\n        /// Right display\n        /// </summary>\n        rightDisplay,\n        /// <summary>\n        /// Center display\n        /// </summary>\n        centerDisplay,\n        /// <summary>\n        /// Program audio, used for the main audio output in a room\n        /// </summary>\n        programAudio,\n        /// <summary>\n        /// Codec content, used for sharing content to the far end in a video call\n        /// </summary>\n        codecContent,\n        /// <summary>\n        /// Front left display, used for rooms with multiple displays\n        /// </summary>\n        frontLeftDisplay,\n        /// <summary>\n        /// Front right display, used for rooms with multiple displays\n        /// </summary>\n        frontRightDisplay,\n        /// <summary>\n        /// Rear left display, used for rooms with multiple displays\n        /// </summary>\n        rearLeftDisplay,\n        /// <summary>\n        /// Rear right display, used for rooms with multiple displays\n        /// </summary>\n        rearRightDisplay,\n        /// <summary>\n        /// Auxiliary display 1, used for additional displays in a room\n        /// </summary>\n        auxDisplay1,\n        /// <summary>\n        /// Auxiliary display 2, used for additional displays in a room\n        /// </summary>\n        auxDisplay2,\n        /// <summary>\n        /// Auxiliary display 3, used for additional displays in a room\n        /// </summary>\n        auxDisplay3,\n        /// <summary>\n        /// Auxiliary display 4, used for additional displays in a room\n        /// </summary>\n        auxDisplay4,\n        /// <summary>\n        /// Auxiliary display 5, used for additional displays in a room\n        /// </summary>\n        auxDisplay5,\n        /// <summary>\n        /// Auxiliary display 6, used for additional displays in a room\n        /// </summary>\n        auxDisplay6,\n        /// <summary>\n        /// Auxiliary display 7, used for additional displays in a room\n        /// </summary>\n        auxDisplay7,\n        /// <summary>\n        /// Auxiliary display 8, used for additional displays in a room\n        /// </summary>\n        auxDisplay8,\n        /// <summary>\n        /// Auxiliary display 9, used for additional displays in a room\n        /// </summary>\n        auxDisplay9,\n        /// <summary>\n        /// Auxiliary display 10, used for additional displays in a room\n        /// </summary>\n        auxDisplay10,\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Devices/VolumeDeviceChangeEventArgs.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic class VolumeDeviceChangeEventArgs : EventArgs\n\t{\n\t\t/// <summary>\n\t\t/// The old device\n\t\t/// </summary>\n\t\tpublic IBasicVolumeControls OldDev { get; private set; }\n\n\t\t/// <summary>\n\t\t/// The new device\n\t\t/// </summary>\n\t\tpublic IBasicVolumeControls NewDev { get; private set; }\n\n\t\t/// <summary>\n\t\t/// The type of change\n\t\t/// </summary>\n\t\tpublic ChangeType Type { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"oldDev\">the old device</param>\n\t\t/// <param name=\"newDev\">the new device</param>\n\t\t/// <param name=\"type\">the type of change</param>\n\t\tpublic VolumeDeviceChangeEventArgs(IBasicVolumeControls oldDev, IBasicVolumeControls newDev, ChangeType type)\n\t\t{\n\t\t\tOldDev = oldDev;\n\t\t\tNewDev = newDev;\n\t\t\tType = type;\n\t\t}\n\t}\n\n /// <summary>\n /// Enumeration of ChangeType values\n /// </summary>\n\tpublic enum ChangeType\n\t{\n\t\t/// <summary>\n\t\t/// Will change\n\t\t/// </summary>\n\t\tWillChange, \n\t\t\n\t\t/// <summary>\n\t\t/// Did change\n\t\t/// </summary>\n\t\tDidChange\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Ethernet/EthernetStatistics.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.Ethernet\n{\n\t/// <summary>\n\t/// Ethernet settings feedbacks\n\t/// </summary>\n\tpublic static class EthernetSettings\n\t{\n\t\t/// <summary>\n\t\t/// Link active feedback\n\t\t/// </summary>\n\t\tpublic static readonly BoolFeedback LinkActive = new BoolFeedback(\"LinkActive\",\n\t\t\t() => true);\n\n\t\t/// <summary>\n\t\t/// DHCP active feedback\n\t\t/// </summary>\t\n\t\tpublic static readonly BoolFeedback DhcpActive = new BoolFeedback(\"DhcpActive\",\n\t\t\t() => CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, 0) == \"ON\");\n\n\t\t/// <summary>\n\t\t/// Hostname feedback\n\t\t/// </summary>\n\t\tpublic static readonly StringFeedback Hostname = new StringFeedback(\"Hostname\",\n\t\t\t() => CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0));\n\n\t\t/// <summary>\n\t\t/// IP Address feedback\n\t\t/// </summary>\n\t\tpublic static readonly StringFeedback IpAddress0 = new StringFeedback(\"IpAddress0\",\n\t\t\t() => CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0));\n\n\t\t/// <summary>\n\t\t/// Subnet Mask feedback\n\t\t/// </summary>\n\t\tpublic static readonly StringFeedback SubnetMask0 = new StringFeedback(\"SubnetMask0\",\n\t\t\t() => CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0));\n\n\t\t/// <summary>\n\t\t/// Default Gateway feedback\n\t\t/// </summary>\n\t\tpublic static readonly StringFeedback DefaultGateway0 = new StringFeedback(\"DefaultGateway0\",\n\t\t\t() => CrestronEthernetHelper.GetEthernetParameter(\n\t\t\t\tCrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0));\t\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Extensions/IpAddressExtensions.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Net;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace PepperDash.Essentials.Core\r\n{\r\n    /// <summary>\r\n    /// Extensions for IPAddress to provide additional functionality such as getting broadcast address, network address, and checking if two addresses are in the same subnet.\r\n    /// </summary>\r\n    public static class IPAddressExtensions\r\n    {\r\n        /// <summary>\r\n        /// Get the broadcast address for a given IP address and subnet mask.\r\n        /// </summary>\r\n        /// <param name=\"address\">Address to check</param>\r\n        /// <param name=\"subnetMask\">Subnet mask in a.b.c.d format</param>\r\n        /// <returns>Broadcast address</returns>\r\n        /// <remarks>\r\n        /// If the input IP address is 192.168.1.100 and the subnet mask is 255.255.255.0, the broadcast address will be 192.168.1.255\r\n        /// </remarks>\r\n        /// <exception cref=\"ArgumentException\"></exception>\r\n        public static IPAddress GetBroadcastAddress(this IPAddress address, IPAddress subnetMask)\r\n        {\r\n            byte[] ipAdressBytes = address.GetAddressBytes();\r\n            byte[] subnetMaskBytes = subnetMask.GetAddressBytes();\r\n\r\n            if (ipAdressBytes.Length != subnetMaskBytes.Length)\r\n                throw new ArgumentException(\"Lengths of IP address and subnet mask do not match.\");\r\n\r\n            byte[] broadcastAddress = new byte[ipAdressBytes.Length];\r\n            for (int i = 0; i < broadcastAddress.Length; i++)\r\n            {\r\n                broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));\r\n            }\r\n            return new IPAddress(broadcastAddress);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Get the network address for a given IP address and subnet mask.\r\n        /// </summary>\r\n        /// <param name=\"address\">Address to check</param>\r\n        /// <param name=\"subnetMask\">Subnet mask in a.b.c.d</param>\r\n        /// <returns>Network Address</returns>\r\n        /// /// <remarks>\r\n        /// If the input IP address is 192.168.1.100 and the subnet mask is 255.255.255.0, the network address will be 192.168.1.0\r\n        /// </remarks>\r\n        /// <exception cref=\"ArgumentException\"></exception>\r\n        public static IPAddress GetNetworkAddress(this IPAddress address, IPAddress subnetMask)\r\n        {\r\n            byte[] ipAdressBytes = address.GetAddressBytes();\r\n            byte[] subnetMaskBytes = subnetMask.GetAddressBytes();\r\n\r\n            if (ipAdressBytes.Length != subnetMaskBytes.Length)\r\n                throw new ArgumentException(\"Lengths of IP address and subnet mask do not match.\");\r\n\r\n            byte[] broadcastAddress = new byte[ipAdressBytes.Length];\r\n            for (int i = 0; i < broadcastAddress.Length; i++)\r\n            {\r\n                broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));\r\n            }\r\n            return new IPAddress(broadcastAddress);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Determine if two IP addresses are in the same subnet.\r\n        /// </summary>\r\n        /// <param name=\"address2\">Address to check</param>\r\n        /// <param name=\"address\">Second address to check</param>\r\n        /// <param name=\"subnetMask\">Subnet mask to use to compare the 2 IP Address</param>\r\n        /// <returns>True if addresses are in the same subnet</returns>\r\n        /// <remarks>\r\n        /// If the input IP addresses are 192.168.1.100 and 192.168.1.200, and the subnet mask is 255.255.255.0, this will return true.\r\n        /// If the input IP addresses are 10.1.1.100 and 192.168.1.100, and the subnet mask is 255.255.255.0, this will return false.\r\n        /// </remarks>\r\n        public static bool IsInSameSubnet(this IPAddress address2, IPAddress address, IPAddress subnetMask)\r\n        {\r\n            IPAddress network1 = address.GetNetworkAddress(subnetMask);\r\n            IPAddress network2 = address2.GetNetworkAddress(subnetMask);\r\n\r\n            return network1.Equals(network2);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Extensions/JsonExtensions.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// JsonExtensions class\n    /// </summary>\n    public static class JsonExtensions\n    {\n        /// <summary>\n        /// FindTokens method\n        /// </summary>\n        /// <param name=\"containerToken\">token for the container</param>\n        /// <param name=\"name\">name of the token to find</param>\n        /// <returns>list of matching tokens</returns>\n        public static List<JToken> FindTokens(this JToken containerToken, string name)\n        {\n            List<JToken> matches = new List<JToken>();\n            FindTokens(containerToken, name, matches);\n            return matches;\n        }\n\n        private static void FindTokens(JToken containerToken, string name, List<JToken> matches)\n        {\n            if (containerToken.Type == JTokenType.Object)\n            {\n                foreach (JProperty child in containerToken.Children<JProperty>())\n                {\n                    if (child.Name == name)\n                    {\n                        matches.Add(child.Value);\n                    }\n                    FindTokens(child.Value, name, matches);\n                }\n            }\n            else if (containerToken.Type == JTokenType.Array)\n            {\n                foreach (JToken child in containerToken.Children())\n                {\n                    FindTokens(child, name, matches);\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Extensions/StringExtensions.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// StringExtensions class\n    /// </summary>\n    public static class StringExtensions\n    {\n        /// <summary>\n        /// Returns null if a string is empty, otherwise returns the string\n        /// </summary>\n        /// <param name=\"s\">string input</param>\n        /// <returns>null if the string is emtpy, otherwise returns the string</returns>\n        public static string NullIfEmpty(this string s)\n        {\n            return string.IsNullOrEmpty(s) ? null : s;\n        }\n\n        /// <summary>\n        /// Returns null if a string is empty or made of only whitespace characters, otherwise returns the string\n        /// </summary>\n        /// <param name=\"s\">string input</param>\n        /// <returns>null if the string is wempty or made of only whitespace characters, otherwise returns the string</returns>\n        public static string NullIfWhiteSpace(this string s)\n        {\n            return string.IsNullOrEmpty(s.Trim()) ? null : s;\n        }\n\n        /// <summary>\n        /// Returns a replacement string if the input string is empty or made of only whitespace characters, otherwise returns the input string\n        /// </summary>\n        /// <param name=\"s\">input string</param>\n        /// <param name=\"newString\">string to replace with if input string is empty or whitespace</param>\n        /// <returns>returns newString if s is null, emtpy, or made of whitespace characters, otherwise returns s</returns>\n        public static string ReplaceIfNullOrEmpty(this string s, string newString)\n        {\n            return string.IsNullOrEmpty(s) ? newString : s;\n        }\n\n        /// <summary>\n        /// Overload for Contains that allows setting an explicit String Comparison\n        /// </summary>\n        /// <param name=\"source\">Source String</param>\n        /// <param name=\"toCheck\">String to check in Source String</param>\n        /// <param name=\"comp\">Comparison parameters</param>\n        /// <returns>true of string contains \"toCheck\"</returns>\n        public static bool Contains(this string source, string toCheck, StringComparison comp)\n        {\n            if (string.IsNullOrEmpty(source)) return false;\n            return source.IndexOf(toCheck, comp) >= 0;\n        }\n\n        /// <summary>\n        /// Performs TrimStart() and TrimEnd() on source string\n        /// </summary>\n        /// <param name=\"source\">String to Trim</param>\n        /// <returns>Trimmed String</returns>\n        public static string TrimAll(this string source)\n        {\n            return string.IsNullOrEmpty(source) ? string.Empty : source.TrimStart().TrimEnd();\n        }\n\n        /// <summary>\n        /// Performs TrimStart(chars char[]) and TrimEnd(chars char[]) on source string.\n        /// </summary>\n        /// <param name=\"source\">String to Trim</param>\n        /// <param name=\"chars\">Char Array to trim from string</param>\n        /// <returns>Trimmed String</returns>\n        public static string TrimAll(this string source, char[] chars)\n        {\n            return string.IsNullOrEmpty(source) ? string.Empty : source.TrimStart(chars).TrimEnd(chars);\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Provides functionality for managing and registering device factories, including loading plugin-based factories and\n    /// retrieving devices based on their configuration.\n    /// </summary>\n    /// <remarks>The <see cref=\"DeviceFactory\"/> class is responsible for discovering and registering device factories\n    /// from plugins, as well as providing methods to retrieve devices based on their configuration. It maintains a\n    /// collection of factory methods that are keyed by device type names, allowing for extensibility through plugins. This\n    /// class also handles metadata retrieval and secret management for device configurations.</remarks>\n    public class DeviceFactory\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DeviceFactory\"/> class and loads all available device factories\n        /// from the current assembly.\n        /// </summary>\n        /// <remarks>This constructor scans the executing assembly for types that implement the <see\n        /// cref=\"IDeviceFactory\"/> interface and are not abstract or interfaces. For each valid type, an instance is\n        /// created and passed to the  <c>LoadDeviceFactories</c> method for further processing.  If a type cannot be\n        /// instantiated, an informational log message is generated, and the process continues with the remaining\n        /// types.</remarks>\n        public DeviceFactory()\n        {\n            var programAssemblies = Directory.GetFiles(InitialParametersClass.ProgramDirectory.ToString(), \"*.dll\");\n\n            // Assemblies known to cause load errors that should be skipped\n            var assembliesToSkip = new[] { \"CrestronOnvif.dll\" };\n\n            foreach (var assembly in programAssemblies)\n            {\n                if (assembliesToSkip.Any(a => Path.GetFileName(assembly).Equals(a, StringComparison.OrdinalIgnoreCase)))\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Skipping assembly: {assemblyName}\", Path.GetFileName(assembly));\n                    continue;\n                }\n\n                try\n                {\n                    Assembly.LoadFrom(assembly);\n                }\n                catch (Exception e)\n                {\n                    Debug.LogError(\"Unable to load assembly: {assemblyName} - {message}\", assembly, e.Message);\n                }\n            }\n\n            var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();\n\n            // Loop through all loaded assemblies that contain at least 1 type that implements IDeviceFactory\n            foreach (var assembly in loadedAssemblies)\n            {\n                Debug.LogDebug(\"loaded assembly: {assemblyName}\", assembly.GetName()?.Name ?? \"Unknown\");\n\n                PluginLoader.AddLoadedAssembly(assembly.GetName()?.Name ?? \"Unknown\", assembly);\n\n                var types = assembly.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);\n\n                if (types == null || !types.Any())\n                {\n                    Debug.LogDebug(\"No DeviceFactory types found in assembly: {assemblyName}\", assembly.GetName().Name);\n                    continue;\n                }\n\n                foreach (var type in types)\n                {\n                    try\n                    {\n                        var factory = (IDeviceFactory)Activator.CreateInstance(type);\n                        LoadDeviceFactories(factory);\n                    }\n                    catch (Exception e)\n                    {\n                        Debug.LogError(\"Unable to load type: '{message}' DeviceFactory: {type}\", e.Message, type.Name);\n                    }\n                }\n\n            }\n        }\n\n        /// <summary>\n        /// Loads device factories from the specified plugin device factory and registers them for use.\n        /// </summary>\n        /// <remarks>This method retrieves metadata from the provided <paramref name=\"deviceFactory\"/>, including\n        /// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type\n        /// names are converted to lowercase for registration.</remarks>\n        /// <param name=\"deviceFactory\">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>\n        private static void LoadDeviceFactories(IDeviceFactory deviceFactory)\n        {\n            foreach (var typeName in deviceFactory.TypeNames)\n            {\n                string description = deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0\n                    ? descriptionAttribute[0].Description\n                    : \"No description available\";\n\n                AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);\n            }\n        }\n\n        /// <summary>\n        /// A dictionary of factory methods, keyed by config types, added by plugins.\n        /// These methods are looked up and called by GetDevice in this class.\n        /// </summary>\n        private static readonly Dictionary<string, DeviceFactoryWrapper> FactoryMethods =\n            new Dictionary<string, DeviceFactoryWrapper>(StringComparer.OrdinalIgnoreCase);\n\n        /// <summary>\n        /// Registers a factory method for creating instances of a specific type.\n        /// </summary>\n        /// <remarks>This method associates a type name with a factory method, allowing instances of the type to\n        /// be created dynamically. The factory method is stored internally and can be retrieved or invoked as\n        /// needed.</remarks>\n        /// <param name=\"typeName\">The name of the type for which the factory method is being registered. This value cannot be null or empty.</param>\n        /// <param name=\"method\">A delegate that defines the factory method. The delegate takes a <see cref=\"DeviceConfig\"/> parameter and\n        /// returns an instance of <see cref=\"IKeyed\"/>.</param>\n        public static void AddFactoryForType(string typeName, Func<DeviceConfig, IKeyed> method)\n        {\n            FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method });\n        }\n\n        /// <summary>\n        /// Registers a factory method for creating instances of a specific device type.\n        /// </summary>\n        /// <remarks>If a factory method for the specified <paramref name=\"typeName\"/> already exists, the method\n        /// will not overwrite it and will log an informational message instead.</remarks>\n        /// <param name=\"typeName\">The unique name of the device type. This serves as the key for identifying the factory method.</param>\n        /// <param name=\"description\">A brief description of the device type. This is used for informational purposes.</param>\n        /// <param name=\"Type\">The <see cref=\"Type\"/> of the device being registered. This represents the runtime type of the device.</param>\n        /// <param name=\"method\">A factory method that takes a <see cref=\"DeviceConfig\"/> as input and returns an instance of <see\n        /// cref=\"IKeyed\"/>.</param>\n        public static void AddFactoryForType(string typeName, string description, Type Type, Func<DeviceConfig, IKeyed> method)\n        {\n            if (FactoryMethods.ContainsKey(typeName))\n            {\n                Debug.LogInformation(\"Unable to add type: '{typeName}'. Already exists in DeviceFactory\", typeName);\n                return;\n            }\n\n            var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };\n\n            FactoryMethods.Add(typeName, wrapper);\n        }\n\n        private static void CheckForSecrets(IEnumerable<JProperty> obj)\n        {\n            foreach (var prop in obj.Where(prop => prop.Value as JObject != null))\n            {\n                if (prop.Name.Equals(\"secret\", StringComparison.CurrentCultureIgnoreCase))\n                {\n                    var secret = GetSecret(prop.Children().First().ToObject<SecretsPropertiesConfig>());\n\n                    prop.Parent.Replace(secret);\n                }\n\n                if (!(prop.Value is JObject recurseProp)) continue;\n\n                CheckForSecrets(recurseProp.Properties());\n            }\n        }\n\n        private static string GetSecret(SecretsPropertiesConfig data)\n        {\n            var secretProvider = SecretsManager.GetSecretProviderByKey(data.Provider);\n            if (secretProvider == null) return null;\n            var secret = secretProvider.GetSecret(data.Key);\n            if (secret != null) return (string)secret.Value;\n            Debug.LogMessage(LogEventLevel.Debug,\n                \"Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider\",\n                data.Provider, data.Key);\n            return string.Empty;\n        }\n\n\n        /// <summary>\n        /// Creates and returns a device instance based on the provided <see cref=\"DeviceConfig\"/>.\n        /// </summary>\n        /// <remarks>This method attempts to create a device using the type specified in the <paramref name=\"dc\"/>\n        /// parameter. If the type corresponds to a registered factory method, the device is created and returned. If the\n        /// type is unrecognized or an exception occurs, the method logs the error and returns <see\n        /// langword=\"null\"/>.</remarks>\n        /// <param name=\"dc\">The configuration object containing the key, name, type, and properties required to create the device.</param>\n        /// <returns>An instance of a device that implements <see cref=\"IKeyed\"/>, or <see langword=\"null\"/> if the device type is\n        /// not recognized or an error occurs during creation.</returns>\n        public static IKeyed GetDevice(DeviceConfig dc)\n        {\n            try\n            {\n                var localDc = new DeviceConfig(dc);\n\n                var key = localDc.Key;\n                var name = localDc.Name;\n                var type = localDc.Type;\n                var properties = localDc.Properties;\n\n                var typeName = localDc.Type.ToLower();\n\n                if (properties is JObject jObject)\n                {\n                    var jProp = jObject.Properties();\n\n                    CheckForSecrets(jProp);\n                }\n\n                if (!FactoryMethods.TryGetValue(typeName, out var wrapper))\n                {\n                    Debug.LogWarning(\"Device type '{typeName}' not found in DeviceFactory\", typeName);\n                    return null;\n                }\n\n                Debug.LogInformation(\"Loading '{type}' from {assemblyName}\", typeName, wrapper.Type.Assembly.FullName);\n\n                // Check for types that have been added by plugin dlls.\n                return wrapper.FactoryMethod(localDc);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(ex, \"Exception occurred while creating device {key}: {message}\", dc.Key, ex.Message);\n                Debug.LogDebug(ex, \"Exception details: {stackTrace}\", ex.StackTrace);\n                return null;\n            }\n        }\n\n        /// <summary>\n        /// Displays a list of device factory types that match the specified filter.\n        /// </summary>\n        /// <remarks>The method outputs the filtered list of device factory types to the console, including their\n        /// key, type, and description. If a type is not specified by the plugin, it will be displayed as \"Not Specified by\n        /// Plugin.\"</remarks>\n        /// <param name=\"filter\">A string used to filter the device factory types by their keys. If the filter is null or empty, all device\n        /// factory types are displayed.</param>\n        public static void GetDeviceFactoryTypes(string filter)\n        {\n            var types = !string.IsNullOrEmpty(filter)\n                ? FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value)\n                : FactoryMethods;\n\n            CrestronConsole.ConsoleCommandResponse(\"Device Types:\");\n\n            foreach (var type in types.OrderBy(t => t.Key))\n            {\n                var description = type.Value.Description;\n                var Type = \"Not Specified by Plugin\";\n\n                if (type.Value.Type != null)\n                {\n                    Type = type.Value.Type.FullName;\n                }\n\n                CrestronConsole.ConsoleCommandResponse(\n                    \"Type: '{0}'\\r\\n\" +\n                    \"                    Type: '{1}'\\r\\n\" +\n                    \"                    Description: {2}{3}\", type.Key, Type, description, CrestronEnvironment.NewLine);\n            }\n        }\n\n        /// <summary>\n        /// Retrieves a dictionary of device factory wrappers, optionally filtered by a specified string.\n        /// </summary>\n        /// <param name=\"filter\">A string used to filter the dictionary keys. Only entries with keys containing the specified filter will be\n        /// included. If <see langword=\"null\"/> or empty, all entries are returned.</param>\n        /// <returns>A dictionary where the keys are strings representing device identifiers and the values are <see\n        /// cref=\"DeviceFactoryWrapper\"/> instances. The dictionary may be empty if no entries match the filter.</returns>\n        public static Dictionary<string, DeviceFactoryWrapper> GetDeviceFactoryDictionary(string filter)\n        {\n            return string.IsNullOrEmpty(filter)\n                ? FactoryMethods\n                : FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Factory/DeviceFactoryWrapper.cs",
    "content": "\n\nusing System;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\n\nnamespace PepperDash.Essentials.Core\n{\n  /// <summary>\n  /// Wraps a device factory, providing metadata and a factory method for creating devices.\n  /// </summary>\n  public class DeviceFactoryWrapper\n  {\n    /// <summary>\n    /// Gets or sets the type associated with the current instance.\n    /// </summary>\n    public Type Type { get; set; }\n\n    /// <summary>\n    /// Gets or sets the description associated with the object.\n    /// </summary>\n    public string Description { get; set; }\n\n    /// <summary>\n    /// Gets or sets the factory method used to create an <see cref=\"IKeyed\"/> instance based on the provided <see\n    /// cref=\"DeviceConfig\"/>.\n    /// </summary>\n    /// <remarks>The factory method allows customization of how <see cref=\"IKeyed\"/> instances are created for\n    /// specific <see cref=\"DeviceConfig\"/> inputs. Ensure the delegate is not null before invoking it.</remarks>\n    public Func<DeviceConfig, IKeyed> FactoryMethod { get; set; }\n\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"DeviceFactoryWrapper\"/> class with default values.\n    /// </summary>\n    /// <remarks>The <see cref=\"Type\"/> property is initialized to <see langword=\"null\"/>, and the <see\n    /// cref=\"Description\"/>  property is set to \"Not Available\".</remarks>\n    public DeviceFactoryWrapper()\n    {\n      Type = null;\n      Description = \"Not Available\";\n    }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Factory/IDeviceFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing PepperDash.Essentials.Core.Config;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IDeviceFactory\n    /// </summary>\n    public interface IDeviceFactory\n    {\n        /// <summary>\n        /// Gets the type of the factory associated with the current instance.\n        /// </summary>\n        Type FactoryType { get; }\n\n        /// <summary>\n        /// Gets a list of type names associated with the current plugin.\n        /// </summary>\n        List<string> TypeNames { get; }\n\n        /// <summary>\n        /// Builds and returns an <see cref=\"EssentialsDevice\"/> instance based on the provided configuration.\n        /// </summary>\n        /// <param name=\"deviceConfig\">The configuration settings used to initialize the device. This parameter cannot be null.</param>\n        /// <returns>An <see cref=\"EssentialsDevice\"/> instance configured according to the specified <paramref\n        /// name=\"deviceConfig\"/>.</returns>\n        EssentialsDevice BuildDevice(DeviceConfig deviceConfig);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Factory/IProcessorExtensionDeviceFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IProcessorExtensionDeviceFactory\n    /// </summary>\n    public interface IProcessorExtensionDeviceFactory\n    {\n        /// <summary>\n        /// Loads all the extension factories to the ProcessorExtensionDeviceFactory\n        /// </summary>\n        void LoadFactories();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Factory/ProcessorExtensionDeviceFactory.cs",
    "content": "﻿\nusing System.Reflection;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace PepperDash.Essentials.Core\n{\n\n    /// <summary>\n    /// Represents a ProcessorExtensionDeviceFactory\n    /// </summary>\n    public class ProcessorExtensionDeviceFactory\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public ProcessorExtensionDeviceFactory() {\n            var assy = Assembly.GetExecutingAssembly();\n            PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy);\n\n            var extensions = assy.GetTypes().Where(ct => typeof(IProcessorExtensionDeviceFactory)\n                .IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);\n\n            if (extensions != null )\n            {\n                foreach ( var extension in extensions )\n                {\n                    try\n                    {\n                        var factory = (IProcessorExtensionDeviceFactory)Activator.CreateInstance(extension);\n                        factory.LoadFactories();\n                    }\n                    catch( Exception e )\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, \"Unable to load extension device: '{1}' ProcessorExtensionDeviceFactory: {0}\", e, extension.Name);\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// A dictionary of factory methods, keyed by config types, added by plugins.\n        /// These methods are looked up and called by GetDevice in this class.\n        /// </summary>\n        static Dictionary<string, DeviceFactoryWrapper> ProcessorExtensionFactoryMethods =\n            new Dictionary<string, DeviceFactoryWrapper>(StringComparer.OrdinalIgnoreCase);\n\n\n        /// <summary>\n        /// Adds a plugin factory method\n        /// </summary>\n        /// <param name=\"extensionName\">name fo extension to add</param>\n        /// <param name=\"method\">method to add</param>\n        /// <returns></returns>\n        public static void AddFactoryForType(string extensionName, Func<DeviceConfig, IKeyed> method)\n        {\n            //Debug.LogMessage(LogEventLevel.Debug, \"Adding factory method for type '{0}'\", typeName);\n            ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method });\n        }\n\n        /// <summary>\n        /// Adds a plugin factory method with type and description\n        /// </summary>\n        /// <param name=\"extensionName\">name of extension to add</param>\n        /// <param name=\"description\">description of extension to add</param>\n        /// <param name=\"Type\">type of extension to add</param>\n        /// <param name=\"method\">method to add</param>\n        public static void AddFactoryForType(string extensionName, string description, Type Type, Func<DeviceConfig, IKeyed> method)\n        {\n            //Debug.LogMessage(LogEventLevel.Debug, \"Adding factory method for type '{0}'\", typeName);\n\n            if (ProcessorExtensionFactoryMethods.ContainsKey(extensionName))\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Unable to add extension device: '{0}'.  Already exists in ProcessorExtensionDeviceFactory\", extensionName);\n                return;\n            }\n\n            var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };\n            ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper);\n        }\n\n        private static void CheckForSecrets(IEnumerable<Newtonsoft.Json.Linq.JProperty> obj)\n        {\n            foreach (var prop in obj.Where(prop => prop.Value as Newtonsoft.Json.Linq.JObject != null))\n            {\n                if (prop.Name.ToLower() == \"secret\")\n                {\n                    var secret = GetSecret(prop.Children().First().ToObject<SecretsPropertiesConfig>());\n                    //var secret = GetSecret(JsonConvert.DeserializeObject<SecretsPropertiesConfig>(prop.Children().First().ToString()));\n                    prop.Parent.Replace(secret);\n                }\n                var recurseProp = prop.Value as Newtonsoft.Json.Linq.JObject;\n                if (recurseProp == null) return;\n                CheckForSecrets(recurseProp.Properties());\n            }\n        }\n\n        private static string GetSecret(SecretsPropertiesConfig data)\n        {\n            var secretProvider = SecretsManager.GetSecretProviderByKey(data.Provider);\n            if (secretProvider == null) return null;\n            var secret = secretProvider.GetSecret(data.Key);\n            if (secret != null) return (string)secret.Value;\n            Debug.LogMessage(LogEventLevel.Debug,\n                \"Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider\",\n                data.Provider, data.Key);\n            return String.Empty;\n        }\n\n        /// <summary>\n        /// The factory method for processor extension devices. Also iterates the Factory methods that have\n        /// been loaded from plugins\n        /// </summary>\n        /// <param name=\"dc\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetExtensionDevice method\n        /// </summary>\n        public static IKeyed GetExtensionDevice(DeviceConfig dc)\n        {\n            try\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Loading '{0}' from Essentials Core\", dc.Type);\n\n                var localDc = new DeviceConfig(dc);\n\n                var key = localDc.Key;\n                var name = localDc.Name;\n                var type = localDc.Type;\n                var properties = localDc.Properties;\n                //var propRecurse = properties;\n\n                var typeName = localDc.Type.ToLower();\n\n                var jObject = properties as Newtonsoft.Json.Linq.JObject;\n                if (jObject != null)\n                {\n                    var jProp = jObject.Properties();\n\n                    CheckForSecrets(jProp);\n                }\n\n                Debug.LogMessage(LogEventLevel.Verbose, \"typeName = {0}\", typeName);\n                // Check for types that have been added by plugin dlls. \n                return !ProcessorExtensionFactoryMethods.ContainsKey(typeName) ? null : ProcessorExtensionFactoryMethods[typeName].FactoryMethod(localDc);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Exception occurred while creating device {0}: {1}\", dc.Key, ex.Message);\n\n                Debug.LogMessage(LogEventLevel.Verbose, \"{0}\", ex.StackTrace);\n\n                if (ex.InnerException == null)\n                {\n                    return null;\n                }\n\n                Debug.LogMessage(LogEventLevel.Information, \"Inner exception while creating device {0}: {1}\", dc.Key,\n                    ex.InnerException.Message);\n                Debug.LogMessage(LogEventLevel.Verbose, \"{0}\", ex.InnerException.StackTrace);\n                return null;\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Factory/ReadyEventArgs.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a IsReadyEventArgs\n    /// </summary>\n    public class IsReadyEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the IsReady\n        /// </summary>\n        public bool IsReady { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"data\">indicates if the object is ready</param>\n        public IsReadyEventArgs(bool data)\n        {\n            IsReady = data;\n        }\n    }\n\n    /// <summary>\n    /// Defines the contract for IHasReady\n    /// </summary>\n    public interface IHasReady\n    {\n        /// <summary>\n        /// Fires when the IsReady property changes\n        /// </summary>\n        event EventHandler<IsReadyEventArgs> IsReadyEvent;\n\n        /// <summary>\n        /// indicates whether the object is ready\n        /// </summary>\n        bool IsReady { get; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/BoolFeedback.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// A Feedback whose output is derived from the return value of a provided Func.\n    /// </summary>\n    public class BoolFeedback : Feedback\n    {\n        /// <summary>\n        /// Returns the current value of the feedback, derived from the ValueFunc. The ValueFunc is \n        /// evaluated whenever FireUpdate() is called\n        /// </summary>\n        public override bool BoolValue { get { return _BoolValue; } }\n        bool _BoolValue;\n\n        /// <summary>\n        /// Fake value to be used in test mode\n        /// </summary>\n        public bool TestValue { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the ValueFunc\n        /// </summary>\n        public Func<bool> ValueFunc { get; private set; }\n\n        List<BoolInputSig> LinkedInputSigs = new List<BoolInputSig>();\n        List<BoolInputSig> LinkedComplementInputSigs = new List<BoolInputSig>();\n\n        List<Crestron.SimplSharpPro.DeviceSupport.Feedback> LinkedCrestronFeedbacks = new List<Crestron.SimplSharpPro.DeviceSupport.Feedback>();\n\n        /// <summary>\n        /// Creates the feedback with the Func as described.\n        /// </summary>\n        /// <remarks>\n        /// While the linked sig value will be updated with the current value stored when it is linked to a EISC Bridge,\n        /// it will NOT reflect an actual value from a device until <seealso cref=\"FireUpdate\"/> has been called\n        /// </remarks>\n        /// <param name=\"valueFunc\">Delegate to invoke when this feedback needs to be updated</param>\n        [Obsolete(\"use constructor with Key parameter. This constructor will be removed in a future version\")]\n        public BoolFeedback(Func<bool> valueFunc)\n            : this(null, valueFunc)\n        {\n        }\n\n        /// <summary>\n        /// Creates the feedback with the Func as described.\n        /// </summary>\n        /// <remarks>\n        /// While the linked sig value will be updated with the current value stored when it is linked to a EISC Bridge,\n        /// it will NOT reflect an actual value from a device until <seealso cref=\"FireUpdate\"/> has been called\n        /// </remarks>\n        /// <param name=\"key\">Key to find this Feedback</param>\n        /// <param name=\"valueFunc\">Delegate to invoke when this feedback needs to be updated</param>\n        public BoolFeedback(string key, Func<bool> valueFunc)\n            : base(key)\n        {\n            ValueFunc = valueFunc;\n        }\n\n        /// <summary>\n        /// Sets the ValueFunc\n        /// </summary>\n        /// <param name=\"newFunc\">New function to set as the ValueFunc</param>\n        public void SetValueFunc(Func<bool> newFunc)\n        {\n            ValueFunc = newFunc;\n        }\n\n        /// <summary>\n        /// FireUpdate method\n        /// </summary>\n        /// <inheritdoc />\n        public override void FireUpdate()\n        {\n            bool newValue = InTestMode ? TestValue : ValueFunc.Invoke();\n            if (newValue != _BoolValue)\n            {\n                _BoolValue = newValue;\n                LinkedInputSigs.ForEach(s => UpdateSig(s));\n                LinkedComplementInputSigs.ForEach(s => UpdateComplementSig(s));\n                OnOutputChange(newValue);\n            }\n        }\n\n        /// <summary>\n        /// Links an input sig\n        /// </summary>\n        /// <param name=\"sig\"></param>\n        /// <summary>\n        /// LinkInputSig method\n        /// </summary>\n        public void LinkInputSig(BoolInputSig sig)\n        {\n            LinkedInputSigs.Add(sig);\n            UpdateSig(sig);\n        }\n\n        /// <summary>\n        /// Unlinks an inputs sig\n        /// </summary>\n        /// <param name=\"sig\"></param>\n        /// <summary>\n        /// UnlinkInputSig method\n        /// </summary>\n        public void UnlinkInputSig(BoolInputSig sig)\n        {\n            LinkedInputSigs.Remove(sig);\n        }\n\n        /// <summary>\n        /// Links an input sig to the complement value\n        /// </summary>\n        /// <param name=\"sig\"></param>\n        public void LinkComplementInputSig(BoolInputSig sig)\n        {\n            LinkedComplementInputSigs.Add(sig);\n            UpdateComplementSig(sig);\n        }\n\n        /// <summary>\n        /// Unlinks an input sig to the complement value\n        /// </summary>\n        /// <param name=\"sig\"></param>\n        /// <summary>\n        /// UnlinkComplementInputSig method\n        /// </summary>\n        public void UnlinkComplementInputSig(BoolInputSig sig)\n        {\n            LinkedComplementInputSigs.Remove(sig);\n        }\n\n        /// <summary>\n        /// Links a Crestron Feedback object\n        /// </summary>\n        /// <param name=\"feedback\"></param>\n        public void LinkCrestronFeedback(Crestron.SimplSharpPro.DeviceSupport.Feedback feedback)\n        {\n            LinkedCrestronFeedbacks.Add(feedback);\n            UpdateCrestronFeedback(feedback);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"feedback\"></param>\n        /// <summary>\n        /// UnlinkCrestronFeedback method\n        /// </summary>\n        public void UnlinkCrestronFeedback(Crestron.SimplSharpPro.DeviceSupport.Feedback feedback)\n        {\n            LinkedCrestronFeedbacks.Remove(feedback);\n        }\n\n        /// <summary>\n        /// ToString override\n        /// </summary>\n        /// <returns></returns>\n        public override string ToString()\n        {\n            return (InTestMode ? \"TEST -- \" : \"\") + BoolValue.ToString();\n        }\n\n        /// <summary>\n        /// Puts this in test mode, sets the test value and fires an update.\n        /// </summary>\n        /// <param name=\"value\"></param>\n        /// <summary>\n        /// SetTestValue method\n        /// </summary>\n        public void SetTestValue(bool value)\n        {\n            TestValue = value;\n            InTestMode = true;\n            FireUpdate();\n        }\n\n        void UpdateSig(BoolInputSig sig)\n        {\n            sig.BoolValue = _BoolValue;\n        }\n\n        void UpdateComplementSig(BoolInputSig sig)\n        {\n            sig.BoolValue = !_BoolValue;\n        }\n\n        void UpdateCrestronFeedback(Crestron.SimplSharpPro.DeviceSupport.Feedback feedback)\n        {\n            feedback.State = _BoolValue;\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/BoolFeedbackOneShot.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Represents a BoolFeedbackPulse\n /// </summary>\n\tpublic class BoolFeedbackPulse\n\t{\n  /// <summary>\n  /// Gets or sets the TimeoutMs\n  /// </summary>\n\t\tpublic uint TimeoutMs { get; set; }\n\n  /// <summary>\n  /// Gets or sets the CanRetrigger\n  /// </summary>\n\t\tpublic bool CanRetrigger { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Feedback\n  /// </summary>\n\t\tpublic BoolFeedback Feedback { get; private set; }\n\t\tCTimer Timer;\n\n\t\tbool _BoolValue;\n\n\t\t/// <summary>\n\t\t/// Creates a non-retriggering one shot\n\t\t/// </summary>\n\t\tpublic BoolFeedbackPulse(uint timeoutMs)\n\t\t\t: this(timeoutMs, false)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Create a retriggerable one shot by setting canRetrigger true\n\t\t/// </summary>\n\t\tpublic BoolFeedbackPulse(uint timeoutMs, bool canRetrigger)\n\t\t{\n\t\t\tTimeoutMs = timeoutMs;\n\t\t\tCanRetrigger = canRetrigger;\n\t\t\tFeedback = new BoolFeedback(() => _BoolValue);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Start method\n\t\t/// </summary>\n\t\tpublic void Start()\n\t\t{\n\t\t\tif (Timer == null)\n\t\t\t{\n\t\t\t\t_BoolValue = true;\n\t\t\t\tFeedback.FireUpdate();\n\t\t\t\tTimer = new CTimer(o =>\n\t\t\t\t\t{\n\t\t\t\t\t\t_BoolValue = false;\n\t\t\t\t\t\tFeedback.FireUpdate();\n\t\t\t\t\t\tTimer = null;\n\t\t\t\t\t}, TimeoutMs);\n\t\t\t}\n\t\t\t// Timer is running, if retrigger is set, reset it.\n\t\t\telse if (CanRetrigger)\n\t\t\t\tTimer.Reset(TimeoutMs);\n\t\t}\n\n  /// <summary>\n  /// Cancel method\n  /// </summary>\n\t\tpublic void Cancel()\n\t\t{\n\t\t\tif(Timer != null)\n\t\t\t\tTimer.Reset(0);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/BoolFeedbackPulseExtender.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// A class that wraps a BoolFeedback with logic that extends it's true state for\n\t/// a time period after the value goes false.\n\t/// </summary>\n\tpublic class BoolFeedbackPulseExtender\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the TimeoutMs\n\t\t/// </summary>\n\t\tpublic uint TimeoutMs { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets the Feedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback Feedback { get; private set; }\n\t\tCTimer Timer;\n\n\t\t/// <summary>\n\t\t/// When set to true, will cause Feedback to go high, and cancel the timer.\n\t\t/// When false, will start the timer, and after timeout, will go low and \n\t\t/// feedback will go low.\n\t\t/// </summary>\n\t\tpublic bool BoolValue\n\t\t{\n\t\t\tget { return _BoolValue; }\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (value)\n\t\t\t\t{\t\t\t// if Timer is running and the value goes high, cancel it.\n\t\t\t\t\tif (Timer != null)\n\t\t\t\t\t{\n\t\t\t\t\t\tTimer.Stop();\n\t\t\t\t\t\tTimer = null;\n\t\t\t\t\t}\n\t\t\t\t\t// if it's already true, don't fire again\n\t\t\t\t\tif (_BoolValue == true)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t_BoolValue = true;\n\t\t\t\t\tFeedback.FireUpdate();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (Timer == null)\n\t\t\t\t\t\tTimer = new CTimer(o => ClearFeedback(), TimeoutMs);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbool _BoolValue;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"timeoutMs\">The time which the true state will be extended after set to false</param>\n\t\tpublic BoolFeedbackPulseExtender(uint timeoutMs)\n\t\t{\n\t\t\tTimeoutMs = timeoutMs;\n\t\t\tFeedback = new BoolFeedback(() => this.BoolValue);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Forces the feedback to false regardless of timeout\n\t\t/// </summary>\n\t\tpublic void ClearNow()\n\t\t{\n\t\t\tif (Timer != null)\n\t\t\t\tTimer.Stop();\n\t\t\tClearFeedback();\n\t\t}\n\n\t\tvoid ClearFeedback()\n\t\t{\n\t\t\t_BoolValue = false;\n\t\t\tFeedback.FireUpdate();\n\t\t\tTimer = null;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/BoolOutputLogicals.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nnamespace PepperDash.Essentials.Core\n{\n\n\t/// <summary>\n\t/// Abstract base class for BoolOutputLogicals\n\t/// </summary>\n\tpublic abstract class BoolFeedbackLogic\n\t{\n  /// <summary>\n  /// Gets or sets the Output\n  /// </summary>\n\t\tpublic BoolFeedback Output { get; private set; }\n\n\t\t/// <summary>\n\t\t/// List of all connected outputs\n\t\t/// </summary>\n\t\tprotected List<BoolFeedback> OutputsIn = new List<BoolFeedback>();\n\n\t\t/// <summary>\n\t\t/// Gets or sets the ComputedValue\n\t\t/// </summary>\n\t\tprotected bool ComputedValue;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t    protected BoolFeedbackLogic()\n\t\t{\n\t\t\tOutput = new BoolFeedback(() => ComputedValue);\n\t\t}\t\n\n\t\t/// <summary>\n\t\t/// AddOutputIn method\n\t\t/// </summary>\n\t\t/// <param name=\"output\">feedback to add</param>\n\t\tpublic void AddOutputIn(BoolFeedback output)\n\t\t{\n\t\t\t// Don't double up outputs\n\t\t\tif(OutputsIn.Contains(output)) return;\n\n\t\t\tOutputsIn.Add(output);\n\t\t\toutput.OutputChange += AnyInput_OutputChange;\n\t\t\tEvaluate();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// AddOutputsIn method\n\t\t/// </summary>\n\t\t/// <param name=\"outputs\">feedbacks to add</param>\n\t\tpublic void AddOutputsIn(List<BoolFeedback> outputs)\n\t\t{\n\t\t    foreach (var o in outputs.Where(o => !OutputsIn.Contains(o)))\n\t\t    {\n\t\t        OutputsIn.Add(o);\n\t\t        o.OutputChange += AnyInput_OutputChange;\n\t\t    }\n\t\t    Evaluate();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// RemoveOutputIn method\n\t\t/// </summary>\n\t\t/// <param name=\"output\">feedback to remove</param>\n\t    public void RemoveOutputIn(BoolFeedback output)\n\t\t{\n\t\t\t// Don't double up outputs\n\t\t\tif (!OutputsIn.Contains(output)) return;\n\n\t\t\tOutputsIn.Remove(output);\n\t\t\toutput.OutputChange -= AnyInput_OutputChange;\n\t\t\tEvaluate();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// RemoveOutputsIn method\n\t\t/// </summary>\n\t\t/// <param name=\"outputs\">feedbacks to remove</param>\n\t\tpublic void RemoveOutputsIn(List<BoolFeedback> outputs)\n\t\t{\n\t\t\tforeach (var o in outputs)\n\t\t\t{\n\t\t\t\tOutputsIn.Remove(o);\n\t\t\t\to.OutputChange -= AnyInput_OutputChange;\n\t\t\t}\n\t\t\tEvaluate();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ClearOutputs method\n\t\t/// </summary>\n\t    public void ClearOutputs()\n\t    {\n\t        OutputsIn.Clear();\n            Evaluate();\n\t    }\n\n\t\t/// <summary>\n\t\t/// AnyInput_OutputChange event handler\n\t\t/// </summary>\n\t\t/// <param name=\"sender\"></param>\n\t\t/// <param name=\"e\"></param>\n\t\tvoid AnyInput_OutputChange(object sender, EventArgs e)\n\t\t{\n\t\t\tEvaluate();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Evaluate method\n\t\t/// </summary>\n\t\tprotected abstract void Evaluate();\n\t}\n\n /// <summary>\n /// Represents a BoolFeedbackAnd\n /// </summary>\n\tpublic class BoolFeedbackAnd : BoolFeedbackLogic\n\t{\n\t\t/// <summary>\n\t\t/// Evaluate method\n\t\t/// </summary>\n\t\tprotected override void Evaluate()\n\t\t{\n\t\t\tvar prevValue = ComputedValue;\n\t\t\tvar newValue = OutputsIn.All(o => o.BoolValue);\n\t\t    if (newValue == prevValue)\n\t\t    {\n\t\t        return;\n\t\t    }\n\t\t    ComputedValue = newValue;\n\t\t    Output.FireUpdate();\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a BoolFeedbackOr\n\t/// </summary>\n\tpublic class BoolFeedbackOr : BoolFeedbackLogic\n\t{\n\t\t/// <summary>\n\t\t/// Evaluate method\n\t\t/// </summary>\n\t\tprotected override void Evaluate()\n\t\t{\n\t\t\tvar prevValue = ComputedValue;\n\t\t\tvar newValue = OutputsIn.Any(o => o.BoolValue);\n\t\t    if (newValue == prevValue)\n\t\t    {\n\t\t        return;\n\t\t    }\n\t\t    ComputedValue = newValue;\n\t\t    Output.FireUpdate();\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a BoolFeedbackLinq\n\t/// </summary>\n\tpublic class BoolFeedbackLinq : BoolFeedbackLogic\n\t{\n\t    readonly Func<IEnumerable<BoolFeedback>, bool> _predicate;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"predicate\"></param>\n\t\tpublic BoolFeedbackLinq(Func<IEnumerable<BoolFeedback>, bool> predicate)\n\t\t\t: base()\n\t\t{\n\t\t\t_predicate = predicate;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Evaluate method\n\t\t/// </summary>\n\t\tprotected override void Evaluate()\n\t\t{\n\t\t\tvar prevValue = ComputedValue;\n\t\t\tvar newValue = _predicate(OutputsIn);\n\t\t    if (newValue == prevValue)\n\t\t    {\n\t\t        return;\n\t\t    }\n\t\t    ComputedValue = newValue;\n\t\t    Output.FireUpdate();\n\t\t} \n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/FeedbackBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Base class for all feedback types\n    /// </summary>\n    public abstract class Feedback : IKeyed\n    {\n        /// <summary>\n        /// Occurs when the output value changes\n        /// </summary>\n        public event EventHandler<FeedbackEventArgs> OutputChange;\n\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the BoolValue\n        /// </summary>\n        /// <inheritdoc />\n        public virtual bool BoolValue { get { return false; } }\n        /// <summary>\n        /// Gets or sets the IntValue\n        /// </summary>\n        public virtual int IntValue { get { return 0; } }\n        /// <summary>\n        /// Gets or sets the StringValue\n        /// </summary>\n        public virtual string StringValue { get { return \"\"; } }\n        /// <summary>\n        /// Gets or sets the SerialValue\n        /// </summary>\n        public virtual string SerialValue { get { return \"\"; } }\n\n        /// <summary>\n        /// Gets or sets the InTestMode\n        /// </summary>\n        public bool InTestMode { get; protected set; }\n\n        /// <summary>\n        /// Base Constructor - empty\n        /// </summary>\n        [Obsolete(\"use constructor with Key parameter. This constructor will be removed in a future version\")]\n        protected Feedback() : this(null) { }\n\n        /// <summary>\n        /// Constructor with Key parameter\n        /// </summary>\n        /// <param name=\"key\">The key for the feedback</param>\n        protected Feedback(string key)\n        {\n            if (key == null)\n                Key = \"\";\n            else\n                Key = key;\n        }\n\n\n\n        /// <summary>\n        /// ClearTestValue method\n        /// </summary>\n        public void ClearTestValue()\n        {\n            InTestMode = false;\n            FireUpdate();\n        }\n\n        /// <summary>\n        /// Fires an update synchronously\n        /// </summary>\n        public abstract void FireUpdate();\n\n        /// <summary>\n        /// Fires the update asynchronously within a CrestronInvoke\n        /// </summary>\n        public void InvokeFireUpdate()\n        {\n            CrestronInvoke.BeginInvoke(o => FireUpdate());\n        }\n\n        /// <summary>\n        /// Helper method that fires event. Use this intstead of calling OutputChange\n        /// </summary>\n        //protected void OnOutputChange()\n        //{\n        //    if (OutputChange != null) OutputChange(this, EventArgs.Empty);\n        //}\n\n        protected void OnOutputChange(bool value)\n        {\n            if (OutputChange != null) OutputChange(this, new FeedbackEventArgs(value));\n        }\n\n        /// <summary>\n        /// Helper method that fires event. Use this intstead of calling OutputChange\n        /// </summary>\n        /// <param name=\"value\">value to seed eventArgs</param>\n        protected void OnOutputChange(int value)\n        {\n            if (OutputChange != null) OutputChange(this, new FeedbackEventArgs(value));\n        }\n\n        /// <summary>\n        /// Helper method that fires event. Use this intstead of calling OutputChange\n        /// </summary>\n        /// <param name=\"value\">value to seed eventArgs</param>\n        protected void OnOutputChange(string value)\n        {\n            if (OutputChange != null) OutputChange(this, new FeedbackEventArgs(value));\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/FeedbackCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Basically a List , with an indexer to find feedbacks by key name\n    /// </summary>\n    public class FeedbackCollection<T> : List<T> where T : Feedback\n    {\n        /// <summary>\n        /// Case-insensitive port lookup linked to feedbacks' keys\n        /// </summary>\n        public T this[string key]\n        {\n            get\n            {\n                return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase));\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/FeedbackEventArgs.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a FeedbackEventArgs\n    /// </summary>\n    public class FeedbackEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the BoolValue\n        /// </summary>\n        public bool BoolValue { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IntValue\n        /// </summary>\n        public int IntValue { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the UShortValue\n        /// </summary>\n        public ushort UShortValue\n        {\n            get\n            {\n                return (ushort)IntValue;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the StringValue\n        /// </summary>\n        public string StringValue { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public eFeedbackEventType Type { get; private set; }\n\n        /// <summary>\n        /// Constructor for BoolValue\n        /// </summary>\n        /// <param name=\"value\">value to set</param>\n        public FeedbackEventArgs(bool value)\n        {\n            BoolValue = value;\n            Type = eFeedbackEventType.TypeBool;\n        }\n\n        /// <summary>\n        /// Constructor for IntValue\n        /// </summary>\n        /// <param name=\"value\">value to set</param>\n        public FeedbackEventArgs(int value)\n        {\n            IntValue = value;\n            Type = eFeedbackEventType.TypeInt;\n        }\n\n        /// <summary>\n        /// Constructor for StringValue\n        /// </summary>\n        /// <param name=\"value\">value to set</param>\n        public FeedbackEventArgs(string value)\n        {\n            StringValue = value;\n            Type = eFeedbackEventType.TypeString;\n        }\n    }\n\n    /// <summary>\n    /// Enumeration of eFeedbackEventType values\n    /// </summary>\n    public enum eFeedbackEventType\n    {\n        /// <summary>\n        /// Boolean type feedback event\n        /// </summary>\n        TypeBool,\n\n        /// <summary>\n        /// Integer type feedback event\n        /// </summary>\n        TypeInt,\n\n        /// <summary>\n        /// String type feedback event\n        /// </summary>\n        TypeString\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/IntFeedback.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a IntFeedback\n    /// </summary>\n    public class IntFeedback : Feedback\n    {\n        /// <summary>\n        /// Gets or sets the IntValue\n        /// </summary>\n        public override int IntValue { get { return _IntValue; } } // ValueFunc.Invoke(); } }\n        int _IntValue;\n        /// <summary>\n        /// Gets or sets the UShortValue\n        /// </summary>\n        public ushort UShortValue { get { return (ushort)_IntValue; } }\n\n        //public override eCueType Type { get { return eCueType.Int; } }\n\n        /// <summary>\n        /// Gets or sets the TestValue\n        /// </summary>\n        public int TestValue { get; private set; }\n\n        /// <summary>\n        /// Func evaluated on FireUpdate\n        /// </summary>\n        Func<int> ValueFunc;\n        List<UShortInputSig> LinkedInputSigs = new List<UShortInputSig>();\n\n        /// <summary>\n        /// Creates the feedback with the Func as described.\n        /// </summary>\n        /// <remarks>\n        /// While the linked sig value will be updated with the current value stored when it is linked to a EISC Bridge,\n        /// it will NOT reflect an actual value from a device until <seealso cref=\"FireUpdate\"/> has been called\n        /// </remarks>\n        /// <param name=\"valueFunc\">Delegate to invoke when this feedback needs to be updated</param>\n        [Obsolete(\"use constructor with Key parameter. This constructor will be removed in a future version\")]\n        public IntFeedback(Func<int> valueFunc)\n            : this(null, valueFunc)\n        {\n        }\n\n        /// <summary>\n        /// Creates the feedback with the Func as described.\n        /// </summary>\n        /// <remarks>\n        /// While the linked sig value will be updated with the current value stored when it is linked to a EISC Bridge,\n        /// it will NOT reflect an actual value from a device until <seealso cref=\"FireUpdate\"/> has been called\n        /// </remarks>\n        /// <param name=\"key\">Key to find this Feedback</param>\n        /// <param name=\"valueFunc\">Delegate to invoke when this feedback needs to be updated</param>\n        public IntFeedback(string key, Func<int> valueFunc)\n            : base(key)\n        {\n            ValueFunc = valueFunc;\n        }\n\n        /// <summary>\n        /// Sets the ValueFunc\n        /// </summary>\n        /// <param name=\"newFunc\">function to set</param>\n        public void SetValueFunc(Func<int> newFunc)\n        {\n            ValueFunc = newFunc;\n        }\n\n\n        /// <summary>\n        /// FireUpdate method\n        /// </summary>\n        /// <inheritdoc />\n        public override void FireUpdate()\n        {\n            var newValue = InTestMode ? TestValue : ValueFunc.Invoke();\n            if (newValue != _IntValue)\n            {\n                _IntValue = newValue;\n                LinkedInputSigs.ForEach(s => UpdateSig(s));\n                OnOutputChange(newValue);\n            }\n        }\n\n        /// <summary>\n        /// LinkInputSig method\n        /// </summary>\n        public void LinkInputSig(UShortInputSig sig)\n        {\n            LinkedInputSigs.Add(sig);\n            UpdateSig(sig);\n        }\n\n        /// <summary>\n        /// UnlinkInputSig method\n        /// </summary>\n        public void UnlinkInputSig(UShortInputSig sig)\n        {\n            LinkedInputSigs.Remove(sig);\n        }\n\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        public override string ToString()\n        {\n            return (InTestMode ? \"TEST -- \" : \"\") + IntValue.ToString();\n        }\n\n        /// <summary>\n        /// Puts this in test mode, sets the test value and fires an update.\n        /// </summary>\n        /// <param name=\"value\"></param>\n        /// <summary>\n        /// SetTestValue method\n        /// </summary>\n        public void SetTestValue(int value)\n        {\n            TestValue = value;\n            InTestMode = true;\n            FireUpdate();\n        }\n\n        void UpdateSig(UShortInputSig sig)\n        {\n            sig.UShortValue = UShortValue;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/SerialFeedback.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// To be used for serial data feedback where the event chain / asynchronicity must be maintained\n    /// and calculating the value based on a Func when it is needed will not suffice.\n    /// </summary>\n    public class SerialFeedback : Feedback\n    {\n        /// <summary>\n        /// Gets the SerialValue\n        /// </summary>\n        public override string SerialValue { get { return _SerialValue; } }\n        string _SerialValue;\n\n        //public override eCueType Type { get { return eCueType.Serial; } }\n\n        /// <summary>\n        /// Gets or sets the TestValue\n        /// </summary>\n        public string TestValue { get; private set; }\n\n        List<StringInputSig> LinkedInputSigs = new List<StringInputSig>();\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        [Obsolete(\"use constructor with Key parameter. This constructor will be removed in a future version\")]\n        public SerialFeedback()\n        {\n        }\n\n        /// <summary>\n        /// Constructor with Key parameter\n        /// </summary>\n        /// <param name=\"key\">Key to find this Feedback</param>\n        public SerialFeedback(string key)\n            : base(key)\n        {\n        }\n\n        /// <summary>\n        /// FireUpdate method\n        /// </summary>\n        /// <inheritdoc />\n        public override void FireUpdate()\n        {\n            throw new NotImplementedException(\"This feedback type does not use Funcs\");\n        }\n\n        /// <summary>\n        /// FireUpdate method\n        /// </summary>\n        public void FireUpdate(string newValue)\n        {\n            _SerialValue = newValue;\n            LinkedInputSigs.ForEach(s => UpdateSig(s, newValue));\n            OnOutputChange(newValue);\n        }\n\n        /// <summary>\n        /// LinkInputSig method\n        /// </summary>\n        public void LinkInputSig(StringInputSig sig)\n        {\n            LinkedInputSigs.Add(sig);\n            UpdateSig(sig);\n        }\n\n        /// <summary>\n        /// UnlinkInputSig method\n        /// </summary>\n        public void UnlinkInputSig(StringInputSig sig)\n        {\n            LinkedInputSigs.Remove(sig);\n        }\n\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        public override string ToString()\n        {\n            return (InTestMode ? \"TEST -- \" : \"\") + SerialValue;\n        }\n\n        /// <summary>\n        /// Puts this in test mode, sets the test value and fires an update.\n        /// </summary>\n        /// <param name=\"value\"></param>\n        /// <summary>\n        /// SetTestValue method\n        /// </summary>\n        public void SetTestValue(string value)\n        {\n            TestValue = value;\n            InTestMode = true;\n            FireUpdate(TestValue);\n        }\n\n        void UpdateSig(StringInputSig sig)\n        {\n            sig.StringValue = _SerialValue;\n        }\n\n        void UpdateSig(StringInputSig sig, string value)\n        {\n            sig.StringValue = value;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Feedbacks/StringFeedback.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nnamespace PepperDash.Essentials.Core\n{\n\n    /// <summary>\n    /// Represents a StringFeedback\n    /// </summary>\n    public class StringFeedback : Feedback\n    {\n        /// <summary>\n        /// Gets or sets the StringValue\n        /// </summary>\n        public override string StringValue { get { return _StringValue; } } // ValueFunc.Invoke(); } }\n        string _StringValue;\n\n        /// <summary>\n        /// Gets or sets the TestValue\n        /// </summary>\n        public string TestValue { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the ValueFunc\n        /// </summary>\n        public Func<string> ValueFunc { get; private set; }\n        List<StringInputSig> LinkedInputSigs = new List<StringInputSig>();\n\n        /// <summary>\n        /// Creates the feedback with the Func as described.\n        /// </summary>\n        /// <remarks>\n        /// While the linked sig value will be updated with the current value stored when it is linked to a EISC Bridge,\n        /// it will NOT reflect an actual value from a device until <seealso cref=\"FireUpdate\"/> has been called\n        /// </remarks>\n        /// <param name=\"valueFunc\">Delegate to invoke when this feedback needs to be updated</param>\n        [Obsolete(\"use constructor with Key parameter. This constructor will be removed in a future version\")]\n        public StringFeedback(Func<string> valueFunc)\n            : this(null, valueFunc)\n        {\n        }\n\n        /// <summary>\n        /// Creates the feedback with the Func as described.\n        /// </summary>\n        /// <remarks>\n        /// While the linked sig value will be updated with the current value stored when it is linked to a EISC Bridge,\n        /// it will NOT reflect an actual value from a device until <seealso cref=\"FireUpdate\"/> has been called\n        /// </remarks>\n        /// <param name=\"key\">Key to find this Feedback</param>\n        /// <param name=\"valueFunc\">Delegate to invoke when this feedback needs to be updated</param>\n        public StringFeedback(string key, Func<string> valueFunc)\n            : base(key)\n        {\n            ValueFunc = valueFunc;\n        }\n\n        /// <summary>\n        /// Sets the ValueFunc\n        /// </summary>\n        /// <param name=\"newFunc\">function to set</param>\n        public void SetValueFunc(Func<string> newFunc)\n        {\n            ValueFunc = newFunc;\n        }\n\n        /// <summary>\n        /// FireUpdate method\n        /// </summary>\n        /// <inheritdoc />\n        public override void FireUpdate()\n        {\n            var newValue = InTestMode ? TestValue : ValueFunc.Invoke();\n            if (newValue != _StringValue)\n            {\n                _StringValue = newValue;\n                LinkedInputSigs.ForEach(s => UpdateSig(s));\n                OnOutputChange(newValue);\n            }\n        }\n\n        /// <summary>\n        /// LinkInputSig method\n        /// </summary>\n        public void LinkInputSig(StringInputSig sig)\n        {\n            LinkedInputSigs.Add(sig);\n            UpdateSig(sig);\n        }\n\n        /// <summary>\n        /// UnlinkInputSig method\n        /// </summary>\n        public void UnlinkInputSig(StringInputSig sig)\n        {\n            LinkedInputSigs.Remove(sig);\n        }\n\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        public override string ToString()\n        {\n            return (InTestMode ? \"TEST -- \" : \"\") + StringValue;\n        }\n\n        /// <summary>\n        /// Puts this in test mode, sets the test value and fires an update.\n        /// </summary>\n        /// <param name=\"value\"></param>\n        /// <summary>\n        /// SetTestValue method\n        /// </summary>\n        public void SetTestValue(string value)\n        {\n            TestValue = value;\n            InTestMode = true;\n            FireUpdate();\n        }\n\n        void UpdateSig(StringInputSig sig)\n        {\n            sig.StringValue = _StringValue;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/File/FileIO.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing PepperDash.Core;\nusing Crestron.SimplSharpPro.CrestronThread;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Static class for FileIO operations\n\t/// </summary>\n\tpublic static class FileIO\n\t{\n\n\t\tstatic CCriticalSection fileLock = new CCriticalSection();\n\t\t/// <summary>\n\t\t/// Delegate for GotFileEventHandler\n\t\t/// </summary>\n\t\t/// <param name=\"sender\"></param>\n\t\t/// <param name=\"e\"></param>\n\t\tpublic delegate void GotFileEventHandler(object sender, FileEventArgs e);\n\n\t\t/// <summary>\n\t\t/// Event for GotFileEvent\n\t\t/// </summary>\n\t\tpublic static event GotFileEventHandler GotFileEvent;\n\n\t\t/// <summary>\n\t\t/// Get the full file info from a path/filename, can include wildcards.\n\t\t/// </summary>\n\t\t/// <param name=\"fileName\"></param>\n\t\t/// <returns></returns>\n        /// <summary>\n        /// GetFiles method\n        /// </summary>\n        public static FileInfo[] GetFiles(string fileName)\n        {\n            string fullFilePath = Global.FilePathPrefix + fileName;\n            DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(fullFilePath));\n            var files = dirInfo.GetFiles(Path.GetFileName(fullFilePath));\n            Debug.LogMessage(LogEventLevel.Information, \"FileIO found: {0}, {1}\", files.Count(), fullFilePath);\n            if (files.Count() > 0)\n            {\n                return files;\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        /// <summary>\n        /// GetFile method\n        /// </summary>\n        public static FileInfo GetFile(string fileName)\n        {\n            string fullFilePath = Global.FilePathPrefix + fileName;\n            DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(fullFilePath));\n            var files = dirInfo.GetFiles(Path.GetFileName(fullFilePath));\n            Debug.LogMessage(LogEventLevel.Information, \"FileIO found: {0}, {1}\", files.Count(), fullFilePath);\n            if (files.Count() > 0)\n            {\n                return files.FirstOrDefault();\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n\n\t\t/// <summary>\n\t\t/// Get the data from string path/filename\n\t\t/// </summary>\n\t\t/// <param name=\"fileName\"></param>\n\t\t/// <returns></returns>\n  /// <summary>\n  /// ReadDataFromFile method\n  /// </summary>\n\t\tpublic static string ReadDataFromFile(string fileName)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\treturn ReadDataFromFile(GetFile(fileName));\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Error: FileIO read failed: \\r{0}\", e);\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\n        /// <summary>\n        /// Get the data with fileInfo object \n        /// </summary>\n        /// <param name=\"file\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// ReadDataFromFile method\n        /// </summary>\n        public static string ReadDataFromFile(FileInfo file)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (fileLock.TryEnter())\n\t\t\t\t{\n\t\t\t\t\tDirectoryInfo dirInfo = new DirectoryInfo(file.DirectoryName);\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, \"FileIO Getting Data {0}\", file.FullName);\n\n\t\t\t\t\tif (File.Exists(file.FullName))\n\t\t\t\t\t{\n\t\t\t\t\t\tusing (StreamReader r = new StreamReader(file.FullName))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn r.ReadToEnd();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, \"File {0} does not exsist\", file.FullName);\n\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"FileIO Unable to enter FileLock\");\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Error: FileIO read failed: \\r{0}\", e);\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tif (fileLock != null && !fileLock.Disposed)\n\t\t\t\t\tfileLock.Leave();\n\n\t\t\t}\n\t\t}\n\n\n  /// <summary>\n  /// ReadDataFromFileASync method\n  /// </summary>\n\t\tpublic static void ReadDataFromFileASync(string fileName)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tReadDataFromFileASync(GetFile(fileName));\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Error: FileIO read failed: \\r{0}\", e);\n\t\t\t}\n\t\t}\n\n  /// <summary>\n  /// ReadDataFromFileASync method\n  /// </summary>\n\t\tpublic static void ReadDataFromFileASync(FileInfo file)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tCrestronInvoke.BeginInvoke(o => _ReadDataFromFileASync(file));\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Error: FileIO read failed: \\r{0}\", e);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void _ReadDataFromFileASync(FileInfo file)\n\t\t{\n\t\t\tstring data;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (fileLock.TryEnter())\n\t\t\t\t{\n\t\t\t\t\tDirectoryInfo dirInfo = new DirectoryInfo(file.Name);\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, \"FileIO Getting Data {0}\", file.FullName);\n\n\n\t\t\t\t\tif (File.Exists(file.FullName))\n\t\t\t\t\t{\n\t\t\t\t\t\tusing (StreamReader r = new StreamReader(file.FullName))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdata = r.ReadToEnd();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, \"File {0} Does not exsist\", file.FullName);\n\t\t\t\t\t\tdata = \"\";\n\t\t\t\t\t}\n\t\t\t\t\tGotFileEvent.Invoke(null, new FileEventArgs(data));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"FileIO Unable to enter FileLock\");\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Error: FileIO read failed: \\r{0}\", e);\n\t\t\t\tdata = \"\";\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tif (fileLock != null && !fileLock.Disposed)\n\t\t\t\t\tfileLock.Leave();\n\n\t\t\t}\n\n\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <param name=\"data\"></param>\n\t\t/// <param name=\"filePath\"></param>\t\t\n\t\tpublic static void WriteDataToFile(string data, string filePath)\n\t\t{\n\t\t\tThread _WriteFileThread;\n            _WriteFileThread = new Thread((O) => _WriteFileMethod(data, Global.FilePathPrefix + \"/\" + filePath), null, Thread.eThreadStartOptions.CreateSuspended);\n\t\t\t_WriteFileThread.Priority = Thread.eThreadPriority.LowestPriority;\n\t\t\t_WriteFileThread.Start();\n\t\t\tDebug.LogMessage(LogEventLevel.Information, \"New WriteFile Thread\");\n\n\t\t}\n\n\t\tstatic object _WriteFileMethod(string data, string filePath)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Attempting to write file: '{0}'\", filePath);\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (fileLock.TryEnter())\n\t\t\t\t{\n\n                    using (StreamWriter sw = new StreamWriter(filePath))\n\t\t\t\t\t{\n\t\t\t\t\t\tsw.Write(data);\n\t\t\t\t\t\tsw.Flush();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"FileIO Unable to enter FileLock\");\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Error, \"Error: FileIO write failed: \\r{0}\", e);\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tif (fileLock != null && !fileLock.Disposed)\n\t\t\t\t\tfileLock.Leave();\n\n\t\t\t}\n\t\t\treturn null;\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\t/// <returns></returns>\n  /// <summary>\n  /// FileIoUnitTest method\n  /// </summary>\n\t\tpublic static bool FileIoUnitTest()\n\t\t{\n\t\t\tvar testData = \"Testing FileIO\";\n\t\t\tFileIO.WriteDataToFile(testData, \"\\\\user\\\\FileIOTest.pdt\");\n\n\t\t\tvar file = FileIO.GetFile(\"\\\\user\\\\*FileIOTest*\");\n\t\t\t\n\t\t\tvar readData = FileIO.ReadDataFromFile(file);\n\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Returned {0}\", readData);\n\t\t\tFile.Delete(file.FullName);\n\t\t\tif (testData == readData)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t}\n /// <summary>\n /// Represents a FileEventArgs\n /// </summary>\n\tpublic class FileEventArgs\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"data\"></param>\n\t\tpublic FileEventArgs(string data) { Data = data; }\n  /// <summary>\n  /// Gets or sets the Data\n  /// </summary>\n\t\tpublic string Data { get; private set; } // readonly\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Essentials.Core.Bridges;\n\n\nnamespace PepperDash.Essentials.Core.Fusion\n{\n    /// <summary>\n    /// Represents a EssentialsHuddleSpaceRoomFusionRoomJoinMap\n    /// </summary>\n    public class EssentialsHuddleSpaceRoomFusionRoomJoinMap : JoinMapBaseAdvanced\n    {\n\n        // Processor Attributes\n        /// <summary>\n        /// Processor IP 1\n        /// </summary>\n        [JoinName(\"ProcessorIp1\")]\n        public JoinDataComplete ProcessorIp1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = \"Info - Processor - IP 1\" },\n            new JoinMetadata { Description = \"Info - Processor - IP 1\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor IP 2\n        /// </summary>\n        [JoinName(\"ProcessorIp2\")]\n        public JoinDataComplete ProcessorIp2 = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1, AttributeName = \"Info - Processor - IP 2\" },\n            new JoinMetadata { Description = \"Info - Processor - IP 2\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Gateway\n        /// </summary>\n        [JoinName(\"ProcessorGateway\")]\n        public JoinDataComplete ProcessorGateway = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1, AttributeName = \"Info - Processor - Gateway\" },\n            new JoinMetadata { Description = \"Info - Processor - Gateway\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Hostname\n        /// </summary>\n        [JoinName(\"ProcessorHostname\")]\n        public JoinDataComplete ProcessorHostname = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1, AttributeName = \"Info - Processor - Hostname\" },\n            new JoinMetadata { Description = \"Info - Processor - Hostname\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Domain\n        /// </summary>\n        [JoinName(\"ProcessorDomain\")]\n        public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1, AttributeName = \"Info - Processor - Domain\" },\n            new JoinMetadata { Description = \"Info - Processor - Domain\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor DNS 1\n        /// </summary>\n        [JoinName(\"ProcessorDns1\")]\n        public JoinDataComplete ProcessorDns1 = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1, AttributeName = \"Info - Processor - DNS 1\" },\n            new JoinMetadata { Description = \"Info - Processor - DNS 1\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor DNS 2\n        /// </summary>\n        [JoinName(\"ProcessorDns2\")]\n        public JoinDataComplete ProcessorDns2 = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1, AttributeName = \"Info - Processor - DNS 2\" },\n            new JoinMetadata { Description = \"Info - Processor - DNS 2\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor MAC 1\n        /// </summary>\n        [JoinName(\"ProcessorMac1\")]\n        public JoinDataComplete ProcessorMac1 = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1, AttributeName = \"Info - Processor - MAC 1\" },\n            new JoinMetadata { Description = \"Info - Processor - MAC 1\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor MAC 2\n        /// </summary>\n        [JoinName(\"ProcessorMac2\")]\n        public JoinDataComplete ProcessorMac2 = new JoinDataComplete(new JoinData { JoinNumber = 58, JoinSpan = 1, AttributeName = \"Info - Processor - MAC 2\" },\n            new JoinMetadata { Description = \"Info - Processor - MAC 2\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Net Mask 1\n        /// </summary>\n        [JoinName(\"ProcessorNetMask1\")]\n        public JoinDataComplete ProcessorNetMask1 = new JoinDataComplete(new JoinData { JoinNumber = 59, JoinSpan = 1, AttributeName = \"Info - Processor - Net Mask 1\" },\n            new JoinMetadata { Description = \"Info - Processor - Net Mask 1\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Net Mask 2\n        /// </summary>\n        [JoinName(\"ProcessorNetMask2\")]\n        public JoinDataComplete ProcessorNetMask2 = new JoinDataComplete(new JoinData { JoinNumber = 60, JoinSpan = 1, AttributeName = \"Info - Processor - Net Mask 2\" },\n            new JoinMetadata { Description = \"Info - Processor - Net Mask 2\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Firmware\n        /// </summary>\n        [JoinName(\"ProcessorFirmware\")]\n        public JoinDataComplete ProcessorFirmware = new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1, AttributeName = \"Info - Processor - Firmware\" },\n            new JoinMetadata { Description = \"Info - Processor - Firmware\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Program Name Start\n        /// </summary>\n        [JoinName(\"ProgramNameStart\")]\n        public JoinDataComplete ProgramNameStart = new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 10, AttributeName = \"Info - Processor - Program\" },\n            new JoinMetadata { Description = \"Info - Processor - Program\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// Processor Reboot\n        /// </summary>\n        [JoinName(\"ProcessorReboot\")]\n        public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1, AttributeName = \"Processor - Reboot\" },\n            new JoinMetadata { Description = \"Processor - Reboot\", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital });\n\n        // Volume Controls\n        /// <summary>\n        /// Volume Fader 1\n        /// </summary>\n        [JoinName(\"VolumeFader1\")]\n        public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = \"Volume - Fader01\" },\n            new JoinMetadata { Description = \"Volume - Fader01\", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog });\n\n        // Codec Info\n        /// <summary>\n        /// VC Codec In Call\n        /// </summary>\n        [JoinName(\"VcCodecInCall\")]\n        public JoinDataComplete VcCodecInCall = new JoinDataComplete(new JoinData { JoinNumber = 69, JoinSpan = 1, AttributeName = \"Conf - VC 1 In Call\" },\n            new JoinMetadata { Description = \"Conf - VC 1 In Call\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// VC Codec Online\n        /// </summary>\n        [JoinName(\"VcCodecOnline\")]\n        public JoinDataComplete VcCodecOnline = new JoinDataComplete(new JoinData { JoinNumber = 122, JoinSpan = 1, AttributeName = \"Online - VC 1\" },\n            new JoinMetadata { Description = \"Online - VC 1\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// VC Codec IP Address\n        /// </summary>\n        [JoinName(\"VcCodecIpAddress\")]\n        public JoinDataComplete VcCodecIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 121, JoinSpan = 1, AttributeName = \"IP Address - VC\" },\n            new JoinMetadata { Description = \"IP Address - VC\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        /// <summary>\n        /// VC Codec IP Port\n        /// </summary>\n        [JoinName(\"VcCodecIpPort\")]\n        public JoinDataComplete VcCodecIpPort = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 1, AttributeName = \"IP Port - VC\" },\n            new JoinMetadata { Description = \"IP Port - VC\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n        // Source Attributes \n        /// <summary>\n        /// Display 1 Current Source Name\n        /// </summary>\n        [JoinName(\"Display1CurrentSourceName\")]\n        public JoinDataComplete Display1CurrentSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1, AttributeName = \"Display 1 - Current Source\" },\n            new JoinMetadata { Description = \"Display 1 - Current Source\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });\n\n\n        // Device Online Status\n        /// <summary>\n        /// Touchpanel Online Start\n        /// </summary>\n        [JoinName(\"TouchpanelOnlineStart\")]\n        public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10, AttributeName = \"Online - Touch Panel\" },\n            new JoinMetadata { Description = \"Online - Touch Panel\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Xpanel Online Start\n        /// </summary>\n        [JoinName(\"XpanelOnlineStart\")]\n        public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 5, AttributeName = \"Online - XPanel\" },\n            new JoinMetadata { Description = \"Online - XPanel\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Display Online Start\n        /// </summary>\n        [JoinName(\"DisplayOnlineStart\")]\n        public JoinDataComplete DisplayOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 170, JoinSpan = 10, AttributeName = \"Online - Display\" },\n            new JoinMetadata { Description = \"Online - Display\", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Display 1 Laptop Source Start\n        /// </summary>\n        [JoinName(\"Display1LaptopSourceStart\")]\n        public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 165, JoinSpan = 5, AttributeName = \"Display 1 - Source Laptop\" },\n            new JoinMetadata { Description = \"Display 1 - Source Laptop\", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });\n        \n        /// <summary>\n        /// Display 1 Disc Player Source Start\n        /// </summary>\n        [JoinName(\"Display1DiscPlayerSourceStart\")]\n        public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 180, JoinSpan = 5, AttributeName = \"Display 1 - Source Disc Player\" },\n            new JoinMetadata { Description = \"Display 1 - Source Disc Player\", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Display 1 Set Top Box Source Start\n        /// </summary>\n        [JoinName(\"Display1SetTopBoxSourceStart\")]\n        public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 185, JoinSpan = 5, AttributeName = \"Display 1 - Source TV\" },\n            new JoinMetadata { Description = \"Display 1 - Source TV\", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });\n\n        // Display 1 \n        /// <summary>\n        /// Display 1 Start\n        /// </summary>\n        [JoinName(\"Display1Start\")]\n        public JoinDataComplete Display1Start = new JoinDataComplete(new JoinData { JoinNumber = 190, JoinSpan = 1 },\n            new JoinMetadata { Description = \"Display 1 Start\", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });\n\n        /// <summary>\n        /// Constructor to use when instantiating this Join Map without inheriting from it\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        public EssentialsHuddleSpaceRoomFusionRoomJoinMap(uint joinStart)\n            : base(joinStart, typeof(EssentialsHuddleSpaceRoomFusionRoomJoinMap))\n        {\n\n        }\n\n        /// <summary>\n        /// Constructor to use when extending this Join map\n        /// </summary>\n        /// <param name=\"joinStart\">Join this join map will start at</param>\n        /// <param name=\"type\">Type of the child join map</param>\n        public EssentialsHuddleSpaceRoomFusionRoomJoinMap(uint joinStart, Type type) : base(joinStart, type)\n        {\n        }\n    }   \n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/FusionCustomPropertiesBridge.cs",
    "content": "﻿\n\nusing System;\nusing System.Linq;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Devices;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Fusion\n{\n    /// <summary>\n    /// Handles mapping Fusion Custom Property values to system properties\n    /// </summary>\n    public class FusionCustomPropertiesBridge\n    {\n\n        /// <summary>\n        /// Evaluates the room info and custom properties from Fusion and updates the system properties aa needed\n        /// </summary>\n        /// <param name=\"room\">The room associated with this Fusion instance</param>\n        /// <param name=\"roomInfo\">The room information from Fusion</param>\n        /// <param name=\"useFusionRoomName\"></param>\n        public void EvaluateRoomInfo(IEssentialsRoom room, RoomInformation roomInfo, bool useFusionRoomName)\n        {\n            try\n            {\n                var reconfigurableDevices = DeviceManager.AllDevices.OfType<ReconfigurableDevice>();\n\n                foreach (var device in reconfigurableDevices)\n                {\n                    // Get the current device config so new values can be overwritten over existing\n                    var deviceConfig = device.Config;\n\n                    if (device is IEssentialsRoom)\n                    {\n                        // Skipping room name as this will affect ALL room instances in the configuration and cause unintended consequences when multiple rooms are present and multiple Fusion instances are used\n                        continue;\n                    }\n\n                    if (device is RoomOnToDefaultSourceWhenOccupied)\n                    {\n                        Debug.LogMessage(LogEventLevel.Debug, \"Mapping Room on via Occupancy values from Fusion\");\n\n                        var devProps = JsonConvert.DeserializeObject<RoomOnToDefaultSourceWhenOccupiedConfig>(deviceConfig.Properties.ToString());\n\n                        var enableFeature = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"EnRoomOnWhenOccupied\"));\n                        if (enableFeature != null)\n                            devProps.EnableRoomOnWhenOccupied = bool.Parse(enableFeature.CustomFieldValue);\n\n                        var enableTime = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"RoomOnWhenOccupiedStartTime\"));\n                        if (enableTime != null)\n                            devProps.OccupancyStartTime = enableTime.CustomFieldValue;\n\n                        var disableTime = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"RoomOnWhenOccupiedEndTime\"));\n                        if (disableTime != null)\n                            devProps.OccupancyEndTime = disableTime.CustomFieldValue;\n\n                        var enableSunday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"EnRoomOnWhenOccupiedSun\"));\n                        if (enableSunday != null)\n                            devProps.EnableSunday = bool.Parse(enableSunday.CustomFieldValue);\n\n                        var enableMonday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"EnRoomOnWhenOccupiedMon\"));\n                        if (enableMonday != null)\n                            devProps.EnableMonday = bool.Parse(enableMonday.CustomFieldValue);\n\n                        var enableTuesday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"EnRoomOnWhenOccupiedTue\"));\n                        if (enableTuesday != null)\n                            devProps.EnableTuesday = bool.Parse(enableTuesday.CustomFieldValue);\n\n                        var enableWednesday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"EnRoomOnWhenOccupiedWed\"));\n                        if (enableWednesday != null)\n                            devProps.EnableWednesday = bool.Parse(enableWednesday.CustomFieldValue);\n\n                        var enableThursday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"EnRoomOnWhenOccupiedThu\"));\n                        if (enableThursday != null)\n                            devProps.EnableThursday = bool.Parse(enableThursday.CustomFieldValue);\n\n                        var enableFriday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"EnRoomOnWhenOccupiedFri\"));\n                        if (enableFriday != null)\n                            devProps.EnableFriday = bool.Parse(enableFriday.CustomFieldValue);\n\n                        var enableSaturday = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"EnRoomOnWhenOccupiedSat\"));\n                        if (enableSaturday != null)\n                            devProps.EnableSaturday = bool.Parse(enableSaturday.CustomFieldValue);\n\n                        deviceConfig.Properties = JToken.FromObject(devProps);\n                    }\n\n                    // Set the config on the device\n                    device.SetConfig(deviceConfig);\n                }\n\n                if (!(room is ReconfigurableDevice reconfigurable))\n                {\n                    Debug.LogWarning(\"FusionCustomPropertiesBridge: Room is not a ReconfigurableDevice. Cannot map custom properties.\");\n                    return;\n                }\n\n                var roomConfig = reconfigurable.Config;\n\n                var updateConfig = false;\n\n                // Set the room name\n                if (!string.IsNullOrEmpty(roomInfo.Name) && useFusionRoomName)\n                {\n                    Debug.LogDebug(\"Current Room Name: {currentName}. New Room Name: {fusionName}\", roomConfig.Name, roomInfo.Name);\n                    // Set the name in config\n                    roomConfig.Name = roomInfo.Name;\n                    updateConfig = true;\n\n                    Debug.LogDebug(\"Room Name Successfully Changed.\");\n                }\n\n                // Set the help message\n                var helpMessage = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals(\"RoomHelpMessage\"));\n                if (helpMessage != null)\n                {\n                    roomConfig.Properties[\"helpMessage\"] = helpMessage.CustomFieldValue;\n                    updateConfig = true;\n                }\n\n                if (updateConfig)\n                {\n                    reconfigurable.SetConfig(roomConfig);\n                }\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(\"FusionCustomPropetiesBridge: Exception mapping properties for {roomKey}: {message}\", room.Key, e.Message);\n                Debug.LogDebug(e, \"Stack Trace: \");\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/FusionEventHandlers.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core.Fusion\n{\n    /// <summary>\n    /// Represents a ScheduleChangeEventArgs\n    /// </summary>\n    public class ScheduleChangeEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the Schedule\n        /// </summary>\n        public RoomSchedule Schedule { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a MeetingChangeEventArgs\n    /// </summary>\n    public class MeetingChangeEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the Meeting\n        /// </summary>\n        public Event Meeting { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/FusionProcessorQueries.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Fusion\n{\n    /// <summary>\n    /// When created, runs progcomments on every slot and stores the program names in a list\n    /// </summary>\n    public class ProcessorProgReg\n    {\n        //public static Dictionary<int, ProcessorProgramItem> Programs { get; private set; }\n\n        /// <summary>\n        /// Gets the processor program registry\n        /// </summary>\n        /// <returns></returns>\n        public static Dictionary<int, ProcessorProgramItem> GetProcessorProgReg()\n        {\n            var programs = new Dictionary<int, ProcessorProgramItem>();\n            for (int i = 1; i <= Global.ControlSystem.NumProgramsSupported; i++)\n            {\n                string response = null;\n                var success = CrestronConsole.SendControlSystemCommand(\"progcomments:\" + i, ref response);\n                var item = new ProcessorProgramItem();\n                if (!success)\n                    item.Name = \"Error: PROGCOMMENTS failed\";\n                else\n                {\n                    if (response.ToLower().Contains(\"bad or incomplete\"))\n                        item.Name = \"\";\n                    else\n                    {\n                        var startPos = response.IndexOf(\"Program File\");\n                        var colonPos = response.IndexOf(\":\", startPos) + 1;\n                        var endPos = response.IndexOf(CrestronEnvironment.NewLine, colonPos);\n                        item.Name = response.Substring(colonPos, endPos - colonPos).Trim();\n                        item.Exists = true;\n                        if (item.Name.Contains(\".dll\"))\n                        {\n                            startPos = response.IndexOf(\"Compiler Revision\");\n                            colonPos = response.IndexOf(\":\", startPos) + 1;\n                            endPos = response.IndexOf(CrestronEnvironment.NewLine, colonPos);\n                            item.Name = item.Name + \"_v\" + response.Substring(colonPos, endPos - colonPos).Trim();\n                        }\n                    }\n                }\n                programs[i] = item;\n                Debug.LogMessage(LogEventLevel.Debug, \"Program {0}: {1}\", i, item.Name);\n            }\n            return programs;\n        }\n    }\n\n    /// <summary>\n    /// Represents a ProcessorProgramItem\n    /// </summary>\n    public class ProcessorProgramItem\n    {\n        /// <summary>\n        /// Gets or sets the Exists\n        /// </summary>\n        public bool Exists { get; set; }\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/FusionRviDataClasses.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.Fusion;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Fusion\n{\n    // Helper Classes for GUIDs\n\n    /// <summary>\n    /// Stores GUIDs to be written to a file in NVRAM \n    /// </summary>\n    public class FusionRoomGuids\n    {\n        /// <summary>\n        /// Gets or sets the RoomName\n        /// </summary>\n        public string RoomName { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IpId\n        /// </summary>\n        public uint IpId { get; set; }\n\n        /// <summary>\n        /// Gets or sets the RoomGuid\n        /// </summary>\n        public string RoomGuid { get; set; }\n\n        /// <summary>\n        /// Gets or sets the OccupancyAsset\n        /// </summary>\n        public FusionOccupancySensorAsset OccupancyAsset { get; set; }\n\n        /// <summary>\n        /// Gets or sets the StaticAssets\n        /// </summary>\n        public Dictionary<int, FusionAsset> StaticAssets { get; set; }\n\n        /// <summary>\n        /// FusionRoomGuids constructor\n        /// </summary>\n        public FusionRoomGuids()\n        {\n            StaticAssets = new Dictionary<int, FusionAsset>();\n            OccupancyAsset = new FusionOccupancySensorAsset();\n        }\n\n        /// <summary>\n        /// FusionRoomGuids constructor\n        /// </summary>\n        /// <param name=\"roomName\">name of the fusion room</param>\n        /// <param name=\"ipId\">ipID of the fusion room</param>\n        /// <param name=\"roomGuid\">room GUID</param>\n        /// <param name=\"staticAssets\">dictionary of assets</param>\n        public FusionRoomGuids(string roomName, uint ipId, string roomGuid, Dictionary<int, FusionAsset> staticAssets)\n        {\n            RoomName = roomName;\n            IpId = ipId;\n            RoomGuid = roomGuid;\n\n            StaticAssets = staticAssets;\n            OccupancyAsset = new FusionOccupancySensorAsset();\n        }\n\n        /// <summary>\n        /// FusionRoomGuids constructor\n        /// </summary>\n        /// <param name=\"roomName\">name of the fusion room</param>\n        /// <param name=\"ipId\">ipID of the fusion room</param>\n        /// <param name=\"roomGuid\">room GUID</param>\n        /// <param name=\"staticAssets\">dictionary of assets</param>\n        /// <param name=\"occAsset\">occupancy asset</param>\n        public FusionRoomGuids(string roomName, uint ipId, string roomGuid, Dictionary<int, FusionAsset> staticAssets, FusionOccupancySensorAsset occAsset)\n        {\n            RoomName = roomName;\n            IpId = ipId;\n            RoomGuid = roomGuid;\n\n            StaticAssets = staticAssets;\n            OccupancyAsset = occAsset;\n        }\n\n        /// <summary>\n        /// Generates a new room GUID prefixed by the program slot number and NIC MAC address\n        /// </summary>\n        /// <param name=\"progSlot\"></param>\n        /// <param name=\"mac\"></param>\n        /// <summary>\n        /// GenerateNewRoomGuid method\n        /// </summary>\n        public string GenerateNewRoomGuid(uint progSlot, string mac)\n        {\n            Guid roomGuid = Guid.NewGuid();\n\n            return string.Format(\"{0}-{1}-{2}\", progSlot, mac, roomGuid.ToString());\n        }\n\n\n        /// <summary>\n        /// Adds an asset to the StaticAssets collection and returns the new asset\n        /// </summary>\n        /// <param name=\"room\"></param>\n        /// <param name=\"uid\"></param>\n        /// <param name=\"assetName\"></param>\n        /// <param name=\"type\"></param>\n        /// <param name=\"instanceId\"></param>\n        /// <returns></returns>\n        public FusionAsset AddStaticAsset(FusionRoom room, int uid, string assetName, string type, string instanceId)\n        {\n            var slotNum = GetNextAvailableAssetNumber(room);\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Adding Fusion Asset: {0} of Type: {1} at Slot Number: {2} with GUID: {3}\", assetName, type, slotNum, instanceId);\n\n            var tempAsset = new FusionAsset(slotNum, assetName, type, instanceId);\n\n            StaticAssets.Add(uid, tempAsset);\n\n            return tempAsset;\n        }\n\n        /// <summary>\n        /// Returns the next available slot number in the Fusion UserConfigurableAssetDetails collection\n        /// </summary>\n        /// <param name=\"room\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetNextAvailableAssetNumber method\n        /// </summary>\n        public static uint GetNextAvailableAssetNumber(FusionRoom room)\n        {\n            uint slotNum = 0;\n\n            foreach (var item in room.UserConfigurableAssetDetails)\n            {\n                if(item.Number > slotNum)\n                    slotNum = item.Number;\n            }\n\n            if (slotNum < 5)\n            {\n                slotNum = 5;\n            }\n            else\n                slotNum = slotNum + 1;\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"#Next available fusion asset number is: {0}\", slotNum);\n\n            return slotNum;\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a FusionOccupancySensorAsset\n    /// </summary>\n    public class FusionOccupancySensorAsset\n    {\n        // SlotNumber fixed at 4\n\n        /// <summary>\n        /// Gets or sets the SlotNumber\n        /// </summary>\n        public uint SlotNumber { get { return 4; } }\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get { return \"Occupancy Sensor\"; } }\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public eAssetType Type { get; set; }\n        /// <summary>\n        /// Gets or sets the InstanceId\n        /// </summary>\n        public string InstanceId { get; set; }\n\n        /// <summary>\n        /// Default constructor\n        /// </summary>\n        public FusionOccupancySensorAsset()\n        {\n        }\n\n        /// <summary>\n        /// FusionOccupancySensorAsset constructor\n        /// </summary>\n        /// <param name=\"type\">asset type</param>\n        public FusionOccupancySensorAsset(eAssetType type)\n        {\n            Type = type;\n\n            InstanceId = Guid.NewGuid().ToString();\n        }\n    }\n\n    /// <summary>\n    /// Represents a FusionAsset\n    /// </summary>\n    public class FusionAsset\n    {\n        /// <summary>\n        /// Gets or sets the SlotNumber\n        /// </summary>\n        public uint SlotNumber { get; set; }\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; set; }\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public string Type { get;  set; }\n        /// <summary>\n        /// Gets or sets the InstanceId\n        /// </summary>\n        public string InstanceId { get;set; }\n\n        /// <summary>\n        /// Default constructor\n        /// </summary>\n        public FusionAsset()\n        {\n\n        }\n\n        /// <summary>\n        /// FusionAsset constructor\n        /// </summary>\n        /// <param name=\"slotNum\">slot number of asset</param>\n        /// <param name=\"assetName\">name of the asset</param>\n        /// <param name=\"type\">type of the asset</param>\n        /// <param name=\"instanceId\">instance ID of the asset</param>\n        public FusionAsset(uint slotNum, string assetName, string type, string instanceId)\n        {\n            SlotNumber = slotNum;\n            Name = assetName;\n            Type = type;\n            if (string.IsNullOrEmpty(instanceId))\n            {\n                InstanceId = Guid.NewGuid().ToString();\n            }\n            else\n            {\n                InstanceId = instanceId;\n            }\n        }\n    }\n\n    //***************************************************************************************************\n\n    /// <summary>\n    /// Represents a RoomSchedule\n    /// </summary>\n    public class RoomSchedule\n    {\n        /// <summary>\n        /// Gets or sets the Meetings\n        /// </summary>\n        public List<Event> Meetings { get; set; }\n\n        /// <summary>\n        /// RoomSchedule constructor\n        /// </summary>\n        public RoomSchedule()\n        {\n            Meetings = new List<Event>();\n        }\n    }\n\n    //****************************************************************************************************\n    // Helper Classes for XML API\n\n    /// <summary>\n    /// Represents a LocalTimeRequest\n    /// </summary>\n    public class LocalTimeRequest\n    {\n        /// <summary>\n        /// Gets or sets the RequestID\n        /// </summary>\n        public string RequestID { get; set; }\n    }\n\n    /// <summary>\n    /// All the data needed for a full schedule request in a room\n    /// </summary>\n    /// //[XmlRoot(ElementName = \"RequestSchedule\")]\n    public class RequestSchedule\n    {\n        //[XmlElement(ElementName = \"RequestID\")]\n        /// <summary>\n        /// Gets or sets the RequestID\n        /// </summary>\n        public string RequestID { get; set; }\n\n        //[XmlElement(ElementName = \"RoomID\")]\n        /// <summary>\n        /// Gets or sets the RoomID\n        /// </summary>\n        public string RoomID { get; set; }\n\n        //[XmlElement(ElementName = \"Start\")]\n        /// <summary>\n        /// Gets or sets the Start\n        /// </summary>\n        public DateTime Start { get; set; }\n\n        //[XmlElement(ElementName = \"HourSpan\")]\n        /// <summary>\n        /// Gets or sets the HourSpan\n        /// </summary>\n        public double HourSpan { get; set; }\n\n        /// <summary>\n        /// RequestSchedule constructor\n        /// </summary>\n        /// <param name=\"requestID\">id of the request</param>\n        /// <param name=\"roomID\">id of the room</param>\n        public RequestSchedule(string requestID, string roomID)\n        {\n            RequestID = requestID;\n            RoomID = roomID;\n            Start = DateTime.Now;\n            HourSpan = 24;\n        }\n    }\n\n\n    //[XmlRoot(ElementName = \"RequestAction\")]\n    /// <summary>\n    /// Represents a RequestAction\n    /// </summary>\n    public class RequestAction\n    {\n        //[XmlElement(ElementName = \"RequestID\")]\n        /// <summary>\n        /// Gets or sets the RequestID\n        /// </summary>\n        public string RequestID { get; set; }\n        //[XmlElement(ElementName = \"RoomID\")]\n        /// <summary>\n        /// Gets or sets the RoomID\n        /// </summary>\n        public string RoomID { get; set; }\n        //[XmlElement(ElementName = \"ActionID\")]\n        /// <summary>\n        /// Gets or sets the ActionID\n        /// </summary>\n        public string ActionID { get; set; }\n        //[XmlElement(ElementName = \"Parameters\")]\n        /// <summary>\n        /// Gets or sets the Parameters\n        /// </summary>\n        public List<Parameter> Parameters { get; set; }\n\n        /// <summary>\n        /// RequestAction constructor\n        /// </summary>\n        /// <param name=\"roomID\">id of the room</param>\n        /// <param name=\"actionID\">id of the action</param>\n        /// <param name=\"parameters\">list of parameters</param>\n        public RequestAction(string roomID, string actionID, List<Parameter> parameters)\n        {\n            RoomID = roomID;\n            ActionID = actionID;\n            Parameters = parameters;\n        }\n    }\n\n    //[XmlRoot(ElementName = \"ActionResponse\")]\n    /// <summary>\n    /// Represents a ActionResponse\n    /// </summary>\n    public class ActionResponse\n    {\n        //[XmlElement(ElementName = \"RequestID\")]\n        /// <summary>\n        /// Gets or sets the RequestID\n        /// </summary>\n        public string RequestID { get; set; }\n\n        //[XmlElement(ElementName = \"ActionID\")]\n        /// <summary>\n        /// Gets or sets the ActionID\n        /// </summary>\n        public string ActionID { get; set; }\n\n        //[XmlElement(ElementName = \"Parameters\")]\n        /// <summary>\n        /// Gets or sets the Parameters\n        /// </summary>\n        public List<Parameter> Parameters { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"Parameter\")]\n    /// <summary>\n    /// Represents a Parameter\n    /// </summary>\n    public class Parameter\n    {\n        //[XmlAttribute(AttributeName = \"ID\")]\n        /// <summary>\n        /// Gets or sets the ID\n        /// </summary>\n        public string ID { get; set; }\n\n        //[XmlAttribute(AttributeName = \"Value\")]\n        /// <summary>\n        /// Gets or sets the Value\n        /// </summary>\n        public string Value { get; set; }\n    }\n\n    ////[XmlRoot(ElementName = \"Parameters\")]\n    //public class Parameters\n    //{\n    //    //[XmlElement(ElementName = \"Parameter\")]\n    //    public List<Parameter> Parameter { get; set; }\n    //}  \n\n    /// <summary>\n    /// Data structure for a ScheduleResponse from Fusion\n    /// </summary>\n    /// //[XmlRoot(ElementName = \"ScheduleResponse\")]\n    public class ScheduleResponse\n    {\n        //[XmlElement(ElementName = \"RequestID\")]\n        /// <summary>\n        /// Gets or sets the RequestID\n        /// </summary>\n        public string RequestID { get; set; }\n\n        //[XmlElement(ElementName = \"RoomID\")]\n        /// <summary>\n        /// Gets or sets the RoomID\n        /// </summary>\n        public string RoomID { get; set; }\n\n        //[XmlElement(ElementName = \"RoomName\")]\n        /// <summary>\n        /// Gets or sets the RoomName\n        /// </summary>\n        public string RoomName { get; set; }\n\n        //[XmlElement(\"Event\")]\n        /// <summary>\n        /// Gets or sets the Events\n        /// </summary>\n        public List<Event> Events { get; set; }\n\n        /// <summary>\n        /// ScheduleResponse constructor\n        /// </summary>\n        public ScheduleResponse()\n        {\n            Events = new List<Event>();\n        }\n    }\n\n    //[XmlRoot(ElementName = \"Event\")]\n    /// <summary>\n    /// Represents a Event\n    /// </summary>\n    public class Event\n    {\n        //[XmlElement(ElementName = \"MeetingID\")]\n        /// <summary>\n        /// Gets or sets the MeetingID\n        /// </summary>\n        public string MeetingID { get; set; }\n        //[XmlElement(ElementName = \"RVMeetingID\")]\n        /// <summary>\n        /// Gets or sets the RVMeetingID\n        /// </summary>\n        public string RVMeetingID { get; set; }\n        //[XmlElement(ElementName = \"Recurring\")]\n        /// <summary>\n        /// Gets or sets the Recurring\n        /// </summary>\n        public string Recurring { get; set; }\n        //[XmlElement(ElementName = \"InstanceID\")]\n        /// <summary>\n        /// Gets or sets the InstanceID\n        /// </summary>\n        public string InstanceID { get; set; }\n        //[XmlElement(ElementName = \"dtStart\")]\n        /// <summary>\n        /// Gets or sets the dtStart\n        /// </summary>\n        public DateTime dtStart { get; set; }\n        //[XmlElement(ElementName = \"dtEnd\")]\n        /// <summary>\n        /// Gets or sets the dtEnd\n        /// </summary>\n        public DateTime dtEnd { get; set; }\n        //[XmlElement(ElementName = \"Organizer\")]\n        /// <summary>\n        /// Gets or sets the Organizer\n        /// </summary>\n        public string Organizer { get; set; }\n        //[XmlElement(ElementName = \"Attendees\")]\n        /// <summary>\n        /// Gets or sets the Attendees\n        /// </summary>\n        public Attendees Attendees { get; set; }\n        //[XmlElement(ElementName = \"Resources\")]\n        /// <summary>\n        /// Gets or sets the Resources\n        /// </summary>\n        public Resources Resources { get; set; }\n        //[XmlElement(ElementName = \"IsEvent\")]\n        /// <summary>\n        /// Gets or sets the IsEvent\n        /// </summary>\n        public string IsEvent { get; set; }\n        //[XmlElement(ElementName = \"IsRoomViewMeeting\")]\n        /// <summary>\n        /// Gets or sets the IsRoomViewMeeting\n        /// </summary>\n        public string IsRoomViewMeeting { get; set; }\n        //[XmlElement(ElementName = \"IsPrivate\")]\n        /// <summary>\n        /// Gets or sets the IsPrivate\n        /// </summary>\n        public string IsPrivate { get; set; }\n        //[XmlElement(ElementName = \"IsExchangePrivate\")]\n        /// <summary>\n        /// Gets or sets the IsExchangePrivate\n        /// </summary>\n        public string IsExchangePrivate { get; set; }\n        //[XmlElement(ElementName = \"MeetingTypes\")]\n        /// <summary>\n        /// Gets or sets the MeetingTypes\n        /// </summary>\n        public MeetingTypes MeetingTypes { get; set; }\n        //[XmlElement(ElementName = \"ParticipantCode\")]\n        /// <summary>\n        /// Gets or sets the ParticipantCode\n        /// </summary>\n        public string ParticipantCode { get; set; }\n        //[XmlElement(ElementName = \"PhoneNo\")]\n        /// <summary>\n        /// Gets or sets the PhoneNo\n        /// </summary>\n        public string PhoneNo { get; set; }\n        //[XmlElement(ElementName = \"WelcomeMsg\")]\n        /// <summary>\n        /// Gets or sets the WelcomeMsg\n        /// </summary>\n        public string WelcomeMsg { get; set; }\n        //[XmlElement(ElementName = \"Subject\")]\n        /// <summary>\n        /// Gets or sets the Subject\n        /// </summary>\n        public string Subject { get; set; }\n        //[XmlElement(ElementName = \"LiveMeeting\")]\n        /// <summary>\n        /// Gets or sets the LiveMeeting\n        /// </summary>\n        public LiveMeeting LiveMeeting { get; set; }\n        //[XmlElement(ElementName = \"ShareDocPath\")]\n        /// <summary>\n        /// Gets or sets the ShareDocPath\n        /// </summary>\n        public string ShareDocPath { get; set; }\n        //[XmlElement(ElementName = \"HaveAttendees\")]\n        /// <summary>\n        /// Gets or sets the HaveAttendees\n        /// </summary>\n        public string HaveAttendees { get; set; }\n        //[XmlElement(ElementName = \"HaveResources\")]\n        /// <summary>\n        /// Gets or sets the HaveResources\n        /// </summary>\n        public string HaveResources { get; set; }\n\n        /// <summary>\n        /// Gets the duration of the meeting\n        /// </summary>\n        public string DurationInMinutes\n        {\n            get\n            {\n                string duration;\n\n                var timeSpan = dtEnd.Subtract(dtStart);\n                int hours = timeSpan.Hours;\n                double minutes = timeSpan.Minutes;\n                double roundedMinutes = Math.Round(minutes);\n                if (hours > 0)\n                {\n                    duration = string.Format(\"{0} hours {1} minutes\", hours, roundedMinutes);\n                }\n                else\n                {\n                    duration = string.Format(\"{0} minutes\", roundedMinutes);\n                }\n\n                return duration;\n            }\n        }\n\n        /// <summary>\n        /// Gets the remaining time in the meeting.  Returns null if the meeting is not currently in progress.\n        /// </summary>\n        public string RemainingTime\n        {\n            get\n            {\n                var now = DateTime.Now;\n\n                string remainingTime;\n\n                if (GetInProgress())\n                {\n                    var timeSpan = dtEnd.Subtract(now);\n                    int hours = timeSpan.Hours;\n                    double minutes = timeSpan.Minutes;\n                    double roundedMinutes = Math.Round(minutes);\n                    if (hours > 0)\n                    {\n                        remainingTime = string.Format(\"{0} hours {1} minutes\", hours, roundedMinutes);\n                    }\n                    else\n                    {\n                        remainingTime = string.Format(\"{0} minutes\", roundedMinutes);\n                    }\n\n                    return remainingTime;\n                }\n                else\n                    return null;\n            }\n\n        }\n\n        /// <summary>\n        /// Indicates that the meeting is in progress\n        /// </summary>\n        public bool isInProgress\n        {\n            get\n            {\n                return GetInProgress();\n            }\n        }\n\n        /// <summary>\n        /// Determines if the meeting is in progress\n        /// </summary>\n        /// <returns>Returns true if in progress</returns>\n        bool GetInProgress()\n        {\n            var now = DateTime.Now;\n\n            if (now > dtStart && now < dtEnd)\n            {\n                return true;\n            }\n            else\n                return false;\n        }\n    }\n\n    //[XmlRoot(ElementName = \"Resources\")]\n    /// <summary>\n    /// Represents a Resources\n    /// </summary>\n    public class Resources\n    {\n        //[XmlElement(ElementName = \"Rooms\")]\n        /// <summary>\n        /// Gets or sets the Rooms\n        /// </summary>\n        public Rooms Rooms { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"Rooms\")]\n    /// <summary>\n    /// Represents a Rooms\n    /// </summary>\n    public class Rooms\n    {\n        //[XmlElement(ElementName = \"Room\")]\n        /// <summary>\n        /// Gets or sets the Room\n        /// </summary>\n        public List<Room> Room { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"Room\")]\n    /// <summary>\n    /// Represents a Room\n    /// </summary>\n    public class Room\n    {\n        //[XmlElement(ElementName = \"Name\")]\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; set; }\n        //[XmlElement(ElementName = \"ID\")]\n        /// <summary>\n        /// Gets or sets the ID\n        /// </summary>\n        public string ID { get; set; }\n        //[XmlElement(ElementName = \"MPType\")]\n        /// <summary>\n        /// Gets or sets the MPType\n        /// </summary>\n        public string MPType { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"Attendees\")]\n    /// <summary>\n    /// Represents a Attendees\n    /// </summary>\n    public class Attendees\n    {\n        //[XmlElement(ElementName = \"Required\")]\n        /// <summary>\n        /// Gets or sets the Required\n        /// </summary>\n        public Required Required { get; set; }\n        //[XmlElement(ElementName = \"Optional\")]\n        /// <summary>\n        /// Gets or sets the Optional\n        /// </summary>\n        public Optional Optional { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"Required\")]\n    /// <summary>\n    /// Represents a Required\n    /// </summary>\n    public class Required\n    {\n        //[XmlElement(ElementName = \"Attendee\")]\n        /// <summary>\n        /// Gets or sets the Attendee\n        /// </summary>\n        public List<string> Attendee { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"Optional\")]\n    /// <summary>\n    /// Represents a Optional\n    /// </summary>\n    public class Optional\n    {\n        //[XmlElement(ElementName = \"Attendee\")]\n        /// <summary>\n        /// Gets or sets the Attendee\n        /// </summary>\n        public List<string> Attendee { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"MeetingType\")]\n    /// <summary>\n    /// Represents a MeetingType\n    /// </summary>\n    public class MeetingType\n    {\n        //[XmlAttribute(AttributeName = \"ID\")]\n        /// <summary>\n        /// Gets or sets the ID\n        /// </summary>\n        public string ID { get; set; }\n        //[XmlAttribute(AttributeName = \"Value\")]\n        /// <summary>\n        /// Gets or sets the Value\n        /// </summary>\n        public string Value { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"MeetingTypes\")]\n    /// <summary>\n    /// Represents a MeetingTypes\n    /// </summary>\n    public class MeetingTypes\n    {\n        //[XmlElement(ElementName = \"MeetingType\")]\n        /// <summary>\n        /// Gets or sets the MeetingType\n        /// </summary>\n        public List<MeetingType> MeetingType { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"LiveMeeting\")]\n    /// <summary>\n    /// Represents a LiveMeeting\n    /// </summary>\n    public class LiveMeeting\n    {\n        //[XmlElement(ElementName = \"URL\")]\n        /// <summary>\n        /// Gets or sets the URL\n        /// </summary>\n        public string URL { get; set; }\n        //[XmlElement(ElementName = \"ID\")]\n        /// <summary>\n        /// Gets or sets the ID\n        /// </summary>\n        public string ID { get; set; }\n        //[XmlElement(ElementName = \"Key\")]\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; set; }\n        //[XmlElement(ElementName = \"Subject\")]\n        /// <summary>\n        /// Gets or sets the Subject\n        /// </summary>\n        public string Subject { get; set; }\n    }\n\n    //[XmlRoot(ElementName = \"LiveMeetingURL\")]\n    /// <summary>\n    /// Represents a LiveMeetingURL\n    /// </summary>\n    public class LiveMeetingURL\n    {\n        //[XmlElement(ElementName = \"LiveMeeting\")]\n        /// <summary>\n        /// Gets or sets the LiveMeeting\n        /// </summary>\n        public LiveMeeting LiveMeeting { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Timers;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharp.CrestronXml;\nusing Crestron.SimplSharp.CrestronXml.Serialization;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.Fusion;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Fusion\n{\n    /// <summary>\n    /// Represents a EssentialsHuddleSpaceFusionSystemControllerBase\n    /// </summary>\n    public class IEssentialsRoomFusionController : EssentialsDevice, IOccupancyStatusProvider, IFusionHelpRequest, IHasFeedback\n    {\n        private IEssentialsRoomFusionControllerPropertiesConfig _config;\n\n        private EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap;\n\n        private const string RemoteOccupancyXml = \"<Occupancy><Type>Local</Type><State>{0}</State></Occupancy>\";\n        private bool _guidFileExists;\n\n        private readonly Dictionary<Device, BoolInputSig> _sourceToFeedbackSigs =\n            new Dictionary<Device, BoolInputSig>();\n\n        /// <summary>\n        /// Gets or sets the CurrentRoomSourceNameSig\n        /// </summary>\n        protected StringSigData CurrentRoomSourceNameSig;\n\n        private readonly FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge();\n\n        /// <summary>\n        /// Gets or sets the FusionOccSensor\n        /// </summary>\n        protected FusionOccupancySensorAsset FusionOccSensor;\n        private readonly FusionRemoteOccupancySensor FusionRemoteOccSensor;\n\n        /// <summary>\n        /// Gets or sets the FusionRoom\n        /// </summary>\n        protected FusionRoom FusionRoom;\n\n        /// <summary>\n        /// Gets or sets the FusionStaticAssets\n        /// </summary>\n        protected Dictionary<int, FusionAsset> FusionStaticAssets;\n        private readonly long PushNotificationTimeout = 5000;\n        private IEssentialsRoom Room;\n        private readonly long SchedulePollInterval = 300000;\n\n        private Event _currentMeeting;\n        private RoomSchedule _currentSchedule;\n        private CTimer _dailyTimeRequestTimer;\n        private StatusMonitorCollection _errorMessageRollUp;\n\n        private FusionRoomGuids _guids;\n\n        private bool _isRegisteredForSchedulePushNotifications;\n        private Event _nextMeeting;\n\n        private CTimer _pollTimer;\n\n        private CTimer _pushNotificationTimer;\n\n        private string _roomOccupancyRemoteString;\n\n        private bool _helpRequestSent;\n\n        private eFusionHelpResponse _helpRequestStatus;\n\n        /// <inheritdoc />\n        public StringFeedback HelpRequestResponseFeedback { get; private set; }\n\n        /// <inheritdoc />\n        public BoolFeedback HelpRequestSentFeedback { get; private set; }\n\n        /// <inheritdoc />\n        public StringFeedback HelpRequestStatusFeedback { get; private set; }\n\n        private Timer _helpRequestTimeoutTimer;\n\n        /// <summary>\n        /// Gets the DefaultHelpRequestTimeoutMs\n        /// </summary>\n        public int HelpRequestTimeoutMs => _config.HelpRequestTimeoutMs;\n\n        /// <summary>\n        /// Gets whether to use a timer for help requests\n        /// </summary>\n        public bool UseHelpRequestTimer => _config.UseTimeoutForHelpRequests;\n\n        #region Processor Info Sigs\n\n        private readonly StringSigData[] _program = new StringSigData[10];\n        private StringSigData _dns1;\n        private StringSigData _dns2;\n        private StringSigData _domain;\n        private StringSigData _firmware;\n        private StringSigData _gateway;\n        private StringSigData _hostname;\n        private StringSigData _ip1;\n        private StringSigData _ip2;\n        private StringSigData _mac1;\n        private StringSigData _mac2;\n        private StringSigData _netMask1;\n        private StringSigData _netMask2;\n\n        #endregion\n\n        #region Default Display Source Sigs\n\n        private readonly BooleanSigData[] _source = new BooleanSigData[10];\n\n        #endregion\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public IEssentialsRoomFusionController(string key, string name, IEssentialsRoomFusionControllerPropertiesConfig config)\n            : base(key, name)\n        {\n            _config = config;\n\n            AddPostActivationAction(() =>\n            {\n                var room = DeviceManager.GetDeviceForKey<IEssentialsRoom>(_config.RoomKey);\n\n                if (room == null)\n                {\n                    this.LogError(\"Error Creating Fusion Room Controller.  No room found with key '{0}'\", _config.RoomKey);\n                    return;\n                }\n\n                this.LogInformation(\"Creating Fusion Room Controller for room '{0}' at IPID: {1:X2}\", room.Key, _config.IpIdInt);\n\n                ConstructorHelper(room, _config.IpIdInt, _config.JoinMapKey);\n\n            });\n        }\n\n        /// <summary>\n        ///     \n        /// </summary>\n        /// <param name=\"room\"></param>\n        /// <param name=\"ipId\"></param>\n        /// <param name=\"joinMapKey\"></param>\n        public IEssentialsRoomFusionController(IEssentialsRoom room, string ipId, string joinMapKey)\n            : base(room.Key + \"-fusion\")\n        {\n            _config = new IEssentialsRoomFusionControllerPropertiesConfig()\n            {\n                IpId = ipId,\n                RoomKey = room.Key,\n                JoinMapKey = joinMapKey\n            };\n\n            ConstructorHelper(room, _config.IpIdInt, joinMapKey);\n        }\n\n        private void ConstructorHelper(IEssentialsRoom room, uint ipId, string joinMapKey)\n        {\n            try\n            {\n                this.LogDebug(\"ConstructorHelper called for Fusion Room Controller for room '{0}' with IPID {1:X2}\", room.Key, ipId);\n\n                this.LogDebug(\"JoinMap Key: {0}\", joinMapKey);\n\n                JoinMap = new EssentialsHuddleSpaceRoomFusionRoomJoinMap(1);\n\n                this.LogDebug(\"JoinMap created\");\n\n                CrestronConsole.AddNewConsoleCommand((o) =>\n                {\n                    if (o is string deviceKey)\n                    {\n                        if (string.IsNullOrEmpty(deviceKey) || deviceKey == \"?\")\n                        {\n                            CrestronConsole.ConsoleCommandResponse(\"Please provide a device key for a Fusion Room instance\");\n                            return;\n                        }\n                        else if (deviceKey != this.Key)\n                        {\n                            return;\n                        }\n                    }\n                    else\n                    {\n                        CrestronConsole.ConsoleCommandResponse(\"Invalid parameter.  Please provide a device key for a Fusion Room instance\");\n                        return;\n                    }\n\n                    JoinMap.PrintJoinMapInfo();\n                }, \"printfusionjoinmap\", \"Prints Attribute Join Map\", ConsoleAccessLevelEnum.AccessOperator);\n\n                if (!string.IsNullOrEmpty(joinMapKey))\n                {\n                    // this.LogDebug(\"Attempting to get custom join map for key: {0}\", joinMapKey);\n                    var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey);\n                    if (customJoins != null)\n                    {\n                        JoinMap.SetCustomJoinData(customJoins);\n                    }\n                }\n\n                Room = room;\n\n                this.LogDebug(\"Room found: {0}\", Room.Key);\n\n                FusionStaticAssets = new Dictionary<int, FusionAsset>();\n\n                this.LogDebug(\"FusionStaticAssets dictionary created\");\n\n                _guids = new FusionRoomGuids();\n\n                this.LogDebug(\"FusionRoomGuids created\");\n\n                if (Room is IRoomOccupancy occupancyRoom)\n                {\n                    Debug.LogDebug(this, \"Room '{0}' supports IRoomOccupancy\", Room.Key);\n                    if (occupancyRoom.RoomOccupancy != null)\n                    {\n                        if (occupancyRoom.OccupancyStatusProviderIsRemote)\n                        {\n                            SetUpRemoteOccupancy();\n                        }\n                        else\n                        {\n                            SetUpLocalOccupancy();\n                        }\n                    }\n                }\n\n                this.LogDebug(\"Occupancy setup complete\");\n\n                HelpRequestResponseFeedback = new StringFeedback(\"HelpRequestResponse\", () => FusionRoom.Help.OutputSig.StringValue);\n\n                HelpRequestSentFeedback = new BoolFeedback(\"HelpRequestSent\", () => _helpRequestSent);\n                HelpRequestStatusFeedback = new StringFeedback(\"HelpRequestStatus\", () => _helpRequestStatus.ToString());\n\n                Feedbacks.Add(HelpRequestResponseFeedback);\n                Feedbacks.Add(HelpRequestSentFeedback);\n                Feedbacks.Add(HelpRequestStatusFeedback);\n                if (RoomOccupancyRemoteStringFeedback != null)\n                    Feedbacks.Add(RoomOccupancyRemoteStringFeedback);\n                if (RoomIsOccupiedFeedback != null)\n                    Feedbacks.Add(RoomIsOccupiedFeedback);\n\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Error Building Fusion System Controller: {0}\", e);\n            }\n        }\n\n        private string GetGuidFilePath(uint ipId)\n        {\n            var mac =\n                CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);\n\n            var slot = Global.ControlSystem.ProgramNumber;\n\n            var guidFilePath = Global.FilePathPrefix +\n                               string.Format(@\"{0}-FusionGuids-{1:X2}.json\", InitialParametersClass.ProgramIDTag, _config.IpIdInt);\n\n            var oldGuidFilePath = Global.FilePathPrefix +\n                                  string.Format(@\"{0}-FusionGuids.json\", InitialParametersClass.ProgramIDTag);\n\n            if (File.Exists(oldGuidFilePath))\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Migrating from old Fusion GUID file to new Fusion GUID File\");\n\n                File.Copy(oldGuidFilePath, guidFilePath);\n\n                File.Delete(oldGuidFilePath);\n            }\n\n            _guidFileExists = File.Exists(guidFilePath);\n\n            // Check if file exists\n            if (!_guidFileExists)\n            {\n                // Does not exist. Create GUIDs\n                _guids = new FusionRoomGuids(Room.Name, ipId, _guids.GenerateNewRoomGuid(slot, mac),\n                    FusionStaticAssets);\n            }\n            else\n            {\n                // Exists. Read GUIDs\n                ReadGuidFile(guidFilePath);\n            }\n\n            return guidFilePath;\n        }\n\n        /// <inheritdoc />\n        public override void Initialize()\n        {\n\n            GenerateGuidFile(GetGuidFilePath(_config.IpIdInt));\n\n            CreateSymbolAndBasicSigs(_config.IpIdInt);\n            SetUpSources();\n            SetUpCommunitcationMonitors();\n            SetUpDisplay();\n            SetUpError();\n            ExecuteCustomSteps();\n\n            FusionRVI.GenerateFileForAllFusionDevices();\n\n        }\n\n        /// <summary>\n        /// Gets the RoomGuid\n        /// </summary>\n        protected string RoomGuid\n        {\n            get { return _guids.RoomGuid; }\n        }\n\n        /// <summary>\n        /// Gets or sets the RoomOccupancyRemoteStringFeedback\n        /// </summary>\n        public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets the RoomIsOccupiedFeedbackFunc\n        /// </summary>\n        protected Func<bool> RoomIsOccupiedFeedbackFunc\n        {\n            get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; }\n        }\n\n        #region IOccupancyStatusProvider Members\n\n        /// <summary>\n        /// Gets or sets the RoomIsOccupiedFeedback\n        /// </summary>\n        public BoolFeedback RoomIsOccupiedFeedback { get; private set; }\n\n        #endregion\n\n\n        /// <inheritdoc />\n        public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();\n\n\n        /// <summary>\n        /// ScheduleChange event\n        /// </summary>\n        public event EventHandler<ScheduleChangeEventArgs> ScheduleChange;\n        //public event EventHandler<MeetingChangeEventArgs> MeetingEndWarning;\n        //public event EventHandler<MeetingChangeEventArgs> NextMeetingBeginWarning;\n\n        /// <summary>\n        /// RoomInfoChange event\n        /// </summary>\n        public event EventHandler<EventArgs> RoomInfoChange;\n\n        //ScheduleResponseEvent NextMeeting;\n\n        /// <summary>\n        /// Used for extension classes to execute whatever steps are necessary before generating the RVI and GUID files\n        /// </summary>\n        protected virtual void ExecuteCustomSteps()\n        {\n        }\n\n        /// <summary>\n        /// Generates the guid file in NVRAM.  If the file already exists it will be overwritten.\n        /// </summary>\n        /// <param name=\"filePath\">path for the file</param>\n        private void GenerateGuidFile(string filePath)\n        {\n            if (string.IsNullOrEmpty(filePath))\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Error writing guid file.  No path specified.\");\n                return;\n            }\n\n            var fileLock = new CCriticalSection();\n\n            try\n            {\n                if (fileLock.Disposed)\n                {\n                    return;\n                }\n\n                fileLock.Enter();\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Writing GUIDs to file\");\n\n                _guids = FusionOccSensor == null\n                    ? new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets)\n                    : new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets, FusionOccSensor);\n\n                var json = JsonConvert.SerializeObject(_guids, Newtonsoft.Json.Formatting.Indented);\n\n                using (var sw = new StreamWriter(filePath))\n                {\n                    sw.Write(json);\n                    sw.Flush();\n                }\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Guids successfully written to file '{0}'\", filePath);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Error writing guid file: {0}\", e);\n            }\n            finally\n            {\n                if (!fileLock.Disposed)\n                {\n                    fileLock.Leave();\n                }\n            }\n        }\n\n        /// <summary>\n        /// Reads the guid file from NVRAM\n        /// </summary>\n        /// <param name=\"filePath\">path for te file</param>\n        private void ReadGuidFile(string filePath)\n        {\n            if (string.IsNullOrEmpty(filePath))\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Error reading guid file.  No path specified.\");\n                return;\n            }\n\n            var fileLock = new CCriticalSection();\n\n            try\n            {\n                if (fileLock.Disposed)\n                {\n                    return;\n                }\n\n                fileLock.Enter();\n\n                if (File.Exists(filePath))\n                {\n                    var json = File.ReadToEnd(filePath, Encoding.ASCII);\n\n                    _guids = JsonConvert.DeserializeObject<FusionRoomGuids>(json);\n\n                    // _config.IpId = _guids.IpId;\n\n                    FusionStaticAssets = _guids.StaticAssets;\n                }\n\n                Debug.LogMessage(LogEventLevel.Information, this, \"Fusion Guids successfully read from file: {0}\",\n                    filePath);\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"\\r\\n********************\\r\\n\\tRoom Name: {0}\\r\\n\\tIPID: {1:X}\\r\\n\\tRoomGuid: {2}\\r\\n*******************\", Room.Name, _config.IpIdInt, RoomGuid);\n\n                foreach (var item in FusionStaticAssets)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"\\nAsset Name: {0}\\nAsset No: {1}\\n Guid: {2}\", item.Value.Name,\n                        item.Value.SlotNumber, item.Value.InstanceId);\n                }\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Error reading guid file: {0}\", e);\n            }\n            finally\n            {\n                if (!fileLock.Disposed)\n                {\n                    fileLock.Leave();\n                }\n            }\n        }\n\n        /// <summary>\n        /// CreateSymbolAndBasicSigs method\n        /// </summary>\n        /// <param name=\"ipId\"></param>\n        protected virtual void CreateSymbolAndBasicSigs(uint ipId)\n        {\n            Debug.LogMessage(LogEventLevel.Information, this, \"Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}\", RoomGuid, ipId);\n\n            FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);\n            FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();\n            FusionRoom.ExtenderFusionRoomDataReservedSigs.Use();\n\n            FusionRoom.Register();\n\n            FusionRoom.FusionStateChange += FusionRoom_FusionStateChange;\n\n            FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange +=\n                FusionRoomSchedule_DeviceExtenderSigChange;\n            FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange +=\n                ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange;\n            FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange;\n\n            CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, \"FusReqRoomSchedule\",\n                \"Requests schedule of the room for the next 24 hours\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, \"FusReqRoomSchMod\",\n                \"Ends or extends a meeting by the specified time\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(CreateAdHocMeeting, \"FusCreateMeeting\",\n                \"Creates and Ad Hoc meeting for on hour or until the next meeting\",\n                ConsoleAccessLevelEnum.AccessOperator);\n\n            // Room to fusion room\n            Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig);\n\n            // Moved to \n            CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.AttributeName,\n                eSigIoMask.InputSigOnly);\n            // Don't think we need to get current status of this as nothing should be alive yet. \n            if (Room is IHasCurrentSourceInfoChange hasCurrentSourceInfoChange)\n            {\n                hasCurrentSourceInfoChange.CurrentSourceChange += Room_CurrentSourceInfoChange;\n            }\n\n\n            FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction(Room.PowerOnToDefaultOrLastSource);\n            FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() =>\n            {\n                if (Room is IRunRouteAction runRouteAction)\n                {\n                    runRouteAction.RunRouteAction(\"roomOff\", Room.SourceListKey);\n                }\n            });\n            // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig);\n            FusionRoom.ErrorMessage.InputSig.StringValue =\n                \"3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;\";\n\n            SetUpEthernetValues();\n\n            GetProcessorEthernetValues();\n\n            GetSystemInfo();\n\n            GetProcessorInfo();\n\n            CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;\n        }\n\n        /// <summary>\n        /// CrestronEnvironment_EthernetEventHandler method\n        /// </summary>\n        /// <param name=\"ethernetEventArgs\"></param>\n        protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)\n        {\n            if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp)\n            {\n                GetProcessorEthernetValues();\n            }\n        }\n\n        /// <summary>\n        /// GetSystemInfo method\n        /// </summary>\n        protected void GetSystemInfo()\n        {\n            //SystemName.InputSig.StringValue = Room.Name;\n            //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName;\n            //SerialNumber.InputSig.StringValue = InitialParametersClass.\n\n            var response = string.Empty;\n\n            var systemReboot = FusionRoom.CreateOffsetBoolSig(JoinMap.ProcessorReboot.JoinNumber, JoinMap.ProcessorReboot.AttributeName, eSigIoMask.OutputSigOnly);\n            systemReboot.OutputSig.SetSigFalseAction(\n                () => CrestronConsole.SendControlSystemCommand(\"reboot\", ref response));\n        }\n\n        /// <summary>\n        /// SetUpEthernetValues method\n        /// </summary>\n        protected void SetUpEthernetValues()\n        {\n            _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.AttributeName, eSigIoMask.InputSigOnly);\n            _ip2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp2.JoinNumber, JoinMap.ProcessorIp2.AttributeName, eSigIoMask.InputSigOnly);\n            _gateway = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorGateway.JoinNumber, JoinMap.ProcessorGateway.AttributeName, eSigIoMask.InputSigOnly);\n            _hostname = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorHostname.JoinNumber, JoinMap.ProcessorHostname.AttributeName, eSigIoMask.InputSigOnly);\n            _domain = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDomain.JoinNumber, JoinMap.ProcessorDomain.AttributeName, eSigIoMask.InputSigOnly);\n            _dns1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns1.JoinNumber, JoinMap.ProcessorDns1.AttributeName, eSigIoMask.InputSigOnly);\n            _dns2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns2.JoinNumber, JoinMap.ProcessorDns2.AttributeName, eSigIoMask.InputSigOnly);\n            _mac1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac1.JoinNumber, JoinMap.ProcessorMac1.AttributeName, eSigIoMask.InputSigOnly);\n            _mac2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac2.JoinNumber, JoinMap.ProcessorMac2.AttributeName, eSigIoMask.InputSigOnly);\n            _netMask1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask1.JoinNumber, JoinMap.ProcessorNetMask1.AttributeName, eSigIoMask.InputSigOnly);\n            _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.AttributeName, eSigIoMask.InputSigOnly);\n        }\n\n        /// <summary>\n        /// GetProcessorEthernetValues method\n        /// </summary>\n        protected void GetProcessorEthernetValues()\n        {\n            _ip1.InputSig.StringValue =\n                CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);\n            _gateway.InputSig.StringValue =\n                CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0);\n            _hostname.InputSig.StringValue =\n                CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);\n            _domain.InputSig.StringValue =\n                CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);\n\n            var dnsServers =\n                CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(',');\n            _dns1.InputSig.StringValue = dnsServers[0];\n            if (dnsServers.Length > 1)\n            {\n                _dns2.InputSig.StringValue = dnsServers[1];\n            }\n\n            _mac1.InputSig.StringValue =\n                CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);\n            _netMask1.InputSig.StringValue =\n                CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0);\n\n            // Interface 1\n\n            if (InitialParametersClass.NumberOfEthernetInterfaces > 1)\n            // Only get these values if the processor has more than 1 NIC\n            {\n                _ip2.InputSig.StringValue =\n                    CrestronEthernetHelper.GetEthernetParameter(\n                        CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1);\n                _mac2.InputSig.StringValue =\n                    CrestronEthernetHelper.GetEthernetParameter(\n                        CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1);\n                _netMask2.InputSig.StringValue =\n                    CrestronEthernetHelper.GetEthernetParameter(\n                        CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1);\n            }\n        }\n\n        /// <summary>\n        /// GetProcessorInfo method\n        /// </summary>\n        protected void GetProcessorInfo()\n        {\n            _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.AttributeName, eSigIoMask.InputSigOnly);\n\n            if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)\n            {\n                for (var i = 0; i < Global.ControlSystem.NumProgramsSupported; i++)\n                {\n                    var join = JoinMap.ProgramNameStart.JoinNumber + i;\n                    var progNum = i + 1;\n                    _program[i] = FusionRoom.CreateOffsetStringSig((uint)join,\n                        string.Format(\"{0} {1}\", JoinMap.ProgramNameStart.AttributeName, progNum), eSigIoMask.InputSigOnly);\n                }\n            }\n\n            _firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion;\n        }\n\n        /// <summary>\n        /// GetCustomProperties method\n        /// </summary>\n        protected void GetCustomProperties()\n        {\n            if (FusionRoom.IsOnline)\n            {\n                const string fusionRoomCustomPropertiesRequest =\n                    @\"<RequestRoomConfiguration><RequestID>RoomConfigurationRequest</RequestID><CustomProperties><Property></Property></CustomProperties></RequestRoomConfiguration>\";\n\n                FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigQuery.StringValue =\n                    fusionRoomCustomPropertiesRequest;\n            }\n        }\n\n        private void GetTouchpanelInfo()\n        {\n            // TODO: Get IP and Project Name from TP\n        }\n\n        /// <summary>\n        /// FusionRoom_OnlineStatusChange method\n        /// </summary>\n        /// <param name=\"currentDevice\"></param>\n        /// <param name=\"args\"></param>\n        protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)\n        {\n            if (!args.DeviceOnLine)\n            {\n                return;\n            }\n\n            if (!_config.EnableSchedulePushNotifications)\n            {\n                return;\n            }\n\n            Task.Run(() =>\n            {\n                // CrestronEnvironment.Sleep(200);\n\n                // Send Push Notification Action request:\n\n                const string requestId = \"InitialPushRequest\";\n\n\n                var actionRequest =\n                        string.Format(\"<RequestAction>\\n<RequestID>{0}</RequestID>\\n\", requestId) +\n                        \"<ActionID>RegisterPushModel</ActionID>\\n\" +\n                        \"<Parameters>\\n\" +\n                        \"<Parameter ID='Enabled' Value='1' />\\n\" +\n                        \"<Parameter ID='RequestID' Value='PushNotification' />\\n\" +\n                        \"<Parameter ID='Start' Value='00:00:00' />\\n\" +\n                        \"<Parameter ID='HourSpan' Value='24' />\\n\" +\n                        \"<Parameter ID='Field' Value='MeetingID' />\\n\" +\n                        \"<Parameter ID='Field' Value='RVMeetingID' />\\n\" +\n                        \"<Parameter ID='Field' Value='InstanceID' />\\n\" +\n                        \"<Parameter ID='Field' Value='dtStart' />\\n\" +\n                        \"<Parameter ID='Field' Value='dtEnd' />\\n\" +\n                        \"<Parameter ID='Field' Value='Subject' />\\n\" +\n                        \"<Parameter ID='Field' Value='Organizer' />\\n\" +\n                        \"<Parameter ID='Field' Value='IsEvent' />\\n\" +\n                        \"<Parameter ID='Field' Value='IsPrivate' />\\n\" +\n                        \"<Parameter ID='Field' Value='IsExchangePrivate' />\\n\" +\n                        \"<Parameter ID='Field' Value='LiveMeeting' />\\n\" +\n                        \"<Parameter ID='Field' Value='ShareDocPath' />\\n\" +\n                        \"<Parameter ID='Field' Value='PhoneNo' />\\n\" +\n                        \"<Parameter ID='Field' Value='ParticipantCode' />\\n\" +\n                        \"</Parameters>\\n\" +\n                        \"</RequestAction>\\n\";\n\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Sending Fusion ActionRequest: \\n{0}\", actionRequest);\n\n                FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest;\n\n                GetCustomProperties();\n\n                // Request current Fusion Server Time\n                RequestLocalDateTime(null);\n\n                // Setup timer to request time daily\n                if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed)\n                {\n                    _dailyTimeRequestTimer.Stop();\n                    _dailyTimeRequestTimer.Dispose();\n                }\n\n                _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000);\n\n                _dailyTimeRequestTimer.Reset(86400000, 86400000);\n            });\n        }\n\n        /// <summary>\n        /// Requests the local date and time from the Fusion Server\n        /// </summary>\n        /// <param name=\"callbackObject\"></param>\n        /// <summary>\n        /// RequestLocalDateTime method\n        /// </summary>\n        public void RequestLocalDateTime(object callbackObject)\n        {\n            const string timeRequestId = \"TimeRequest\";\n\n            var timeRequest = string.Format(\"<LocalTimeRequest><RequestID>{0}</RequestID></LocalTimeRequest>\",\n                timeRequestId);\n\n            FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest;\n        }\n\n        /// <summary>\n        /// RequestFullRoomSchedule method\n        /// </summary>\n        public void RequestFullRoomSchedule(object callbackObject)\n        {\n            var now = DateTime.Today;\n\n            var currentTime = now.ToString(\"s\");\n\n            var requestTest =\n                string.Format(\n                    \"<RequestSchedule><RequestID>FullScheduleRequest</RequestID><RoomID>{0}</RoomID><Start>{1}</Start><HourSpan>24</HourSpan></RequestSchedule>\",\n                    RoomGuid, currentTime);\n\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Sending Fusion ScheduleQuery: \\n{0}\", requestTest);\n\n            FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest;\n\n            if (_isRegisteredForSchedulePushNotifications)\n            {\n                _pushNotificationTimer.Stop();\n            }\n        }\n\n        /// <summary>\n        /// Wrapper method to allow console commands to modify the current meeting end time\n        /// </summary>\n        /// <param name=\"command\">meetingID extendTime</param>\n        /// <summary>\n        /// ModifyMeetingEndTimeConsoleHelper method\n        /// </summary>\n        public void ModifyMeetingEndTimeConsoleHelper(string command)\n        {\n            var extendMinutes = -1;\n\n            const string requestId = \"ModifyMeetingTest12345\";\n\n            try\n            {\n                var tokens = command.Split(' ');\n\n                extendMinutes = Int32.Parse(tokens[1]);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Error parsing console command: {0}\", e);\n            }\n\n            ModifyMeetingEndTime(requestId, extendMinutes);\n        }\n\n        /// <summary>\n        /// Ends or Extends the current meeting by the specified number of minutes.\n        /// </summary>\n        /// <param name=\"requestId\"></param>\n        /// <param name=\"extendMinutes\">Number of minutes to extend the meeting.  A value of 0 will end the meeting.</param>\n        /// <summary>\n        /// ModifyMeetingEndTime method\n        /// </summary>\n        public void ModifyMeetingEndTime(string requestId, int extendMinutes)\n        {\n            if (_currentMeeting == null)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"No meeting in progress.  Unable to modify end time.\");\n                return;\n            }\n\n            if (extendMinutes > -1)\n            {\n                if (extendMinutes > 0)\n                {\n                    var extendTime = _currentMeeting.dtEnd - DateTime.Now;\n                    var extendMinutesRaw = extendTime.TotalMinutes;\n\n                    extendMinutes += (int)Math.Round(extendMinutesRaw);\n                }\n\n\n                var requestTest = string.Format(\n                    \"<RequestAction><RequestID>{0}</RequestID><RoomID>{1}</RoomID><ActionID>MeetingChange</ActionID><Parameters><Parameter ID = 'MeetingID' Value = '{2}' /><Parameter ID = 'EndTime' Value = '{3}' /></Parameters></RequestAction>\"\n                    , requestId, RoomGuid, _currentMeeting.MeetingID, extendMinutes);\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Sending MeetingChange Request: \\n{0}\", requestTest);\n\n                FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = requestTest;\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Invalid time specified\");\n            }\n        }\n\n        /// <summary>\n        /// CreateAdHocMeeting method\n        /// </summary>\n        public void CreateAdHocMeeting(string command)\n        {\n            const string requestId = \"CreateAdHocMeeting\";\n\n            var now = DateTime.Now.AddMinutes(1);\n\n            now.AddSeconds(-now.Second);\n\n            // Assume 1 hour meeting if possible\n            var dtEnd = now.AddHours(1);\n\n            // Check if room is available for 1 hour before next meeting\n            if (_nextMeeting != null)\n            {\n                var roomAvailable = _nextMeeting.dtEnd.Subtract(dtEnd);\n\n                if (roomAvailable.TotalMinutes < 60)\n                {\n                    // Room not available for full hour, book until next meeting starts\n                    dtEnd = _nextMeeting.dtEnd;\n                }\n            }\n\n            var createMeetingRequest =\n                \"<CreateSchedule>\" +\n                string.Format(\"<RequestID>{0}</RequestID>\", requestId) +\n                string.Format(\"<RoomID>{0}</RoomID>\", RoomGuid) +\n                \"<Event>\" +\n                string.Format(\"<dtStart>{0}</dtStart>\", now.ToString(\"s\")) +\n                string.Format(\"<dtEnd>{0}</dtEnd>\", dtEnd.ToString(\"s\")) +\n                \"<Subject>AdHoc Meeting</Subject>\" +\n                \"<Organizer>Room User</Organizer>\" +\n                \"<WelcomMsg>Example Message</WelcomMsg>\" +\n                \"</Event>\" +\n                \"</CreateSchedule>\";\n\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Sending CreateMeeting Request: \\n{0}\", createMeetingRequest);\n\n            FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest;\n\n            //Debug.LogMessage(LogEventLevel.Debug, this, \"Sending CreateMeeting Request: \\n{0}\", command);\n\n            //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command;\n        }\n\n        /// <summary>\n        /// Event handler method for Device Extender sig changes\n        /// </summary>\n        /// <param name=\"currentDeviceExtender\"></param>\n        /// <param name=\"args\"></param>\n        protected void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender,\n            SigEventArgs args)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Event: {0}\\n Sig: {1}\\nFusionResponse:\\n{2}\", args.Event, args.Sig.Name,\n                args.Sig.StringValue);\n\n\n            if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse)\n            {\n                try\n                {\n                    var message = new XmlDocument();\n\n                    message.LoadXml(args.Sig.StringValue);\n\n                    var actionResponse = message[\"ActionResponse\"];\n\n                    if (actionResponse == null)\n                    {\n                        return;\n                    }\n\n                    var requestId = actionResponse[\"RequestID\"];\n\n                    if (requestId.InnerText != \"InitialPushRequest\")\n                    {\n                        return;\n                    }\n\n                    if (actionResponse[\"ActionID\"].InnerText != \"RegisterPushModel\")\n                    {\n                        return;\n                    }\n\n                    var parameters = actionResponse[\"Parameters\"];\n\n                    foreach (var isRegistered in from XmlElement parameter in parameters\n                                                 where parameter.HasAttributes\n                                                 select parameter.Attributes\n                        into attributes\n                                                 where attributes[\"ID\"].Value == \"Registered\"\n                                                 select int.Parse(attributes[\"Value\"].Value))\n                    {\n                        switch (isRegistered)\n                        {\n                            case 1:\n                                _isRegisteredForSchedulePushNotifications = true;\n                                if (_pollTimer != null && !_pollTimer.Disposed)\n                                {\n                                    _pollTimer.Stop();\n                                    _pollTimer.Dispose();\n                                }\n                                _pushNotificationTimer = new CTimer(RequestFullRoomSchedule, null,\n                                    PushNotificationTimeout, PushNotificationTimeout);\n                                _pushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout);\n                                break;\n                            case 0:\n                                _isRegisteredForSchedulePushNotifications = false;\n                                if (_pushNotificationTimer != null && !_pushNotificationTimer.Disposed)\n                                {\n                                    _pushNotificationTimer.Stop();\n                                    _pushNotificationTimer.Dispose();\n                                }\n                                _pollTimer = new CTimer(RequestFullRoomSchedule, null, SchedulePollInterval,\n                                    SchedulePollInterval);\n                                _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval);\n                                break;\n                        }\n                    }\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Error parsing ActionQueryResponse: {0}\", e);\n                }\n            }\n            else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQueryResponse)\n            {\n                try\n                {\n                    var message = new XmlDocument();\n\n                    message.LoadXml(args.Sig.StringValue);\n\n                    var localDateTimeResponse = message[\"LocalTimeResponse\"];\n\n                    if (localDateTimeResponse != null)\n                    {\n                        var localDateTime = localDateTimeResponse[\"LocalDateTime\"];\n\n                        if (localDateTime != null)\n                        {\n                            var tempLocalDateTime = localDateTime.InnerText;\n\n                            var currentTime = DateTime.Parse(tempLocalDateTime);\n\n                            Debug.LogMessage(LogEventLevel.Debug, this, \"DateTime from Fusion Server: {0}\", currentTime);\n\n                            // Parse time and date from response and insert values\n                            CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute,\n                                (ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day,\n                                (ushort)currentTime.Year);\n\n                            Debug.LogMessage(LogEventLevel.Debug, this, \"Processor time set to {0}\", CrestronEnvironment.GetLocalTime());\n                        }\n                    }\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Error parsing LocalDateTimeQueryResponse: {0}\", e);\n                }\n            }\n            else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigResponse)\n            {\n                // Room info response with custom properties\n\n                var roomConfigResponseArgs = args.Sig.StringValue.Replace(\"&\", \"and\");\n\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Fusion Response: \\n {0}\", roomConfigResponseArgs);\n\n                try\n                {\n                    var roomConfigResponse = new XmlDocument();\n\n                    roomConfigResponse.LoadXml(roomConfigResponseArgs);\n\n                    var requestRoomConfiguration = roomConfigResponse[\"RoomConfigurationResponse\"];\n\n                    if (requestRoomConfiguration != null)\n                    {\n                        var roomInformation = new RoomInformation();\n\n                        foreach (XmlElement e in roomConfigResponse.FirstChild.ChildNodes)\n                        {\n                            if (e.Name == \"RoomInformation\")\n                            {\n                                var roomInfo = new XmlReader(e.OuterXml);\n\n                                roomInformation = CrestronXMLSerialization.DeSerializeObject<RoomInformation>(roomInfo);\n                            }\n                            else if (e.Name == \"CustomFields\")\n                            {\n                                foreach (XmlElement el in e)\n                                {\n                                    var customProperty = new FusionCustomProperty();\n\n                                    if (el.Name == \"CustomField\")\n                                    {\n                                        customProperty.ID = el.Attributes[\"ID\"].Value;\n                                    }\n\n                                    foreach (XmlElement elm in el)\n                                    {\n                                        if (elm.Name == \"CustomFieldName\")\n                                        {\n                                            customProperty.CustomFieldName = elm.InnerText;\n                                        }\n                                        if (elm.Name == \"CustomFieldType\")\n                                        {\n                                            customProperty.CustomFieldType = elm.InnerText;\n                                        }\n                                        if (elm.Name == \"CustomFieldValue\")\n                                        {\n                                            customProperty.CustomFieldValue = elm.InnerText;\n                                        }\n                                    }\n\n                                    roomInformation.FusionCustomProperties.Add(customProperty);\n                                }\n                            }\n                        }\n                        RoomInfoChange?.Invoke(this, new EventArgs());\n\n                        CustomPropertiesBridge.EvaluateRoomInfo(Room, roomInformation, _config.UseFusionRoomName);\n                    }\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Error parsing Custom Properties response: {0}\", e);\n                }\n                //PrintRoomInfo();\n                //getRoomInfoBusy = false;\n                //_DynFusion.API.EISC.BooleanInput[Constants.GetRoomInfo].BoolValue = getRoomInfoBusy;\n            }\n        }\n\n        /// <summary>\n        /// Event handler method for Device Extender sig changes\n        /// </summary>\n        /// <param name=\"currentDeviceExtender\"></param>\n        /// <param name=\"args\"></param>\n        protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender,\n            SigEventArgs args)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Schedule Response Event: {0}\\n Sig: {1}\\nFusionResponse:\\n{2}\", args.Event,\n                args.Sig.Name, args.Sig.StringValue);\n\n\n            if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse)\n            {\n                try\n                {\n                    var scheduleResponse = new ScheduleResponse();\n\n                    var message = new XmlDocument();\n\n                    message.LoadXml(args.Sig.StringValue);\n\n                    var response = message[\"ScheduleResponse\"];\n\n                    if (response != null)\n                    {\n                        // Check for push notification\n                        if (response[\"RequestID\"].InnerText == \"RVRequest\")\n                        {\n                            var action = response[\"Action\"];\n\n                            if (action.OuterXml.IndexOf(\"RequestSchedule\", StringComparison.Ordinal) > -1)\n                            {\n                                _pushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout);\n                            }\n                        }\n                        else // Not a push notification\n                        {\n                            _currentSchedule = new RoomSchedule(); // Clear Current Schedule\n                            _currentMeeting = null; // Clear Current Meeting\n                            _nextMeeting = null; // Clear Next Meeting\n\n                            var isNextMeeting = false;\n\n                            foreach (XmlElement element in message.FirstChild.ChildNodes)\n                            {\n                                if (element.Name == \"RequestID\")\n                                {\n                                    scheduleResponse.RequestID = element.InnerText;\n                                }\n                                else if (element.Name == \"RoomID\")\n                                {\n                                    scheduleResponse.RoomID = element.InnerText;\n                                }\n                                else if (element.Name == \"RoomName\")\n                                {\n                                    scheduleResponse.RoomName = element.InnerText;\n                                }\n                                else if (element.Name == \"Event\")\n                                {\n                                    Debug.LogMessage(LogEventLevel.Verbose, this, \"Event Found:\\n{0}\", element.OuterXml);\n\n                                    var reader = new XmlReader(element.OuterXml);\n\n                                    var tempEvent = CrestronXMLSerialization.DeSerializeObject<Event>(reader);\n\n                                    scheduleResponse.Events.Add(tempEvent);\n\n                                    // Check is this is the current event\n                                    if (tempEvent.dtStart <= DateTime.Now && tempEvent.dtEnd >= DateTime.Now)\n                                    {\n                                        _currentMeeting = tempEvent; // Set Current Meeting\n                                        isNextMeeting = true; // Flag that next element is next meeting\n                                    }\n\n                                    if (isNextMeeting)\n                                    {\n                                        _nextMeeting = tempEvent; // Set Next Meeting\n                                        isNextMeeting = false;\n                                    }\n\n                                    _currentSchedule.Meetings.Add(tempEvent);\n                                }\n                            }\n\n                            PrintTodaysSchedule();\n\n                            if (!_isRegisteredForSchedulePushNotifications)\n                            {\n                                _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval);\n                            }\n\n                            // Fire Schedule Change Event \n                            ScheduleChange?.Invoke(this, new ScheduleChangeEventArgs { Schedule = _currentSchedule });\n                        }\n                    }\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Error parsing ScheduleResponse: {0}\", e);\n                }\n            }\n            else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Create Meeting Response Event: {0}\\n Sig: {1}\\nFusionResponse:\\n{2}\", args.Event,\n                    args.Sig.Name, args.Sig.StringValue);\n            }\n        }\n\n        /// <summary>\n        /// Prints today's schedule to console for debugging\n        /// </summary>\n        private void PrintTodaysSchedule()\n        {\n            if (Debug.Level > 1)\n            {\n                if (_currentSchedule.Meetings.Count > 0)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Today's Schedule for '{0}'\\n\", Room.Name);\n\n                    foreach (var e in _currentSchedule.Meetings)\n                    {\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Subject: {0}\", e.Subject);\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Organizer: {0}\", e.Organizer);\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"MeetingID: {0}\", e.MeetingID);\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Start Time: {0}\", e.dtStart);\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"End Time: {0}\", e.dtEnd);\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Duration: {0}\\n\", e.DurationInMinutes);\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// SetUpSources method\n        /// </summary>\n        protected virtual void SetUpSources()\n        {\n            // Sources\n            var dict = ConfigReader.ConfigObject.GetSourceListForKey(Room.SourceListKey);\n            if (dict != null)\n            {\n                // NEW PROCESS:\n                // Make these lists and insert the fusion attributes by iterating these\n                var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls);\n                uint i = 0;\n                foreach (var kvp in setTopBoxes)\n                {\n                    TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.AttributeName + \" \" + (i + 1), JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);\n                    i++;\n                    if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots\n                    {\n                        break;\n                    }\n                }\n\n                var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls);\n                i = 0;\n                foreach (var kvp in discPlayers)\n                {\n                    TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + \" \" + (i + 1), JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);\n                    i++;\n                    if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots\n                    {\n                        break;\n                    }\n                }\n\n                var laptops = dict.Where(d => d.Value.SourceDevice is IRoutingSource);\n                i = 0;\n                foreach (var kvp in laptops)\n                {\n                    TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.AttributeName + \" \" + (i + 1), JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);\n                    i++;\n                    if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots???\n                    {\n                        break;\n                    }\n                }\n\n                foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType<IUsageTracking>())\n                {\n                    usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) { UsageIsTracked = true };\n                    usageDevice.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;\n                }\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"WARNING: Config source list '{0}' not found for room '{1}'\",\n                    Room.SourceListKey, Room.Key);\n            }\n        }\n\n        /// <summary>\n        /// Collects usage data from source and sends to Fusion\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        protected void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e)\n        {\n            if (!(sender is UsageTracking deviceTracker))\n            {\n                return;\n            }\n\n            var group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key);\n\n            var currentMeetingId = \"-\";\n\n            if (_currentMeeting != null)\n            {\n                currentMeetingId = _currentMeeting.MeetingID;\n            }\n\n            //String Format:  \"USAGE||[Date YYYY-MM-DD]||[Time HH-mm-ss]||TIME||[Asset_Type]||[Asset_Name]||[Minutes_used]||[Asset_ID]||[Meeting_ID]\"\n            // [Asset_ID] property does not appear to be used in Crestron SSI examples.  They are sending \"-\" instead so that's what is replicated here\n            var deviceUsage = string.Format(\"USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\\r\\n\",\n                e.UsageEndTime.ToString(\"yyyy-MM-dd\"), e.UsageEndTime.ToString(\"HH:mm:ss\"),\n                @group, deviceTracker.Parent.Name, e.MinutesUsed, \"-\", currentMeetingId);\n\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Device usage for: {0} ended at {1}. In use for {2} minutes\",\n                deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed);\n\n            FusionRoom.DeviceUsage.InputSig.StringValue = deviceUsage;\n\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Device usage string: {0}\", deviceUsage);\n        }\n\n        /// <summary>\n        /// Tries to add route action sigs for a source\n        /// </summary>\n        /// <param name=\"attrName\"></param>\n        /// <param name=\"attrNum\"></param>\n        /// <param name=\"routeKey\"></param>\n        /// <param name=\"pSrc\"></param>\n        protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc)\n        {\n            this.LogVerbose(\"Creating attribute '{0}' with join {1} for source {2}\",\n                attrName, attrNum, pSrc.Key);\n            try\n            {\n                var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig);\n                // Need feedback when this source is selected\n                // Event handler, added below, will compare source changes with this sig dict\n                if (!_sourceToFeedbackSigs.ContainsKey(pSrc))\n                {\n                    _sourceToFeedbackSigs.Add(pSrc, sigD.InputSig);\n                }\n                else\n                {\n                    this.LogWarning(\"Source '{0}' already has a feedback sig mapped. Overwriting.\", pSrc.Key);\n                    _sourceToFeedbackSigs[pSrc] = sigD.InputSig;\n                }\n\n                // And respond to selection in Fusion\n                sigD.OutputSig.SetSigFalseAction(() =>\n                {\n                    if (Room is IRunRouteAction runRouteAction)\n                    {\n                        runRouteAction.RunRouteAction(routeKey, Room.SourceListKey);\n                    }\n                });\n            }\n            catch (Exception)\n            {\n                this.LogVerbose(\"Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING\",\n                    attrNum, attrName, pSrc.Key);\n            }\n        }\n\n\n        private void SetUpCommunitcationMonitors()\n        {\n            uint displayNum = 0;\n            uint touchpanelNum = 0;\n            uint xpanelNum = 0;\n\n            // Attach to all room's devices with monitors.\n            //foreach (var dev in DeviceManager.Devices)\n            foreach (var dev in DeviceManager.GetDevices())\n            {\n                if (!(dev is ICommunicationMonitor))\n                {\n                    continue;\n                }\n\n                string attrName = null;\n                uint attrNum = 1;\n\n                //var keyNum = ExtractNumberFromKey(dev.Key);\n                //if (keyNum == -1)\n                //{\n                //    Debug.LogMessage(LogEventLevel.Debug, this, \"WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes\",\n                //        dev.Key);\n                //    continue;\n                //}\n                //uint attrNum = Convert.ToUInt32(keyNum);\n\n                // Check for UI devices\n                if (dev is IHasBasicTriListWithSmartObject uiDev)\n                {\n                    if (uiDev.Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics)\n                    {\n                        attrNum += touchpanelNum;\n\n                        if (attrNum > JoinMap.XpanelOnlineStart.JoinSpan)\n                        {\n                            continue;\n                        }\n                        attrName = JoinMap.XpanelOnlineStart.AttributeName + \" \" + attrNum;\n                        attrNum += JoinMap.XpanelOnlineStart.JoinNumber;\n\n                        touchpanelNum++;\n                    }\n                    else\n                    {\n                        attrNum += xpanelNum;\n\n                        if (attrNum > JoinMap.TouchpanelOnlineStart.JoinSpan)\n                        {\n                            continue;\n                        }\n                        attrName = JoinMap.TouchpanelOnlineStart.AttributeName + \" \" + attrNum;\n                        attrNum += JoinMap.TouchpanelOnlineStart.JoinNumber;\n\n                        xpanelNum++;\n                    }\n                }\n\n                //else \n                if (dev is IDisplay)\n                {\n                    attrNum += displayNum;\n                    if (attrNum > JoinMap.DisplayOnlineStart.JoinSpan)\n                    {\n                        continue;\n                    }\n                    attrName = JoinMap.DisplayOnlineStart.AttributeName + \" \" + attrNum;\n                    attrNum += JoinMap.DisplayOnlineStart.JoinNumber;\n\n                    displayNum++;\n                }\n                //else if (dev is DvdDeviceBase)\n                //{\n                //    if (attrNum > 5)\n                //        continue;\n                //    attrName = \"Device Ok - DVD \" + attrNum;\n                //    attrNum += 260;\n                //}\n                // add set top box\n\n                // add Cresnet roll-up\n\n                // add DM-devices roll-up\n\n                if (attrName != null)\n                {\n                    this.LogDebug(\"Linking communication monitor for device '{0}' to Fusion attribute '{1}' at join {2}\",\n                        dev.Key, attrName, attrNum);\n                    // Link comm status to sig and update\n                    var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly);\n                    var smd = dev as ICommunicationMonitor;\n                    sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk;\n                    smd.CommunicationMonitor.StatusChange +=\n                        (o, a) => { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; };\n                    Debug.LogMessage(LogEventLevel.Information, this, \"Linking '{0}' communication monitor to Fusion '{1}'\", dev.Key, attrName);\n                }\n            }\n        }\n\n        /// <summary>\n        /// SetUpDisplay method\n        /// </summary>\n        protected virtual void SetUpDisplay()\n        {\n            try\n            {\n                //Setup Display Usage Monitoring\n\n                var displays = DeviceManager.AllDevices.Where(d => d is IDisplay);\n\n                //  Consider updating this in multiple display systems\n\n                foreach (var display in displays.Cast<IDisplay>())\n                {\n                    display.UsageTracker = new UsageTracking(display as Device) { UsageIsTracked = true };\n                    display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;\n                }\n\n                if (!(Room is IHasDefaultDisplay hasDefaultDisplay))\n                {\n                    return;\n                }\n                if (!(hasDefaultDisplay.DefaultDisplay is IDisplay defaultDisplay))\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Cannot link null display to Fusion because default display is null\");\n                    return;\n                }\n\n                var dispPowerOnAction = new Action<bool>(b =>\n                {\n                    if (!b)\n                    {\n                        defaultDisplay.PowerOn();\n                    }\n                });\n                var dispPowerOffAction = new Action<bool>(b =>\n                {\n                    if (!b)\n                    {\n                        defaultDisplay.PowerOff();\n                    }\n                });\n\n                // Display to fusion room sigs\n                FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction;\n                FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction;\n\n                MapDisplayToRoomJoins(1, JoinMap.Display1Start.JoinNumber, defaultDisplay);\n\n\n                var deviceConfig =\n                    ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key));\n\n                //Check for existing asset in GUIDs collection\n\n                FusionAsset tempAsset;\n\n                if (FusionStaticAssets.ContainsKey(deviceConfig.Uid))\n                {\n                    tempAsset = FusionStaticAssets[deviceConfig.Uid];\n                }\n                else\n                {\n                    // Create a new asset\n                    tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom),\n                        defaultDisplay.Name, \"Display\", \"\");\n                    FusionStaticAssets.Add(deviceConfig.Uid, tempAsset);\n                }\n\n                var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, \"Display\",\n                    tempAsset.InstanceId);\n                dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction;\n                dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction;\n\n                if (defaultDisplay is IHasPowerControlWithFeedback defaultTwoWayDisplay)\n                {\n                    defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig);\n                    if (defaultDisplay is IDisplayUsage)\n                    {\n                        (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig);\n                    }\n\n                    defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig);\n                }\n\n                // Use extension methods\n                dispAsset.TrySetMakeModel(defaultDisplay as Device);\n                dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay as Device);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Error setting up display in Fusion: {0}\", e);\n            }\n        }\n\n        /// <summary>\n        /// Maps room attributes to a display at a specified index\n        /// </summary>\n        /// <param name=\"joinOffset\"></param>\n        /// <param name=\"display\"></param>\n        /// <param name=\"displayIndex\"></param>\n        /// a\n        protected virtual void MapDisplayToRoomJoins(int displayIndex, uint joinOffset, IDisplay display)\n        {\n            var displayName = string.Format(\"Display {0} - \", displayIndex);\n\n\n            if (!(Room is IHasDefaultDisplay hasDefaultDisplay) || display != hasDefaultDisplay.DefaultDisplay)\n            {\n                return;\n            }\n            // Display volume\n            var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.AttributeName,\n                eSigIoMask.InputOutputSig);\n            defaultDisplayVolume.OutputSig.UserObject = new Action<ushort>(b =>\n            {\n                if (!(display is IBasicVolumeWithFeedback basicVolumeWithFeedback))\n                {\n                    return;\n                }\n\n                basicVolumeWithFeedback.SetVolume(b);\n                basicVolumeWithFeedback.VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig);\n            });\n\n\n            // Power on\n            var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + \"Power On\",\n                eSigIoMask.InputOutputSig);\n            defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b =>\n            {\n                if (!b)\n                {\n                    display.PowerOn();\n                }\n            });\n\n            // Power Off\n            var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + \"Power Off\",\n                eSigIoMask.InputOutputSig);\n            defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b =>\n            {\n                if (!b)\n                {\n                    display.PowerOff();\n                }\n            });\n\n\n            if (display is IHasPowerControlWithFeedback defaultTwoWayDisplay)\n            {\n                defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig);\n                defaultTwoWayDisplay.PowerIsOnFeedback.LinkComplementInputSig(defaultDisplayPowerOff.InputSig);\n            }\n\n            // Current Source\n            var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8,\n                displayName + \"Source None\", eSigIoMask.InputOutputSig);\n            defaultDisplaySourceNone.OutputSig.UserObject = new Action<bool>(b =>\n            {\n                if (!b)\n                {\n                    if (Room is IRunRouteAction runRouteAction)\n                    {\n                        runRouteAction.RunRouteAction(\"roomOff\", Room.SourceListKey);\n                    }\n                }\n            });\n        }\n\n        private void SetUpError()\n        {\n            // Roll up ALL device errors\n            _errorMessageRollUp = new StatusMonitorCollection(this);\n            foreach (var dev in DeviceManager.GetDevices())\n            {\n                if (dev is ICommunicationMonitor md)\n                {\n                    _errorMessageRollUp.AddMonitor(md.CommunicationMonitor);\n                    Debug.LogMessage(LogEventLevel.Verbose, this, \"Adding '{0}' to room's overall error monitor\",\n                        md.CommunicationMonitor.Parent.Key);\n                }\n            }\n            _errorMessageRollUp.Start();\n            FusionRoom.ErrorMessage.InputSig.StringValue = _errorMessageRollUp.Message;\n            _errorMessageRollUp.StatusChange +=\n                (o, a) => { FusionRoom.ErrorMessage.InputSig.StringValue = _errorMessageRollUp.Message; };\n        }\n\n        /// <summary>\n        /// Sets up a local occupancy sensor, such as one attached to a Fusion Scheduling panel.  The occupancy status of the room will be read from Fusion\n        /// </summary>\n        private void SetUpLocalOccupancy()\n        {\n            RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);\n\n            FusionRoom.FusionAssetStateChange += FusionRoom_FusionAssetStateChange;\n\n            // Build Occupancy Asset?\n            // Link sigs?\n\n            //Room.SetRoomOccupancy(this as IOccupancyStatusProvider, 0);\n        }\n\n        private void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args)\n        {\n            if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId ||\n                args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId)\n            {\n                RoomIsOccupiedFeedback.FireUpdate();\n            }\n        }\n\n        /// <summary>\n        /// Sets up remote occupancy that will relay the occupancy status determined by local system devices to Fusion\n        /// </summary>\n        private void SetUpRemoteOccupancy()\n        {\n            //  Need to have the room occupancy object first and somehow determine the slot number of the Occupancy asset but will not be able to use the UID from config likely.\n            //  Consider defining an object just for Room Occupancy (either eAssetType.Occupancy Sensor (local) or eAssetType.RemoteOccupancySensor (from Fusion sched. panel)) and reserving slot 4 for that asset (statics would start at 5)\n\n            //if (Room.OccupancyObj != null)\n            //{ \n\n            var tempOccAsset = _guids.OccupancyAsset;\n\n            if (tempOccAsset == null)\n            {\n                FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor);\n                tempOccAsset = FusionOccSensor;\n            }\n\n            var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name,\n                \"Occupancy Sensor\", tempOccAsset.InstanceId);\n\n            occSensorAsset.RoomOccupied.AddSigToRVIFile = true;\n\n            //var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, \"Occ Shutdown - Minutes\", eSigIoMask.InputOutputSig);\n\n            // Tie to method on occupancy object\n            //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b));\n\n\n            if (Room is IRoomOccupancy occRoom)\n            {\n                occRoom.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig);\n                occRoom.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;\n            }\n            RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString);\n\n            RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig);\n\n            //}\n        }\n\n        private void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            _roomOccupancyRemoteString = String.Format(RemoteOccupancyXml, e.BoolValue ? \"Occupied\" : \"Unoccupied\");\n            RoomOccupancyRemoteStringFeedback.FireUpdate();\n        }\n\n        /// <summary>\n        /// Helper to get the number from the end of a device's key string\n        /// </summary>\n        /// <returns>-1 if no number matched</returns>\n        private int ExtractNumberFromKey(string key)\n        {\n            var capture = System.Text.RegularExpressions.Regex.Match(key, @\"\\b(\\d+)\");\n            if (!capture.Success)\n            {\n                return -1;\n            }\n            return Convert.ToInt32(capture.Groups[1].Value);\n        }\n\n        /// <summary>\n        /// Event handler for when room source changes\n        /// </summary>\n        protected void Room_CurrentSourceInfoChange(SourceListItem info, ChangeType type)\n        {\n            // Handle null. Nothing to do when switching from or to null\n            if (info == null || info.SourceDevice == null)\n            {\n                return;\n            }\n\n            var dev = info.SourceDevice;\n            if (type == ChangeType.WillChange)\n            {\n                if (_sourceToFeedbackSigs.ContainsKey(dev))\n                {\n                    _sourceToFeedbackSigs[dev].BoolValue = false;\n                }\n            }\n            else\n            {\n                if (_sourceToFeedbackSigs.ContainsKey(dev))\n                {\n                    _sourceToFeedbackSigs[dev].BoolValue = true;\n                }\n                //var name = (room == null ? \"\" : room.Name);\n                CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name;\n            }\n        }\n\n        /// <summary>\n        /// Event handler for Fusion state changes\n        /// </summary>\n        /// <param name=\"device\"></param>\n        /// <param name=\"args\"></param>\n        protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args)\n        {\n            if (args.EventId == FusionEventIds.HelpMessageReceivedEventId)\n            {\n                this.LogInformation(\"Help message received from Fusion for room '{0}'\",\n             Room.Name);\n\n                this.LogDebug(\"Help message content: {0}\", FusionRoom.Help.OutputSig.StringValue);\n                // Fire help request event\n                HelpRequestResponseFeedback.FireUpdate();\n\n                if (!string.IsNullOrEmpty(FusionRoom.Help.OutputSig.StringValue))\n                {\n                    switch (FusionRoom.Help.OutputSig.StringValue)\n                    {\n                        case \"Please wait, a technician is on his / her way.\":\n                            // this.LogInformation(\"Please wait, a technician is on his / her way.\",\n                            //  Room.Name);\n\n                            _helpRequestStatus = eFusionHelpResponse.HelpOnTheWay;\n                            break;\n                        case \"Please call the helpdesk.\":\n                            // this.LogInformation(\"Please call the helpdesk.\");\n                            _helpRequestStatus = eFusionHelpResponse.CallHelpDesk;\n                            break;\n                        case \"Please wait, I will reschedule your meeting to a different room.\":\n                            // this.LogInformation(\"Please wait, I will reschedule your meeting to a different room.\",\n                            //  Room.Name);\n\n                            _helpRequestStatus = eFusionHelpResponse.ReschedulingMeeting;\n                            break;\n                        case \"I will be taking control of your system. Please be patient while I adjust the settings.\":\n                            // this.LogInformation(\"I will be taking control of your system. Please be patient while I adjust the settings.\",\n                            //  Room.Name);\n\n                            _helpRequestStatus = eFusionHelpResponse.TakingControl;\n                            break;\n                        default:\n                            // this.LogInformation(\"Unknown help request code received from Fusion for room '{0}'\",\n                            //  Room.Name);\n\n                            _helpRequestStatus = eFusionHelpResponse.None;\n                            break;\n                    }\n                }\n                else\n                {\n                    _helpRequestStatus = eFusionHelpResponse.None;\n                }\n\n                if (_helpRequestStatus == eFusionHelpResponse.None)\n                {\n                    _helpRequestSent = false;\n                    HelpRequestSentFeedback.FireUpdate();\n                }\n\n                HelpRequestStatusFeedback.FireUpdate();\n\n                if (_helpRequestTimeoutTimer != null)\n                {\n                    _helpRequestTimeoutTimer.Stop();\n                    _helpRequestTimeoutTimer.Elapsed -= OnTimedEvent;\n                    _helpRequestTimeoutTimer.Dispose();\n                    _helpRequestTimeoutTimer = null;\n                }\n            }\n\n\n            // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, \n            // even though they all contain sigs.\n\n            BoolOutputSig outSig;\n            if (args.UserConfiguredSigDetail is BooleanSigDataFixedName sigData)\n            {\n                outSig = sigData.OutputSig;\n                if (outSig.UserObject is Action<bool>)\n                {\n                    (outSig.UserObject as Action<bool>).Invoke(outSig.BoolValue);\n                }\n                else if (outSig.UserObject is Action<ushort>)\n                {\n                    (outSig.UserObject as Action<ushort>).Invoke(outSig.UShortValue);\n                }\n                else if (outSig.UserObject is Action<string>)\n                {\n                    (outSig.UserObject as Action<string>).Invoke(outSig.StringValue);\n                }\n                return;\n            }\n\n            var attrData = (args.UserConfiguredSigDetail as BooleanSigData);\n            if (attrData == null)\n            {\n                return;\n            }\n            outSig = attrData.OutputSig;\n            if (outSig.UserObject is Action<bool>)\n            {\n                (outSig.UserObject as Action<bool>).Invoke(outSig.BoolValue);\n            }\n            else if (outSig.UserObject is Action<ushort>)\n            {\n                (outSig.UserObject as Action<ushort>).Invoke(outSig.UShortValue);\n            }\n            else if (outSig.UserObject is Action<string>)\n            {\n                (outSig.UserObject as Action<string>).Invoke(outSig.StringValue);\n            }\n        }\n\n        /// <inheritdoc />\n        public void SendHelpRequest()\n        {\n\n            var now = DateTime.Now;\n\n            var breakString = _config.UseHtmlFormatForHelpRequests ? \"<BR>\" : \"\\r\\n\";\n\n            var date = now.ToString(\"MMMM dd, yyyy\");\n            var time = now.ToString(\"hh:mm tt\");\n            if (_config.Use24HourTimeFormat)\n            {\n                time = now.ToString(\"HH:mm\");\n            }\n\n            var requestString = $\"HR00: {breakString} Assistance has been requested from room {Room.Name}{breakString}on {date} at {time}\";\n\n            FusionRoom.Help.InputSig.StringValue = requestString;\n\n            this.LogInformation(\"Help request sent to Fusion from room '{0}'\", Room.Name);\n            this.LogDebug(\"Help request content: {0}\", FusionRoom.Help.InputSig.StringValue);\n\n            _helpRequestSent = true;\n            HelpRequestSentFeedback.FireUpdate();\n\n            if (UseHelpRequestTimer)\n            {\n                if (_helpRequestTimeoutTimer == null)\n                {\n                    _helpRequestTimeoutTimer = new Timer(HelpRequestTimeoutMs);\n                    _helpRequestTimeoutTimer.AutoReset = false;\n                    _helpRequestTimeoutTimer.Enabled = true;\n\n                    _helpRequestTimeoutTimer.Elapsed += OnTimedEvent;\n                }\n\n                _helpRequestTimeoutTimer.Interval = HelpRequestTimeoutMs;\n                _helpRequestTimeoutTimer.Start();\n\n                this.LogDebug(\"Help request timeout timer started for room '{0}' with timeout of {1} ms.\",\n    Room.Name, HelpRequestTimeoutMs);\n            }\n\n            _helpRequestStatus = eFusionHelpResponse.HelpRequested;\n            HelpRequestStatusFeedback.FireUpdate();\n        }\n\n        private void OnTimedEvent(object source, ElapsedEventArgs e)\n        {\n            this.LogInformation(\"Help request timeout reached for room '{0}'. Cancelling help request.\", Room.Name);\n            CancelHelpRequest();\n        }\n\n        /// <inheritdoc />\n        public void CancelHelpRequest()\n        {\n            if (_helpRequestSent)\n            {\n                FusionRoom.Help.InputSig.StringValue = \"\";\n                _helpRequestSent = false;\n                HelpRequestSentFeedback.FireUpdate();\n                _helpRequestStatus = eFusionHelpResponse.None;\n                HelpRequestStatusFeedback.FireUpdate();\n                Debug.LogMessage(LogEventLevel.Information, this, \"Help request cancelled for room '{0}'\", Room.Name);\n            }\n\n            if (_helpRequestTimeoutTimer != null)\n            {\n                _helpRequestTimeoutTimer.Stop();\n                _helpRequestTimeoutTimer.Elapsed -= OnTimedEvent;\n                _helpRequestTimeoutTimer.Dispose();\n                _helpRequestTimeoutTimer = null;\n                this.LogDebug(\"Help request timeout timer stopped for room '{0}'.\", Room.Name);\n            }\n        }\n\n        /// <inheritdoc />\n        public void ToggleHelpRequest()\n        {\n            if (_helpRequestSent)\n            {\n                CancelHelpRequest();\n            }\n            else\n            {\n                SendHelpRequest();\n            }\n        }\n\n    }\n\n\n    /// <summary>\n    /// Extensions to enhance Fusion room, asset and signal creation.\n    /// </summary>\n    public static class FusionRoomExtensions\n    {\n        /// <summary>\n        /// Creates and returns a fusion attribute. The join number will match the established Simpl\n        /// standard of 50+, and will generate a 50+ join in the RVI. It calls\n        /// FusionRoom.AddSig with join number - 49\n        /// </summary>\n        /// <returns>The new attribute</returns>\n        /// <summary>\n        /// CreateOffsetBoolSig method\n        /// </summary>\n        public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)\n        {\n            Debug.LogDebug(\"Creating Offset Bool Sig: {0} at Join {1}\", name, number);\n\n            if (number < 50)\n            {\n                throw new ArgumentOutOfRangeException(\"number\", \"Cannot be less than 50\");\n            }\n            number -= 49;\n            fr.AddSig(eSigType.Bool, number, name, mask);\n            return fr.UserDefinedBooleanSigDetails[number];\n        }\n\n        /// <summary>\n        /// Creates and returns a fusion attribute. The join number will match the established Simpl\n        /// standard of 50+, and will generate a 50+ join in the RVI. It calls\n        /// FusionRoom.AddSig with join number - 49\n        /// </summary>\n        /// <returns>The new attribute</returns>\n        /// <summary>\n        /// CreateOffsetUshortSig method\n        /// </summary>\n        public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)\n        {\n            Debug.LogDebug(\"Creating Offset UShort Sig: {0} at Join {1}\", name, number);\n\n            if (number < 50)\n            {\n                throw new ArgumentOutOfRangeException(\"number\", \"Cannot be less than 50\");\n            }\n            number -= 49;\n            fr.AddSig(eSigType.UShort, number, name, mask);\n            return fr.UserDefinedUShortSigDetails[number];\n        }\n\n        /// <summary>\n        /// Creates and returns a fusion attribute. The join number will match the established Simpl\n        /// standard of 50+, and will generate a 50+ join in the RVI. It calls\n        /// FusionRoom.AddSig with join number - 49\n        /// </summary>\n        /// <returns>The new attribute</returns>\n        /// <summary>\n        /// CreateOffsetStringSig method\n        /// </summary>\n        public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)\n        {\n            Debug.LogDebug(\"Creating Offset String Sig: {0} at Join {1}\", name, number);\n\n            if (number < 50)\n            {\n                throw new ArgumentOutOfRangeException(\"number\", \"Cannot be less than 50\");\n            }\n            number -= 49;\n            fr.AddSig(eSigType.String, number, name, mask);\n            return fr.UserDefinedStringSigDetails[number];\n        }\n\n        /// <summary>\n        /// Creates and returns a static asset\n        /// </summary>\n        /// <returns>the new asset</returns>\n        /// <summary>\n        /// CreateStaticAsset method\n        /// </summary>\n        public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type,\n            string instanceId)\n        {\n            try\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Adding Fusion Static Asset '{0}' to slot {1} with GUID: '{2}'\", name, number, instanceId);\n\n                fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId);\n                return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset;\n            }\n            catch (InvalidOperationException ex)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Error creating Static Asset for device: '{0}'.  Check that multiple devices don't have missing or duplicate uid properties in configuration. /r/nError: {1}\", name, ex);\n                return null;\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Error creating Static Asset: {0}\", e);\n                return null;\n            }\n        }\n\n        /// <summary>\n        /// CreateOccupancySensorAsset method\n        /// </summary>\n        public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name,\n            string type, string instanceId)\n        {\n            try\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'\", name, number,\n                    instanceId);\n\n                fr.AddAsset(eAssetType.OccupancySensor, number, name, type, instanceId);\n                return fr.UserConfigurableAssetDetails[number].Asset as FusionOccupancySensor;\n            }\n            catch (InvalidOperationException ex)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Error creating Static Asset for device: '{0}'.  Check that multiple devices don't have missing or duplicate uid properties in configuration.  Error: {1}\", name, ex);\n                return null;\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Error, \"Error creating Static Asset: {0}\", e);\n                return null;\n            }\n        }\n    }\n\n    //************************************************************************************************\n    /// <summary>\n    /// Extensions to enhance Fusion room, asset and signal creation.\n    /// </summary>\n    public static class FusionStaticAssetExtensions\n    {\n        /// <summary>\n        /// Tries to set a Fusion asset with the make and model of a device.\n        /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset.\n        /// Otherwise, does nothing.\n        /// </summary>\n        public static void TrySetMakeModel(this FusionStaticAsset asset, Device device)\n        {\n            if (device is IMakeModel mm)\n            {\n                asset.ParamMake.Value = mm.DeviceMake;\n                asset.ParamModel.Value = mm.DeviceModel;\n            }\n        }\n\n        /// <summary>\n        /// Tries to attach the AssetError input on a Fusion asset to a Device's\n        /// CommunicationMonitor.StatusChange event. Does nothing if the device is not \n        /// IStatusMonitor\n        /// </summary>\n        /// <param name=\"asset\"></param>\n        /// <param name=\"device\"></param>\n        public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device)\n        {\n            if (device is ICommunicationMonitor)\n            {\n                var monitor = (device as ICommunicationMonitor).CommunicationMonitor;\n                monitor.StatusChange += (o, a) =>\n                {\n                    // Link connected and error inputs on asset\n                    asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk;\n                    asset.AssetError.InputSig.StringValue = a.Status.ToString();\n                };\n                // set current value\n                asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk;\n                asset.AssetError.InputSig.StringValue = monitor.Status.ToString();\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a RoomInformation\n    /// </summary>\n    public class RoomInformation\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public RoomInformation()\n        {\n            FusionCustomProperties = new List<FusionCustomProperty>();\n        }\n\n        /// <summary>\n        /// Gets or sets the ID\n        /// </summary>\n        public string ID { get; set; }\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; set; }\n        /// <summary>\n        /// Gets or sets the Location\n        /// </summary>\n        public string Location { get; set; }\n        /// <summary>\n        /// Gets or sets the Description\n        /// </summary>\n        public string Description { get; set; }\n        /// <summary>\n        /// Gets or sets the TimeZone\n        /// </summary>\n        public string TimeZone { get; set; }\n        /// <summary>\n        /// Gets or sets the WebcamURL\n        /// </summary>\n        public string WebcamURL { get; set; }\n        /// <summary>\n        /// Gets or sets the BacklogMsg\n        /// </summary>\n        public string BacklogMsg { get; set; }\n        /// <summary>\n        /// Gets or sets the SubErrorMsg\n        /// </summary>\n        public string SubErrorMsg { get; set; }\n        /// <summary>\n        /// Gets or sets the EmailInfo\n        /// </summary>\n        public string EmailInfo { get; set; }\n        /// <summary>\n        /// Gets or sets the FusionCustomProperties\n        /// </summary>\n        public List<FusionCustomProperty> FusionCustomProperties { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a FusionCustomProperty\n    /// </summary>\n    public class FusionCustomProperty\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public FusionCustomProperty()\n        {\n        }\n\n        /// <summary>\n        /// Constructor with id\n        /// </summary>\n        /// <param name=\"id\"></param>\n        public FusionCustomProperty(string id)\n        {\n            ID = id;\n        }\n\n        /// <summary>\n        /// Gets or sets the ID\n        /// </summary>\n        public string ID { get; set; }\n        /// <summary>\n        /// Gets or sets the CustomFieldName\n        /// </summary>\n        public string CustomFieldName { get; set; }\n        /// <summary>\n        /// Gets or sets the CustomFieldType\n        /// </summary>\n        public string CustomFieldType { get; set; }\n        /// <summary>\n        /// Gets or sets the CustomFieldValue\n        /// </summary>\n        public string CustomFieldValue { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionControllerFactory.cs",
    "content": "using System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Fusion;\n\n/// <summary>\n/// Factory for creating IEssentialsRoomFusionController devices\n/// </summary>\npublic class IEssentialsRoomFusionControllerFactory : EssentialsDeviceFactory<IEssentialsRoomFusionController>\n{\n    /// <summary>\n    /// Constructor\n    /// </summary>\n    public IEssentialsRoomFusionControllerFactory()\n    {\n        TypeNames = new List<string>() { \"fusionRoom\" };\n    }\n\n    /// <summary>\n    /// Builds the device\n    /// </summary>\n    /// <param name=\"dc\"></param>\n    /// <returns></returns>\n    public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)\n    {\n        Debug.LogDebug(\"Factory Attempting to create new IEssentialsRoomFusionController Device\");\n\n\n        var properties = dc.Properties.ToObject<IEssentialsRoomFusionControllerPropertiesConfig>();\n\n        return new IEssentialsRoomFusionController(dc.Key, dc.Name, properties);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/IEssentialsRoomFusionControllerPropertiesConfig.cs",
    "content": "using Newtonsoft.Json;\nusing PepperDash.Core;\n\n/// <summary>\n/// Config properties for an IEssentialsRoomFusionController device\n/// </summary>\npublic class IEssentialsRoomFusionControllerPropertiesConfig\n{\n    /// <summary>\n    /// Gets or sets the IP ID of the Fusion Room Controller\n    /// </summary>\n    [JsonProperty(\"ipId\")]\n    public string IpId { get; set; }\n\n    /// <summary>\n    /// Gets the IP ID as a UInt16\n    /// </summary>\n    [JsonIgnore]\n    public uint IpIdInt\n    {\n        get\n        {\n            // Try to parse the IpId string to UInt16 as hex\n            if (ushort.TryParse(IpId, System.Globalization.NumberStyles.HexNumber, null, out ushort result))\n            {\n                return result;\n            }\n            else\n            {\n                Debug.LogWarning(\"Failed to parse IpId '{0}' as UInt16\", IpId);\n                return 0;\n            }\n        }\n    }\n\n    /// <summary>\n    /// Gets or sets the join map key\n    /// </summary>\n    [JsonProperty(\"joinMapKey\")]\n    public string JoinMapKey { get; set; }\n\n    /// <summary>\n    /// Gets or sets the room key associated with this Fusion Room Controller\n    /// </summary>\n    [JsonProperty(\"roomKey\")]\n    public string RoomKey { get; set; }\n\n    /// <summary>\n    /// Gets or sets whether to use the Fusion room name for this room\n    /// </summary>\n    /// <remarks>Defaults to true to preserve current behavior. Set to false to skip updating the room name from Fusion</remarks>\n    [JsonProperty(\"useFusionRoomName\")]\n    public bool UseFusionRoomName { get; set; } = true;\n\n    /// <summary>\n    /// Gets or sets whether to use HTML format for help requests\n    /// </summary>\n    [JsonProperty(\"useHtmlFormatForHelpRequests\")]\n    public bool UseHtmlFormatForHelpRequests { get; set; } = false;\n\n    /// <summary>\n    /// Gets or sets whether to use 24-hour time format\n    /// </summary>\n    [JsonProperty(\"use24HourTimeFormat\")]\n    public bool Use24HourTimeFormat { get; set; } = false;\n\n    /// <summary>\n    /// Gets or sets whether to use a timeout for help requests\n    /// </summary>\n    [JsonProperty(\"useTimeoutForHelpRequests\")]\n    public bool UseTimeoutForHelpRequests { get; set; } = false;\n\n    /// <summary>\n    /// Gets or sets the timeout duration for help requests in milliseconds\n    /// </summary>\n    [JsonProperty(\"helpRequestTimeoutMs\")]\n    public int HelpRequestTimeoutMs { get; set; } = 30000;\n\n    /// <summary>\n    /// Gets or sets whether to enable schedule push notifications\n    /// </summary>\n    /// <remarks>Defaults to false to skip getting schedule unless required</remarks>\n    [JsonProperty(\"enableSchedulePushNotifications\")]\n    public bool EnableSchedulePushNotifications { get; set; } = false;\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/IFusionHelpRequest.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.Fusion\n{\n    /// <summary>\n    /// Represents Fusion Help Request functionality\n    /// </summary>\n    public interface IFusionHelpRequest\n    {\n        /// <summary>\n        /// Feedback containing the response to a help request\n        /// </summary>\n        StringFeedback HelpRequestResponseFeedback { get; }\n\n        /// <summary>\n        /// Indicates whether a help request has been sent\n        /// </summary>\n        BoolFeedback HelpRequestSentFeedback { get; }\n\n        /// <summary>\n        /// Feedback containing the current status of the help request\n        /// </summary>\n        StringFeedback HelpRequestStatusFeedback { get; }\n\n        /// <summary>\n        /// Sends a help request\n        /// </summary>\n        void SendHelpRequest();\n\n        /// <summary>\n        /// Clears the current help request status\n        /// </summary>\n        void CancelHelpRequest();\n\n        /// <summary>\n        /// Toggles between sending and cancelling a help request\n        /// </summary>\n        void ToggleHelpRequest();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Fusion/eFusionHelpResponse.cs",
    "content": "\n\nnamespace PepperDash.Essentials.Core.Fusion\n{\n    /// <summary>\n    /// Enumeration of possible Fusion Help Responses based on the standard responses from Fusion\n    /// </summary>\n    public enum eFusionHelpResponse\n    {\n        /// <summary>\n        /// No help response\n        /// </summary>\n        None,\n        /// <summary>\n        /// Help has been requested\n        /// </summary>\n        HelpRequested,\n        /// <summary>\n        /// Help is on the way\n        /// </summary>\n        HelpOnTheWay,\n        /// <summary>\n        /// Please call the helpdesk.\n        /// </summary>\n        CallHelpDesk,\n        /// <summary>\n        /// Rescheduling meeting.\n        /// </summary>\n        ReschedulingMeeting,\n\n        /// <summary>\n        /// Technician taking control.\n        /// </summary>\n        TakingControl,\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Global/EthernetAdapterInfo.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a EthernetAdapterInfo\n    /// </summary>\n    public class EthernetAdapterInfo\n    {\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public EthernetAdapterType Type { get; set; }\n        /// <summary>\n        /// Gets or sets the DhcpIsOn\n        /// </summary>\n        public bool DhcpIsOn { get; set; }\n        /// <summary>\n        /// Gets or sets the Hostname\n        /// </summary>\n        public string Hostname { get; set; }\n        /// <summary>\n        /// Gets or sets the MacAddress\n        /// </summary>\n        public string MacAddress { get; set; }\n        /// <summary>\n        /// Gets or sets the IpAddress\n        /// </summary>\n        public string IpAddress { get; set; }\n        /// <summary>\n        /// Gets or sets the Subnet\n        /// </summary>\n        public string Subnet { get; set; }\n        /// <summary>\n        /// Gets or sets the Gateway\n        /// </summary>\n        public string Gateway { get; set; }\n        /// <summary>\n        /// Gets or sets the Dns1\n        /// </summary>\n        public string Dns1 { get; set; }\n        /// <summary>\n        /// Gets or sets the Dns2\n        /// </summary>\n        public string Dns2 { get; set; }\n        /// <summary>\n        /// Gets or sets the Dns3\n        /// </summary>\n        public string Dns3 { get; set; }\n        /// <summary>\n        /// Gets or sets the Domain\n        /// </summary>\n        public string Domain { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Global/Global.cs",
    "content": "﻿\n\nusing System;\nusing System.Linq;\nusing System.Text.RegularExpressions;\nusing System.Globalization;\nusing Crestron.SimplSharp;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharp.CrestronDataStore;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DM;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.License;\n\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing Newtonsoft.Json.Schema;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Global static class for Essentials\n    /// </summary>\n\tpublic static class Global\n\t{\n  /// <summary>\n  /// Gets or sets the ControlSystem\n  /// </summary>\n\t\tpublic static CrestronControlSystem ControlSystem { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Platform\n        /// </summary>\n        public static eDevicePlatform Platform { get { return CrestronEnvironment.DevicePlatform; } }\n\n        /// <summary>\n        /// Gets the collection of Ethernet Adapter Info\n        /// </summary>\n        public static Dictionary<short, EthernetAdapterInfo> EthernetAdapterInfoCollection { get; private set; }\n\n  /// <summary>\n  /// Gets or sets the LicenseManager\n  /// </summary>\n\t\tpublic static LicenseManager LicenseManager { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ProcessorSeries\n        /// </summary>\n        public static eCrestronSeries ProcessorSeries { get { return CrestronEnvironment.ProgramCompatibility; } }\n\n        // TODO: consider making this configurable later\n        /// <summary>\n        /// The CultureInfo for formatting\n        /// </summary>\n        public static IFormatProvider Culture = CultureInfo.CreateSpecificCulture(\"en-US\");\n\n        /// <summary>\n        /// True when the processor type is a DMPS variant\n        /// </summary>\n        public static bool ControlSystemIsDmpsType\n        {\n            get\n            {\n                if(ControlSystem.SystemControl != null)\n                {\n                    if(ControlSystem.SystemControl.SystemControlType > 0)\n                    {\n                        return true;\n                    }         \n                }\n                return false;\n            }\n        }\n\n        /// <summary>\n        /// True when the processor type is a DMPS 4K variant\n        /// </summary>\n        public static bool ControlSystemIsDmps4kType\n        {\n            get\n            {\n                if(ControlSystem.SystemControl != null)\n                {\n                    if(ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K150CSystemControl ||\n                       ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K200CSystemControl ||\n                       ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K250CSystemControl ||\n                       ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K300CSystemControl ||\n                       ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K350CSystemControl)\n                    {\n                        return true;\n                    }         \n                }\n                return false;\n            }\n        }\n\n                /// <summary>\n        /// True when the processor type is a DMPS 4K 200/300/250/350 variant\n        /// </summary>\n        public static bool ControlSystemIsDmps4k3xxType\n        {\n            get\n            {\n                if(ControlSystem.SystemControl != null)\n                {\n                    if(ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K200CSystemControl ||\n                       ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K250CSystemControl ||\n                       ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K300CSystemControl ||\n                       ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K350CSystemControl)\n                    {\n                        return true;\n                    }         \n                }\n                return false;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the FilePathPrefix\n        /// </summary>\n        public static string FilePathPrefix { get; private set; }\n\n        /// <summary>\n        /// The file path prefix to the applciation directory\n        /// </summary>\n        public static string ApplicationDirectoryPathPrefix \n        {\n            get\n            {\n                return Crestron.SimplSharp.CrestronIO.Directory.GetApplicationDirectory();\n            }\n        }\n\n        /// <summary>\n        /// Returns the directory separator character based on the running OS\n        /// </summary>\n        public static char DirectorySeparator\n        {\n            get\n            {\n                return System.IO.Path.DirectorySeparatorChar;\n            }\n        }\n\n        /// <summary>\n        /// Wildcarded config file name for global reference\n        /// </summary>\n        public const string ConfigFileName = \"*configurationFile*.json\";\n\n        /// <summary>\n        /// Sets the file path prefix\n        /// </summary>\n        /// <param name=\"prefix\"></param>\n        public static void SetFilePathPrefix(string prefix)\n        {\n            FilePathPrefix = prefix;\n            Debug.LogMessage(LogEventLevel.Information, \"File Path Prefix set to '{0}'\", FilePathPrefix);\n        }\n\n        static string _AssemblyVersion;\n\n        /// <summary>\n        /// Gets the Assembly Version of Essentials\n        /// </summary>\n        /// <returns>The Assembly Version at Runtime</returns>\n        public static string AssemblyVersion\n        {\n            get\n            {\n                return _AssemblyVersion;\n            }\n            private set\n            {\n                _AssemblyVersion = value;\n            }\n        }\n\n        /// <summary>\n        /// Sets the Assembly version to the version of the Essentials Library\n        /// </summary>\n        /// <param name=\"assemblyVersion\"></param>\n        /// <summary>\n        /// SetAssemblyVersion method\n        /// </summary>\n        public static void SetAssemblyVersion(string assemblyVersion)\n        {\n            AssemblyVersion = assemblyVersion;\n        }\n\n        /// <summary>\n        /// Checks to see if the running version is in the list of development versions.  If so, checks to see if it meets the minimum version requirement.\n        /// If not in the list, returns false.  For beta versions (0.xx.yy), will always return true.\n        /// </summary>\n        /// <param name=\"developmentVersions\">list of development versions</param>\n        /// <param name=\"minimumVersion\">minimum version</param>\n        /// <returns></returns>\n\t    public static bool IsRunningDevelopmentVersion(List<string> developmentVersions, string minimumVersion)\n\t    {\n\t        if (Regex.Match(AssemblyVersion, @\"^(\\d*).(\\d*).(\\d*).*\").Groups[1].Value == \"0\")\n\t        {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Running Local Build.  Bypassing Dependency Check.\");\n                return true;\n\t        }\n\n\t        if (developmentVersions == null)\n\t        {\n\t            Debug.LogMessage(LogEventLevel.Information, \n                    \"Development Plugin does not specify a list of versions.  Loading plugin may not work as expected.  Checking Minumum version\");\n                return IsRunningMinimumVersionOrHigher(minimumVersion);\n\t        }\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Comparing running version '{0}' to minimum versions '{1}'\", AssemblyVersion, developmentVersions);\n\n\t        var versionMatch = developmentVersions.FirstOrDefault(x => x == AssemblyVersion);\n\n\t        if (String.IsNullOrEmpty(versionMatch))\n\t        {\n\t            Debug.LogMessage(LogEventLevel.Information, \"Essentials Build does not match any builds required for plugin load.  Bypassing Plugin Load.\");\n\t            return false;\n\t        }\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Essentials Build {0} matches list of development builds\", AssemblyVersion);\n\t        return IsRunningMinimumVersionOrHigher(minimumVersion);\n\n\n\n\t    }\n\n        /// <summary>\n        /// Checks to see if the running version meets or exceed the minimum specified version.  For beta versions (0.xx.yy), will always return true.\n        /// </summary>\n        /// <param name=\"minimumVersion\">Minimum specified version in format of xx.yy.zz</param>\n        /// <returns>Returns true if the running version meets or exceeds the minimum specified version</returns>\n        public static bool IsRunningMinimumVersionOrHigher(string minimumVersion)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, \"Comparing running version '{0}' to minimum version '{1}'\", AssemblyVersion, minimumVersion);\n\n            if (String.IsNullOrEmpty(minimumVersion))\n            {\n                Debug.LogMessage(LogEventLevel.Information,\"Plugin does not specify a minimum version. Loading plugin may not work as expected. Proceeding with loading plugin\");\n                return true;\n            }\n            \n            var runtimeVersion = Regex.Match(AssemblyVersion, @\"^(\\d*).(\\d*).(\\d*).*\");\n\n            var runtimeVersionMajor = Int16.Parse(runtimeVersion.Groups[1].Value);\n            var runtimeVersionMinor = Int16.Parse(runtimeVersion.Groups[2].Value);\n            var runtimeVersionBuild = Int16.Parse(runtimeVersion.Groups[3].Value);\n\n            var runtimeVer = new Version(runtimeVersionMajor, runtimeVersionMinor, runtimeVersionBuild);\n\n            Version minimumVer;\n            try\n            {\n                minimumVer = new Version(minimumVersion);\n            }\n            catch\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"unable to parse minimum version {0}. Bypassing plugin load.\", minimumVersion);\n                return false;\n            }\n\n\n            // Check for beta build version\n            if (runtimeVer.Major != 0)\n            {\n                return runtimeVer.CompareTo(minimumVer) >= 0;\n            }\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Running Local Build.  Bypassing Dependency Check.\");\n            return true;\n\n            /*\n            var minVersion = Regex.Match(minimumVersion, @\"^(\\d*).(\\d*).(\\d*)$\");\n\n            if(!minVersion.Success)\n            {\n                \n            }\n\n            var minVersionMajor = Int16.Parse(minVersion.Groups[1].Value);\n            var minVersionMinor = Int16.Parse(minVersion.Groups[2].Value);\n            var minVersionBuild = Int16.Parse(minVersion.Groups[3].Value);\n\n\n\n            if (minVersionMajor > runtimeVersionMajor)\n                return false;\n\n            if (minVersionMinor > runtimeVersionMinor)\n                return false;\n\n            if (minVersionBuild > runtimeVersionBuild)\n                return false;\n\n            return true;\n             */\n        }\n\n\t\tstatic Global()\n\t\t{\n\t\t\t// Fire up CrestronDataStoreStatic\n\t\t\tvar err = CrestronDataStoreStatic.InitCrestronDataStore();\n\t\t\tif (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n\t\t\t{\n\t\t\t\tCrestronConsole.PrintLine(\"Error starting CrestronDataStoreStatic: {0}\", err);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Global/JobTimer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Represents a JobTimer\n\t/// </summary>\n\tpublic static class JobTimer\n\t{\n\t\tstatic CTimer MinuteTimer;\n\n\t\tstatic List<JobTimerItem> Items = new List<JobTimerItem>();\n\n\t\t/// <summary>\n\t\t/// AddAction method\n\t  \t/// </summary>\n\t\t/// <param name=\"act\">action to add</param>\n\t\tpublic static void AddAction(Action act)\n\t\t{\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// AddJobTimerItem method\n\t\t/// </summary>\n\t\t/// <param name=\"item\">JobTimerItem to add</param>\n\t\tpublic static void AddJobTimerItem(JobTimerItem item)\n\t\t{\n\t\t\tvar existing = Items.FirstOrDefault(i => i.Key == item.Key);\n\t\t\tif (existing != null)\n\t\t\t{\n\t\t\t\tItems.Remove(existing);\n\t\t\t}\n\t\t\tItems.Add(item);\n\t\t}\n\n\t\tstatic void CheckAndRunTimer()\n\t\t{\n\t\t\tif (Items.Count > 0 && MinuteTimer == null)\n\t\t\t{\n\t\t\t\tMinuteTimer = new CTimer(o => MinuteTimerCallback(), null, 60000, 60000);\n\t\t\t}\n\t\t}\n\n\t\tstatic void MinuteTimerCallback()\n\t\t{\n\t\t\t\n\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a JobTimerItem\n /// </summary>\n\tpublic class JobTimerItem\n\t{\n\t\t/// <summary>\n\t\t/// Key property\n\t\t/// </summary>\n\t\tpublic string Key { get; private set; }\n\n\t\t/// <summary>\n\t\t/// JobAction property\n\t\t/// </summary>\n\t\tpublic Action JobAction { get; private set; }\n\n\t\t/// <summary>\n\t\t/// CycleType property\n\t\t/// </summary>\n\t\tpublic eJobTimerCycleTypes CycleType { get; private set; }\n\n\t\t/// <summary>\n\t\t/// RunNextAt property\n\t\t/// </summary>\n\t\tpublic DateTime RunNextAt { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\">item key</param>\n\t\t/// <param name=\"cycle\">cycle type</param>\n\t\t/// <param name=\"act\">action to run</param>\n\t\tpublic JobTimerItem(string key, eJobTimerCycleTypes cycle, Action act)\n\t\t{\n\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// JobTimerCycleTypes enum\n\t/// </summary>\n\tpublic enum eJobTimerCycleTypes\n\t{\n\t\t/// <summary>\n\t\t/// RunEveryDay property\n\t\t/// </summary>\n        RunEveryDay,\n\n\t\t/// <summary>\n\t\t/// RunEveryHour property\n\t\t/// </summary>\n\t\tRunEveryHour,\n\n\t\t/// <summary>\n\t\t/// RunEveryHalfHour property\n\t\t/// </summary>\n\t\tRunEveryHalfHour,\n\n\t\t/// <summary>\n\t\t/// RunEveryMinute property\n\t\t/// </summary>\n\t\tRunEveryMinute\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Global/Scheduler.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.Reflection;\nusing Crestron.SimplSharp.Scheduler;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Fusion;\nusing PepperDash.Essentials.Room.Config;\nusing Serilog.Events;\nusing Activator = System.Activator;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Global Scheduler for the system\n    /// </summary>\n    public static class Scheduler\n    {\n        private static readonly Dictionary<string, ScheduledEventGroup> EventGroups = new Dictionary<string,ScheduledEventGroup>();\n\n        static Scheduler()\n        {\n            CrestronConsole.AddNewConsoleCommand(DeleteEventGroup, \"DeleteEventGroup\", \"Deletes the event group by key\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(ClearEventsFromGroup, \"ClearAllEvents\", \"Clears all scheduled events for this group\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(ListAllEventGroups, \"ListAllEventGroups\", \"Lists all the event groups by key\", ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(ListAllEventsForGroup, \"ListEventsForGroup\",\n                \"Lists all events for the given group\", ConsoleAccessLevelEnum.AccessOperator);\n        }\n\n\n        static void DeleteEventGroup(string groupName)\n        {\n            if (EventGroups.ContainsKey(groupName))\n            {\n                var group = EventGroups[groupName];\n                 \n                EventGroups.Remove(groupName);\n\n                group.Dispose();\n\n                group = null;\n            }\n        }   \n\n        /// <summary>\n        /// Clears (deletes) all events from a group\n        /// </summary>\n        /// <param name=\"groupName\"></param>\n        static void ClearEventsFromGroup(string groupName)\n        {\n            if (!EventGroups.ContainsKey(groupName))\n            {\n                Debug.LogMessage(LogEventLevel.Information,\n                    \"[Scheduler]: Unable to delete events from group '{0}'.  Group not found in EventGroups dictionary.\", null,\n                    groupName);\n                return;\n            }\n\n            var group = EventGroups[groupName];\n\n            if (group != null)\n            {\n                group.ClearAllEvents();\n\n                Debug.LogMessage(LogEventLevel.Information, \"[Scheduler]: All events deleted from group '{0}'\", null, groupName);\n            }\n            else\n                Debug.LogMessage(LogEventLevel.Information,\n                    \"[Scheduler]: Unable to delete events from group '{0}'.  Group not found in EventGroups dictionary.\", null,\n                    groupName);\n        }\n\n        static void ListAllEventGroups(string command)\n        {\n            CrestronConsole.ConsoleCommandResponse(\"Event Groups:\");\n            foreach (var group in EventGroups)\n            {\n                CrestronConsole.ConsoleCommandResponse($\"{group.Key}\");\n            }\n        }\n\n        static void ListAllEventsForGroup(string args)\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Getting events for group {0}...\", null, args);\n\n            ScheduledEventGroup group;\n\n            if (!EventGroups.TryGetValue(args, out group))\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Unabled to get event group for key {0}\", null, args);\n                return;\n            }\n\n            foreach (var evt in group.ScheduledEvents)\n            {\n                CrestronConsole.ConsoleCommandResponse(\n$@\"\n****Event key {evt.Key}****\nEvent state: {evt.Value.EventState}\nEvent date/time: {evt.Value.DateAndTime}\nPersistent: {evt.Value.Persistent}\nAcknowlegable: {evt.Value.Acknowledgeable}\nRecurrence: {evt.Value.Recurrence.Recurrence}\nRecurrence Days: {evt.Value.Recurrence.RecurrenceDays}\n********************\");\n            }\n        }\n\n        /// <summary>\n        /// Adds the event group to the global list\n        /// </summary>\n        /// <returns></returns>\n        /// <summary>\n        /// AddEventGroup method\n        /// </summary>\n        public static void AddEventGroup(ScheduledEventGroup eventGroup)\n        {\n            // Add this group to the global collection\n            if (!EventGroups.ContainsKey(eventGroup.Name))\n                EventGroups.Add(eventGroup.Name, eventGroup);\n        }\n\n        /// <summary>\n        /// Removes the event group from the global list\n        /// </summary>\n        /// <param name=\"eventGroup\"></param>\n        /// <summary>\n        /// RemoveEventGroup method\n        /// </summary>\n        public static void RemoveEventGroup(ScheduledEventGroup eventGroup)\n        {\n            if(!EventGroups.ContainsKey(eventGroup.Name))\n                EventGroups.Remove(eventGroup.Name);\n        }\n\n        /// <summary>\n        /// Gets the event group by key\n        /// </summary>\n        /// <param name=\"key\">key of the event group</param>\n        /// <returns></returns>\n        public static ScheduledEventGroup GetEventGroup(string key)\n        {\n            ScheduledEventGroup returnValue;\n\n            return EventGroups.TryGetValue(key, out returnValue) ? returnValue : null;\n        }\n    }\n\n    /// <summary>\n    /// SchedulerUtilities class\n    /// </summary>\n    public static class SchedulerUtilities\n    {\n        /// <summary>\n        /// Checks the day of week in eventTime to see if it matches the weekdays defined in the recurrence enum.\n        /// </summary>\n        /// <param name=\"eventTime\"></param>\n        /// <param name=\"recurrence\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// CheckIfDayOfWeekMatchesRecurrenceDays method\n        /// </summary>\n        public static bool CheckIfDayOfWeekMatchesRecurrenceDays(DateTime eventTime, ScheduledEventCommon.eWeekDays recurrence)\n        {\n            bool isMatch = false;\n\n            var dayOfWeek = eventTime.DayOfWeek;\n\n            Debug.LogMessage(LogEventLevel.Debug, \"[Scheduler]: eventTime day of week is: {0}\",null, dayOfWeek);\n            switch (dayOfWeek)\n            {   \n                case DayOfWeek.Sunday:\n                    {\n                        if ((recurrence & ScheduledEventCommon.eWeekDays.Sunday) == ScheduledEventCommon.eWeekDays.Sunday)\n                            isMatch = true;\n                        break;\n                    }\n                case DayOfWeek.Monday:\n                    {\n                        if ((recurrence & ScheduledEventCommon.eWeekDays.Monday) == ScheduledEventCommon.eWeekDays.Monday)\n                            isMatch = true;\n                        break;\n                    }\n                case DayOfWeek.Tuesday:\n                    {\n                        if ((recurrence & ScheduledEventCommon.eWeekDays.Tuesday) == ScheduledEventCommon.eWeekDays.Tuesday)\n                            isMatch = true;\n                        break;\n                    }\n                case DayOfWeek.Wednesday:\n                    {\n                        if ((recurrence & ScheduledEventCommon.eWeekDays.Wednesday) == ScheduledEventCommon.eWeekDays.Wednesday)\n                            isMatch = true;\n                        break;\n                    }\n                case DayOfWeek.Thursday:\n                    {\n                        if ((recurrence & ScheduledEventCommon.eWeekDays.Thursday) == ScheduledEventCommon.eWeekDays.Thursday)\n                            isMatch = true;\n                        break;\n                    }\n                case DayOfWeek.Friday:\n                    {\n                        if ((recurrence & ScheduledEventCommon.eWeekDays.Friday) == ScheduledEventCommon.eWeekDays.Friday)\n                            isMatch = true;\n                        break;\n                    }\n                case DayOfWeek.Saturday:\n                    {\n                        if ((recurrence & ScheduledEventCommon.eWeekDays.Saturday) == ScheduledEventCommon.eWeekDays.Saturday)\n                            isMatch = true;\n                        break;\n                    }\n            }\n\n            Debug.LogMessage(LogEventLevel.Debug, \"[Scheduler]: eventTime day of week matches recurrence days: {0}\", isMatch);\n\n            return isMatch;\n        }\n\n        /// <summary>\n        /// CheckEventTimeForMatch method\n        /// </summary>\n        public static bool CheckEventTimeForMatch(ScheduledEvent evnt, DateTime time)\n        {\n            return evnt.DateAndTime.Hour == time.Hour && evnt.DateAndTime.Minute == time.Minute;\n        }\n\n        /// <summary>\n        /// CheckEventRecurrenceForMatch method\n        /// </summary>\n        public static bool CheckEventRecurrenceForMatch(ScheduledEvent evnt, ScheduledEventCommon.eWeekDays days)\n        {\n            return evnt.Recurrence.RecurrenceDays == days;\n        }\n\n        /// <summary>\n        /// CreateEventFromConfig method\n        /// </summary>\n        public static void CreateEventFromConfig(ScheduledEventConfig config, ScheduledEventGroup group, ScheduledEvent.UserEventCallBack handler)\n        {\n            try\n            {\n                if (group == null)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Unable to create event. Group is null\", null, null);\n                    return;\n                }\n                var scheduledEvent = new ScheduledEvent(config.Key, group)\n                {\n                    Acknowledgeable = config.Acknowledgeable,\n                    Persistent = config.Persistent\n                };\n\n                scheduledEvent.UserCallBack += handler;\n\n                scheduledEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday);\n\n                var eventTime = DateTime.Parse(config.Time);\n\n                if (DateTime.Now > eventTime)\n                {\n                    eventTime = eventTime.AddDays(1);\n                }\n\n                Debug.LogMessage(LogEventLevel.Verbose, \"[Scheduler] Current Date day of week: {0} recurrence days: {1}\", null, eventTime.DayOfWeek,\n                    config.Days);\n\n                var dayOfWeekConverted = ConvertDayOfWeek(eventTime);\n\n                Debug.LogMessage(LogEventLevel.Debug, \"[Scheduler] eventTime Day: {0}\", null, dayOfWeekConverted);\n\n                while (!dayOfWeekConverted.IsFlagSet(config.Days))\n                {\n                    eventTime = eventTime.AddDays(1);\n\n                    dayOfWeekConverted = ConvertDayOfWeek(eventTime);\n                }\n\n                scheduledEvent.DateAndTime.SetAbsoluteEventTime(eventTime);\n\n                scheduledEvent.Recurrence.Weekly(config.Days);\n\n                Debug.LogMessage(LogEventLevel.Verbose, $\"[Scheduler] Event State: {scheduledEvent.EventState}\", null, null);\n\n                if (config.Enable && scheduledEvent.EventState != ScheduledEventCommon.eEventState.Enabled)\n                {\n                    scheduledEvent.Enable();\n                }\n                else if (!config.Enable && scheduledEvent.EventState != ScheduledEventCommon.eEventState.Disabled)\n                {\n                    scheduledEvent.Disable();\n                }\n \n            }\n            catch (Exception e)\n            {\n\n                Debug.LogMessage(LogEventLevel.Error, \"Error creating scheduled event: {0}\", null, e);\n            }\n        }\n\n        private static ScheduledEventCommon.eWeekDays ConvertDayOfWeek(DateTime eventTime)\n        {\n            return (ScheduledEventCommon.eWeekDays) Enum.Parse(typeof(ScheduledEventCommon.eWeekDays), eventTime.DayOfWeek.ToString(), true);\n        }\n\n        private static bool IsFlagSet<T>(this T value, T flag) where T : struct\n        {\n            CheckIsEnum<T>(true);\n\n            var lValue = Convert.ToInt64(value);\n            var lFlag = Convert.ToInt64(flag);\n\n            return (lValue & lFlag) != 0;\n        }\n\n        private static void CheckIsEnum<T>(bool withFlags)\n        {\n            if (!typeof(T).IsEnum)\n                throw new ArgumentException(string.Format(\"Type '{0}' is not an enum\", typeof(T).FullName));\n            if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))\n                throw new ArgumentException(string.Format(\"Type '{0}' doesn't have the 'Flags' attribute\", typeof(T).FullName));\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/InUseTracking/IInUseTracking.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Defines a class that uses an InUseTracker\n\t/// </summary>\n\tpublic interface IInUseTracking\n\t{\n\t\t/// <summary>\n\t\t/// Gets the InUseTracker\n\t\t/// </summary>\n\t\tInUseTracking InUseTracker { get; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/InUseTracking/InUseTracking.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Provides in use tracking.  Objects can register with this.  InUseFeedback can provide \n\t/// events when usage changes.\n\t/// </summary>\n\tpublic class InUseTracking\n\t{\n\t\t/// <summary>\n\t\t/// Returns a copied list of all users of this tracker.\n\t\t/// </summary>\n\t\tpublic IEnumerable<InUseTrackingObject> Users { get { return new List<InUseTrackingObject>(_Users); } }\n\t\tList<InUseTrackingObject> _Users = new List<InUseTrackingObject>();\n\n\t\t/// <summary>\n\t\t/// Feedback that changes when this goes in/out of use\n\t\t/// </summary>\n\t\tpublic BoolFeedback InUseFeedback { get; private set; }\n\n  /// <summary>\n  /// Gets or sets the InUseCountFeedback\n  /// </summary>\n\t\tpublic IntFeedback InUseCountFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic InUseTracking()\n\t\t{\n\t\t\tInUseFeedback = new BoolFeedback(() => _Users.Count > 0);\n\t\t\tInUseCountFeedback = new IntFeedback(() => _Users.Count);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Add a \"user\" object to this tracker. A user can be added to this tracker \n\t\t/// multiple times, provided that the label is different\n\t\t/// </summary>\n\t\t/// <param name=\"label\">A label to identify the instance of the user. Treated like a \"role\", etc.</param>\n\t\t/// <param name=\"objectToAdd\">The object to add</param>\n\t\tpublic void AddUser(object objectToAdd, string label)\n\t\t{\n\t\t\t// check if an exact object/label pair exists and ignore if so.  No double-registers.\n\t\t\tvar check = _Users.FirstOrDefault(u => u.Label == label && u.User == objectToAdd);\n\t\t\tif (check != null) return;\n\n\t\t\tvar prevCount = _Users.Count;\n\t\t\t_Users.Add(new InUseTrackingObject(objectToAdd, label));\n\t\t\t// if this is the first add, fire an update\n\t\t\tif (prevCount == 0 && _Users.Count > 0)\n\t\t\t\tInUseFeedback.FireUpdate();\n\t\t\tInUseCountFeedback.FireUpdate();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// RemoveUser method\n\t\t/// </summary>\n\t\t/// <param name=\"label\">The label of the user to remove</param>\n\t\t/// <param name=\"objectToRemove\">The object to remove</param>\n\t\tpublic void RemoveUser(object objectToRemove, string label)\n\t\t{\n\t\t\t// Find the user object if exists and remove it\n\t\t\tvar toRemove = _Users.FirstOrDefault(u => u.Label == label && u.User == objectToRemove);\n\t\t\tif (toRemove != null)\n\t\t\t{\n\t\t\t\t_Users.Remove(toRemove);\n\t\t\t\tif (_Users.Count == 0)\n\t\t\t\t\tInUseFeedback.FireUpdate();\n\t\t\t\tInUseCountFeedback.FireUpdate();\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a InUseTrackingObject\n\t/// </summary>\n\tpublic class InUseTrackingObject\n\t{\n\t\t/// <summary>\n\t\t/// The label of the user\n\t\t/// </summary>\n\t\tpublic string Label { get; private set; }\n\n\t\t/// <summary>\n\t\t/// The user object\n\t\t/// </summary>\n\t\tpublic object User { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"user\">user using the object</param>\n\t\t/// <param name=\"label\">label for the object</param>\n\t\tpublic InUseTrackingObject(object user, string label)\n\t\t{\n\t\t\tUser = user;\n\t\t\tLabel = label;\n\t\t}\n\t}\n\n\t//public class InUseEventArgs\n\t//{\n\t//    public int EventType { get; private set; }\n\t//    public InUseTracking Tracker { get; private set; }\n\n\t//    public InUseEventArgs(InUseTracking tracker, int eventType)\n\t//    {\n\t//        Tracker = tracker;\n\t//        EventType = eventType;\n\t//    }\n\t//}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Interfaces/ILogStrings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.Interfaces\n{\n\t/// <summary>\n\t/// Defines the contract for ILogStrings\n\t/// </summary>\n\t[Obsolete(\"ILogStrings is no longer supported and will be removed in a future release.\")]\n\tpublic interface ILogStrings : IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// Defines a class that is capable of logging a string\n\t\t/// </summary>\n\t\tvoid SendToLog(IKeyed device, string logMessage);\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Interfaces/ILogStringsWithLevel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.Interfaces\n{\n\t/// <summary>\n\t/// Defines the contract for ILogStringsWithLevel\n\t/// </summary>\n\t[Obsolete(\"ILogStringsWithLevel is no longer supported and will be removed in a future release.\")]\n\tpublic interface ILogStringsWithLevel : IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// Defines a class that is capable of logging a string with an int level\n\t\t/// </summary>\n\t\tvoid SendToLog(IKeyed device, Debug.ErrorLogLevel level, string logMessage);\n\t}\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/JoinMaps/JoinMapBase.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Reflection;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\n\nusing Newtonsoft.Json;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// JoinMapHelper class\n    /// </summary>\n    public static class JoinMapHelper\n    {\n        /// <summary>\n        /// Attempts to get the serialized join map from config\n        /// </summary>\n        /// <param name=\"joinMapKey\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetSerializedJoinMapForDevice method\n        /// </summary>\n        public static string GetSerializedJoinMapForDevice(string joinMapKey)\n        {\n            if (string.IsNullOrEmpty(joinMapKey))\n                return null;\n\n            var joinMap = ConfigReader.ConfigObject.JoinMaps[joinMapKey];\n\n            return joinMap.ToString();\n        }\n\n        /// <summary>\n        /// Attempts to get the serialized join map from config\n        /// </summary>\n        /// <param name=\"joinMapKey\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetJoinMapForDevice method\n        /// </summary>\n        public static string GetJoinMapForDevice(string joinMapKey)\n        {\n            return GetSerializedJoinMapForDevice(joinMapKey);\n        }\n\n        /// <summary>\n        /// Attempts to find a custom join map by key and returns it deserialized if found\n        /// </summary>\n        /// <param name=\"joinMapKey\"></param>\n        /// <returns></returns>\n        public static Dictionary<string, JoinData> TryGetJoinMapAdvancedForDevice(string joinMapKey)\n        {\n            try\n            {\n                if (string.IsNullOrEmpty(joinMapKey))\n                    return null;\n\n                if (!ConfigReader.ConfigObject.JoinMaps.ContainsKey(joinMapKey))\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"No Join Map found in config with key: '{0}'\", joinMapKey);\n                    return null;\n                }\n\n                Debug.LogMessage(LogEventLevel.Verbose, \"Attempting to load custom join map with key: {0}\", joinMapKey);\n\n                var joinMapJToken = ConfigReader.ConfigObject.JoinMaps[joinMapKey];\n\n                if (joinMapJToken == null)\n                    return null;\n\n                var joinMapData = joinMapJToken.ToObject<Dictionary<string, JoinData>>();\n\n                return joinMapData;\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Error getting join map for key: '{0}'.  Error: {1}\", joinMapKey, e);\n                return null;\n            }\n        }\n\n    }\n\n    /// <summary>\n    /// Base class for join maps\n    /// </summary>\n    public abstract class JoinMapBaseAdvanced\n    {\n        /// <summary>\n        /// The join offset\n        /// </summary>\n        protected uint JoinOffset;\n\n        /// <summary>\n        /// The collection of joins and associated metadata\n        /// </summary>\n        public Dictionary<string, JoinDataComplete> Joins { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"joinStart\">join start offset</param>\n        protected JoinMapBaseAdvanced(uint joinStart)\n        {\n            Joins = new Dictionary<string, JoinDataComplete>();\n\n            JoinOffset = joinStart - 1;\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"joinStart\">join start offset</param>\n        /// <param name=\"type\">type of joinstart</param>\n        protected JoinMapBaseAdvanced(uint joinStart, Type type):this(joinStart)\n        {\n            AddJoins(type);\n        }\n\n        /// <summary>\n        /// AddJoins method\n        /// </summary>\n        /// <param name=\"type\">type of join to add</param>\n        protected void AddJoins(Type type)\n        {            \n            var fields =\n                type.GetFields(BindingFlags.Public | BindingFlags.Instance)\n                    .Where(f => f.IsDefined(typeof (JoinNameAttribute), true)).ToList();\n\n            Debug.LogMessage(LogEventLevel.Debug, \"Got {fields} with JoinNameAttribute\", fields.Count);\n\n            foreach (var field in fields)\n            {\n                var childClass = Convert.ChangeType(this, type, null);\n\n                //this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields.\n\n                if (!(field.GetValue(childClass) is JoinDataComplete value))\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Unable to cast base class to {0}\", type.Name);\n                    continue;\n                }\n\n                value.SetJoinOffset(JoinOffset);\n\n                var joinName = value.GetNameAttribute(field);\n\n                if (string.IsNullOrEmpty(joinName)) continue;\n\n                Joins.Add(joinName, value);\n            }\n\n\n            if (Debug.Level > 0)\n            {\n                PrintJoinMapInfo();\n            }\n        }\n\n        /// <summary>\n        /// PrintJoinMapInfo method\n        /// </summary>\n        public void PrintJoinMapInfo()\n        {\n            var sb = JoinmapStringBuilder();\n\n            CrestronConsole.ConsoleCommandResponse(sb.ToString());\n        }\n\n        private StringBuilder JoinmapStringBuilder()\n        {\n            var sb = new StringBuilder();\n\n            var lineEnding = \"\\r\\n\";\n\n            var digitals =\n                Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Digital))\n                    .ToDictionary(j => j.Key, j => j.Value);\n\n            var analogs = Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Analog))\n                    .ToDictionary(j => j.Key, j => j.Value);\n\n            var serials =\n                Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Serial))\n                    .ToDictionary(j => j.Key, j => j.Value);\n\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, \"Digital join count {digitalCount} Analog join count {analogCount} Serial join count {serialCount}\", null, digitals.Count, analogs.Count, serials.Count);\n\n            // Get the joins of each type and print them\n            sb.Append($\"# {GetType().Name}\\r\\n\");\n            sb.Append(lineEnding);\n            sb.Append($\"## Digitals{lineEnding}\");\n            sb.Append(lineEnding);\n            // Get the joins of each type and print them\n            \n            var digitalSb = AppendJoinList(GetSortedJoins(digitals));\n            digitalSb.Append($\"## Analogs{lineEnding}\");\n            digitalSb.Append(lineEnding);\n            \n            var analogSb = AppendJoinList(GetSortedJoins(analogs));\n            analogSb.Append($\"## Serials{lineEnding}\");\n            analogSb.Append(lineEnding);\n\n            \n            var serialSb = AppendJoinList(GetSortedJoins(serials));\n\n            sb.EnsureCapacity(sb.Length + digitalSb.Length + analogSb.Length + serialSb.Length);\n            sb.Append(digitalSb).Append(analogSb).Append(serialSb);\n            return sb;\n        }\n\n        /// <summary>\n        /// MarkdownJoinMapInfo method\n        /// </summary>\n        public void MarkdownJoinMapInfo(string deviceKey, string bridgeKey)\n        {\n            var pluginType = GetType().Name;\n\n            CrestronConsole.ConsoleCommandResponse(\"{0}:\\n\", pluginType);\n\n\n\n            WriteJoinmapMarkdown(JoinmapStringBuilder(), pluginType, bridgeKey, deviceKey);\n\n        }\n\n        private static void WriteJoinmapMarkdown(StringBuilder stringBuilder, string pluginType, string bridgeKey, string deviceKey)\n        {\n            var fileName = string.Format(\"{0}{1}{2}__{3}__{4}.md\", Global.FilePathPrefix, \"joinMaps/\", pluginType, bridgeKey, deviceKey);\n\n            using (var sw = new StreamWriter(fileName))\n            {\n                sw.WriteLine(stringBuilder.ToString());\n                CrestronConsole.ConsoleCommandResponse(\"Joinmap Readme generated and written to {0}\", fileName);\n            }\n\n        }\n\n        /// <summary>\n        /// Returns  a sorted list by JoinNumber\n        /// </summary>\n        /// <param name=\"joins\"></param>\n        /// <returns></returns>\n        static List<KeyValuePair<string, JoinDataComplete>> GetSortedJoins(Dictionary<string, JoinDataComplete> joins)\n        {\n            var sortedJoins = joins.ToList();\n\n            sortedJoins.Sort((pair1, pair2) => pair1.Value.JoinNumber.CompareTo(pair2.Value.JoinNumber));\n\n            return sortedJoins;\n        }\n\n\n        static StringBuilder AppendJoinList(List<KeyValuePair<string, JoinDataComplete>> joins)\n        {\n            var sb = new StringBuilder();\n            const string stringFormatter = \"| {0} | {1} | {2} | {3} | {4} |\\r\\n\";\n            const int joinNumberLen = 11;\n            const int joinSpanLen = 9;\n            const int typeLen = 19;\n            const int capabilitiesLen = 12;\n            var descriptionLen = (from @join in joins select @join.Value into j select j.Metadata.Description.Length).Concat(new[] {11}).Max();\n\n            //build header\n            sb.Append(string.Format(stringFormatter,\n                string.Format(\"Join Number\").PadRight(joinNumberLen, ' '),\n                string.Format(\"Join Span\").PadRight(joinSpanLen, ' '),\n                string.Format(\"Description\").PadRight(descriptionLen, ' '),\n                string.Format(\"Type\").PadRight(typeLen, ' '),\n                 string.Format(\"Capabilities\").PadRight(capabilitiesLen, ' ')));\n            //build table seperator\n            sb.Append(string.Format(stringFormatter,\n                new string('-', joinNumberLen),\n                new string('-', joinSpanLen),\n                new string('-', descriptionLen),\n                new string('-', typeLen),\n                new string('-', capabilitiesLen)));\n\n            foreach (var join in joins)\n            {\n                sb.Append(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen));\n            }\n            sb.Append(\"\\r\\n\");\n            return sb;\n        }\n\n        /// <summary>\n        /// Attempts to find the matching key for the custom join and if found overwrites the default JoinData with the custom\n        /// </summary>\n        /// <param name=\"joinData\"></param>\n        /// <summary>\n        /// SetCustomJoinData method\n        /// </summary>\n        public void SetCustomJoinData(Dictionary<string, JoinData> joinData)\n        {\n            foreach (var customJoinData in joinData)\n            {\n                JoinDataComplete join;\n\n                if (!Joins.TryGetValue(customJoinData.Key, out join))\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"No matching key found in join map for: '{0}'\", customJoinData.Key);\n                    continue;\n                }\n\n                if (join != null)\n                {\n                    join.SetCustomJoinData(customJoinData.Value);\n                }\n            }\n\n            PrintJoinMapInfo();\n        }\n\n        // /// <summary>\n        // /// Returns the join span for the join with the specified key\n        // /// </summary>\n        // /// <param name=\"key\"></param>\n        // /// <returns></returns>\n        //public uint GetJoinSpanForKey(string key)\n        //{\n        //    return Joins.ContainsKey(key) ? Joins[key].JoinSpan : 0;\n        //}\n    }\n\n    /// <summary>\n    /// Read = Provides feedback to SIMPL\n    /// Write = Responds to sig values from SIMPL\n    /// </summary>\n    [Flags]\n    public enum eJoinCapabilities\n    {\n        /// <summary>\n        /// No capabilities\n        /// </summary>\n        None = 0,\n\n        /// <summary>\n        /// To SIMPL\n        /// </summary>\n        ToSIMPL = 1,\n\n        /// <summary>\n        /// From SIMPL\n        /// </summary>\n        FromSIMPL = 2,\n\n        /// <summary>\n        /// To and From SIMPL\n        /// </summary>\n        ToFromSIMPL = ToSIMPL | FromSIMPL,\n\n        /// <summary>\n        /// To Fusion\n        /// </summary>\n        ToFusion = 4,\n\n        /// <summary>\n        /// From Fusion\n        /// </summary>\n        FromFusion = 8,\n\n        /// <summary>\n        /// To and From Fusion\n        /// </summary>\n        ToFromFusion = ToFusion | FromFusion,\n    }\n\n    /// <summary>\n    /// Enumeration of eJoinType values\n    /// </summary>\n    [Flags]\n    public enum eJoinType\n    {\n        /// <summary>\n        /// No join type\n        /// </summary>\n        None = 0,\n\n        /// <summary>\n        /// Digital join\n        /// </summary>\n        Digital = 1,\n\n        /// <summary>\n        /// Analog join\n        /// </summary>\n        Analog = 2,\n\n        /// <summary>\n        /// Serial join\n        /// </summary>\n        Serial = 4,\n\n        /// <summary>\n        /// Digital and Analog join\n        /// </summary>\n        DigitalAnalog = Digital | Analog,\n\n        /// <summary>\n        /// Digital and Serial join\n        /// </summary>\n        DigitalSerial = Digital | Serial,\n\n        /// <summary>\n        /// Analog and Serial join\n        /// </summary>\n        AnalogSerial = Analog | Serial,\n\n        /// <summary>\n        /// Digital, Analog and Serial join\n        /// </summary>\n        DigitalAnalogSerial = Digital | Analog | Serial,\n    }\n\n    /// <summary>\n    /// Represents a JoinMetadata\n    /// </summary>\n    public class JoinMetadata\n    {\n        private string _description;\n        /// <summary>\n        /// A description for the join to better describe its usage\n        /// </summary>\n        [JsonProperty(\"description\")]\n        public string Description { get { return _description; } set { _description = value; } }\n\n        /// <summary>\n        /// Gets or sets the JoinType\n        /// </summary>\n        [JsonProperty(\"joinType\")]\n        public eJoinType JoinType { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the JoinCapabilities\n        /// </summary>\n        [JsonProperty(\"joinCapabilities\")]\n        public eJoinCapabilities JoinCapabilities { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ValidValues\n        /// </summary>\n        [JsonProperty(\"validValues\")]\n        public string[] ValidValues { get; set; }\n\n    }\n\n    /// <summary>\n    /// Data describing the join.  Can be overridden from configuratino\n    /// </summary>\n    public class JoinData\n    {\n        /// <summary>\n        /// Join number (based on join offset value)\n        /// </summary>\n        [JsonProperty(\"joinNumber\")]\n        public uint JoinNumber { get; set; }\n\n        /// <summary>\n        /// Join range span.  If join indicates the start of a range of joins, this indicated the maximum number of joins in the range\n        /// </summary>\n        [JsonProperty(\"joinSpan\")]\n        public uint JoinSpan { get; set; }\n\n        /// <summary>\n        /// Gets or sets the AttributeName\n        /// </summary>\n        [JsonProperty(\"attributeName\")]\n        public string AttributeName { get; set; }\n    }\n\n    /// <summary>\n    /// A class to aggregate the JoinData and JoinMetadata for a join\n    /// </summary>\n    public class JoinDataComplete\n    {\n        private uint _joinOffset;\n\n        private JoinData _data;\n\n        /// <summary>\n        /// Gets or sets the JoinMetadata\n        /// </summary>\n        public JoinMetadata Metadata { get; set; }\n\n        /// <summary>\n        /// To store some future information as you please\n        /// </summary>\n        public object UserObject { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"data\"></param>\n        /// <param name=\"metadata\"></param>\n        public JoinDataComplete(JoinData data, JoinMetadata metadata)\n        {\n            _data = data;\n            Metadata = metadata;\n        }\n\n        /// <summary>\n        /// GetMarkdownFormattedData method\n        /// </summary>\n        /// <param name=\"stringFormatter\">formatter to use</param>\n        /// <param name=\"descriptionLen\">length of the description</param>\n        /// <returns></returns>\n        public string GetMarkdownFormattedData(string stringFormatter, int descriptionLen)\n        {\n\n            //Fixed Width Headers\n            var joinNumberLen = string.Format(\"Join Number\").Length;\n            var joinSpanLen = string.Format(\"Join Span\").Length;\n            var typeLen = string.Format(\"AnalogDigitalSerial\").Length;\n            var capabilitiesLen = string.Format(\"ToFromFusion\").Length;\n\n            //Track which one failed, if it did\n            const string placeholder = \"unknown\";\n            var dataArray = new Dictionary<string, string>\n            {\n                {\"joinNumber\", placeholder.PadRight(joinNumberLen, ' ')},\n                {\"joinSpan\", placeholder.PadRight(joinSpanLen, ' ')},\n                {\"description\", placeholder.PadRight(descriptionLen, ' ')},\n                {\"joinType\", placeholder.PadRight(typeLen, ' ')},\n                {\"capabilities\", placeholder.PadRight(capabilitiesLen, ' ')}\n            };\n\n\n            try\n            {\n                dataArray[\"joinNumber\"] = string.Format(\"{0}\", JoinNumber.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinNumberLen, ' ');\n                dataArray[\"joinSpan\"] = string.Format(\"{0}\", JoinSpan.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinSpanLen, ' ');\n                dataArray[\"description\"] = string.Format(\"{0}\", Metadata.Description.ReplaceIfNullOrEmpty(placeholder)).PadRight(descriptionLen, ' ');\n                dataArray[\"joinType\"] = string.Format(\"{0}\", Metadata.JoinType.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(typeLen, ' ');\n                dataArray[\"capabilities\"] = string.Format(\"{0}\", Metadata.JoinCapabilities.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(capabilitiesLen, ' ');\n\n                return string.Format(stringFormatter,\n                    dataArray[\"joinNumber\"],\n                    dataArray[\"joinSpan\"],\n                    dataArray[\"description\"],\n                    dataArray[\"joinType\"],\n                    dataArray[\"capabilities\"]);\n\n            }\n            catch (Exception e)\n            {\n                //Don't Throw - we don't want to kill the system if this falls over - it's not mission critical. Print the error, use placeholder data\n                var errorKey = string.Empty;\n                foreach (var item in dataArray)\n                {\n                    if (item.Value.TrimEnd() == placeholder) continue;\n                    errorKey = item.Key;\n                    break;\n                }\n                Debug.LogMessage(LogEventLevel.Information, \"Unable to decode join metadata {1}- {0}\", e.Message, !string.IsNullOrEmpty(errorKey) ? (' ' + errorKey) : string.Empty);\n                return string.Format(stringFormatter,\n                    dataArray[\"joinNumber\"],\n                    dataArray[\"joinSpan\"],\n                    dataArray[\"description\"],\n                    dataArray[\"joinType\"],\n                    dataArray[\"capabilities\"]);\n            }\n        }\n\n\n        /// <summary>\n        /// Sets the join offset value\n        /// </summary>\n        /// <param name=\"joinOffset\"></param>\n        public void SetJoinOffset(uint joinOffset)\n        {\n            _joinOffset = joinOffset;\n        }\n\n        /// <summary>\n        /// The join number (including the offset)\n        /// </summary>\n        public uint JoinNumber\n        {\n            get { return _data.JoinNumber+ _joinOffset; }\n            set { _data.JoinNumber = value; }\n        }\n\n        /// <summary>\n        /// The join span\n        /// </summary>\n        public uint JoinSpan\n        {\n            get { return _data.JoinSpan; }\n        }\n\n        /// <summary>\n        /// The attribute name\n        /// </summary>\n        public string AttributeName\n        {\n            get { return _data.AttributeName; }\n        }\n\n        /// <summary>\n        /// SetCustomJoinData method\n        /// </summary>\n        public void SetCustomJoinData(JoinData customJoinData)\n        {\n            _data = customJoinData;\n        }\n\n        /// <summary>\n        /// GetNameAttribute method\n        /// </summary>\n        public string GetNameAttribute(MemberInfo memberInfo)\n        {\n            var name = string.Empty;\n            var attribute = (JoinNameAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));\n\n            if (attribute == null) return name;\n\n            name = attribute.Name;\n            Debug.LogMessage(LogEventLevel.Verbose, \"JoinName Attribute value: {0}\", name);\n            return name;\n        }\n    }\n\n    \n\n    /// <summary>\n    /// Represents a JoinNameAttribute\n    /// </summary>\n    [AttributeUsage(AttributeTargets.All)]\n    public class JoinNameAttribute : Attribute\n    {\n        private string _Name;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"name\">name of the attribute</param>\n        public JoinNameAttribute(string name)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, \"Setting Attribute Name: {0}\",null, name);\n            _Name = name;\n        }\n\n        /// <summary>\n        /// Gets the Name\n        /// </summary>\n        public string Name\n        {\n            get { return _Name; }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/License/EssentialsLicenseManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronDataStore;\n\nusing PepperDash.Essentials.Core;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.License\n{\n\t/// <summary>\n\t/// Abstract base class for License Managers\n\t/// </summary>\n\tpublic abstract class LicenseManager\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the LicenseIsValid\n\t\t/// </summary>\n\t\tpublic BoolFeedback LicenseIsValid { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the LicenseMessage\n\t\t/// </summary>\n\t\tpublic StringFeedback LicenseMessage { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the LicenseLog\n\t\t/// </summary>\n\t\tpublic StringFeedback LicenseLog { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tprotected LicenseManager()\n\t\t{\n\t\t\tCrestronConsole.AddNewConsoleCommand(\n\t\t\t\ts => CrestronConsole.ConsoleCommandResponse(GetStatusString()), \n\t\t\t\t\"licensestatus\", \"shows license and related data\", \n\t\t\t\tConsoleAccessLevelEnum.AccessOperator);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the status string for console command\n\t\t/// </summary>\n\t\tprotected abstract string GetStatusString();\n\t}\n\n\t/// <summary>\n\t/// Represents a MockEssentialsLicenseManager\n\t/// </summary>\n\tpublic class MockEssentialsLicenseManager : LicenseManager\n\t{\n\t\t/// <summary>\n\t\t/// Returns the singleton mock license manager for this app\n\t\t/// </summary>\n\t\tpublic static MockEssentialsLicenseManager Manager\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\tif (_Manager == null)\n\t\t\t\t\t_Manager = new MockEssentialsLicenseManager();\n\t\t\t\treturn _Manager;\n\t\t\t}\n\t\t}\n\t\tstatic MockEssentialsLicenseManager _Manager;\n\n\t\tbool IsValid;\n\n\t\tMockEssentialsLicenseManager() : base()\n\t\t{\n\t\t\tLicenseIsValid = new BoolFeedback(\"LicenseIsValid\",\n\t\t\t\t() => { return IsValid; });\n\t\t\tCrestronConsole.AddNewConsoleCommand(\n\t\t\t\ts => SetFromConsole(s.Equals(\"true\", StringComparison.OrdinalIgnoreCase)), \n\t\t\t\t\"mocklicense\", \"true or false for testing\", ConsoleAccessLevelEnum.AccessOperator);\n\n\t\t\tbool valid;\n\t\t\tvar err = CrestronDataStoreStatic.GetGlobalBoolValue(\"MockLicense\", out valid);\n\t\t\tif (err == CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n\t\t\t\tSetIsValid(valid);\n\t\t\telse if (err == CrestronDataStore.CDS_ERROR.CDS_RECORD_NOT_FOUND)\n\t\t\t\tCrestronDataStoreStatic.SetGlobalBoolValue(\"MockLicense\", false);\n\t\t\telse\n\t\t\t\tCrestronConsole.PrintLine(\"Error restoring Mock License setting: {0}\", err);\n\t\t}\n\n\t\tvoid SetIsValid(bool isValid)\n\t\t{\n\t\t\tIsValid = isValid;\n\t\t\tCrestronDataStoreStatic.SetGlobalBoolValue(\"MockLicense\", isValid);\n\t\t\tDebug.LogMessage(LogEventLevel.Information, \"Mock License is{0} valid\", IsValid ? \"\" : \" not\");\n\t\t\tLicenseIsValid.FireUpdate();\n\t\t}\n\n\t\tvoid SetFromConsole(bool isValid)\n\t\t{\n\t\t\tSetIsValid(isValid);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the status string for console command\n\t\t/// </summary>\n\t\t/// <returns>license status valid or invalid</returns>\n\t\tprotected override string GetStatusString()\n\t\t{\n\t\t\treturn string.Format(\"License Status: {0}\", IsValid ? \"Valid\" : \"Not Valid\");\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Lighting/Lighting Interfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core.Lighting\n{\n    /// <summary>\n    /// Defines the contract for ILightingScenes\n    /// </summary>\n    public interface ILightingScenes\n    {\n        /// <summary>\n        /// Fires when the lighting scene changes\n        /// </summary>\n        event EventHandler<LightingSceneChangeEventArgs> LightingSceneChange;\n\n        /// <summary>\n        /// Gets the list of LightingScenes\n        /// </summary>\n        List<LightingScene> LightingScenes { get; }\n\n        /// <summary>\n        /// Selects the given LightingScene\n        /// </summary>\n        /// <param name=\"scene\">scene to select</param>\n        void SelectScene(LightingScene scene);\n\n        /// <summary>\n        /// Gets the currently selected LightingScene\n        /// </summary>\n        LightingScene CurrentLightingScene { get; }\n\n    }\n\n    /// <summary>\n    /// Defines the contract for ILightingScenesDynamic\n    /// </summary>\n    public interface ILightingScenesDynamic : ILightingScenes\n    {\n        /// <summary>\n        /// Fires when the lighting scenes are updated\n        /// </summary>\n        event EventHandler LightingScenesUpdated;\n    }\n\n    /// <summary>\n    /// Defines the contract for ILightingMasterRaiseLower\n    /// </summary>\n    public interface ILightingMasterRaiseLower\n    {\n        /// <summary>\n        /// Raises the master level\n        /// </summary>\n        void MasterRaise();\n\n        /// <summary>\n        /// Lowers the master level\n        /// </summary>\n        void MasterLower();\n\n        /// <summary>\n        /// Stops raising or lowering the master level\n        /// </summary>\n        void MasterRaiseLowerStop();\n    }\n\n    /// <summary>\n    /// Defines the contract for ILightingLoad\n    /// </summary>\n    public interface ILightingLoad\n    {\n        /// <summary>\n        /// Sets the load level\n        /// </summary>\n        /// <param name=\"level\">level to set</param>\n        void SetLoadLevel(int level);\n\n        /// <summary>\n        /// Raises the load level\n        /// </summary>\n        void Raise();\n\n        /// <summary>\n        /// Lowers the load level\n        /// </summary>\n        void Lower();\n\n        /// <summary>\n        /// feedback of the current load level\n        /// </summary>\n        IntFeedback LoadLevelFeedback { get; }\n\n        /// <summary>\n        /// feedback of whether the load is on\n        /// </summary>\n        BoolFeedback LoadIsOnFeedback { get; }\n    }\n\n    /// <summary>\n    /// Represents a LightingSceneChangeEventArgs\n    /// </summary>\n    public class LightingSceneChangeEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the CurrentLightingScene\n        /// </summary>\n        public LightingScene CurrentLightingScene { get; private set; }\n\n        /// <summary>\n        /// Constructor for LightingSceneChangeEventArgs\n        /// </summary>\n        /// <param name=\"scene\">The lighting scene that changed</param>\n        public LightingSceneChangeEventArgs(LightingScene scene)\n        {\n            CurrentLightingScene = scene;\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Lighting/LightingScene.cs",
    "content": "﻿\n\nusing System;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core.Lighting\n{\n    /// <summary>\n    /// Represents a LightingScene\n    /// </summary>\n    public class LightingScene\n    {\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        [JsonProperty(\"name\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ID\n        /// </summary>\n        [JsonProperty(\"id\", NullValueHandling = NullValueHandling.Ignore)]\n        public string ID { get; set; }\n        \n        bool _IsActive;\n\n        /// <summary>\n        /// Gets or sets whether the scene is active\n        /// </summary>\n        [JsonProperty(\"isActive\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool IsActive \n        {\n            get\n            {\n                return _IsActive;\n            }\n            set\n            {\n                _IsActive = value;\n                IsActiveFeedback.FireUpdate();\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the SortOrder\n        /// </summary>\n        [JsonProperty(\"sortOrder\", NullValueHandling = NullValueHandling.Ignore)]\n        public int SortOrder { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the IsActiveFeedback\n        /// </summary>\n        [JsonIgnore]\n        public BoolFeedback IsActiveFeedback { get; set; }\n\n        /// <summary>\n        /// Constructor for LightingScene\n        /// </summary>\n        public LightingScene()\n        {\n            IsActiveFeedback = new BoolFeedback(new Func<bool>(() => IsActive));\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Microphone Privacy/MicrophonePrivacyController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.CrestronIO;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core.Privacy\n{\n    /// <summary>\n    /// Used for applications where one or more microphones with momentary contact closure outputs are used to\n    /// toggle the privacy state of the room.  Privacy state feedback is represented \n    /// </summary>\n    public class MicrophonePrivacyController : EssentialsDevice\n    {\n        MicrophonePrivacyControllerConfig Config;\n\n        bool initialized;\n\n        /// <summary>\n        /// Gets or sets whether LED control is enabled\n        /// </summary>\n        public bool EnableLeds\n        {\n            get\n            {\n                return _enableLeds;\n            }\n            set\n            {\n                _enableLeds = value;\n\n                if (initialized)\n                {\n                    if (value)\n                    {\n                        CheckPrivacyMode();\n                        SetLedStates();\n                    }\n                    else\n                        TurnOffAllLeds();\n                }\n            }\n        }\n        bool _enableLeds;\n\n        /// <summary>\n        /// Gets or sets the Inputs\n        /// </summary>\n        public List<IDigitalInput> Inputs { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the RedLedRelay\n        /// </summary>\n        public GenericRelayDevice RedLedRelay { get; private set; }\n        bool _redLedRelayState;\n\n        /// <summary>\n        /// Gets or sets the GreenLedRelay\n        /// </summary>\n        public GenericRelayDevice GreenLedRelay { get; private set; }\n        bool _greenLedRelayState;\n\n        /// <summary>\n        /// Gets or sets the PrivacyDevice\n        /// </summary>\n        public IPrivacy PrivacyDevice { get; private set; }\n\n        /// <summary>\n        /// Constructor for MicrophonePrivacyController\n        /// </summary>\n        /// <param name=\"key\">key of the controller device</param>\n        /// <param name=\"config\">configuration for the controller device</param>\n        public MicrophonePrivacyController(string key, MicrophonePrivacyControllerConfig config) :\n            base(key)\n        {\n            Config = config;\n\n            Inputs = new List<IDigitalInput>();\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            foreach (var i in Config.Inputs)\n            {\n                var input = DeviceManager.GetDeviceForKey(i.DeviceKey) as IDigitalInput;\n\n                if(input != null)\n                    AddInput(input);\n            }\n\n            var greenLed = DeviceManager.GetDeviceForKey(Config.GreenLedRelay.DeviceKey) as GenericRelayDevice;\n\n            if (greenLed != null)\n                GreenLedRelay = greenLed;\n            else\n                Debug.LogMessage(LogEventLevel.Information, this, \"Unable to add Green LED device\");\n\n            var redLed = DeviceManager.GetDeviceForKey(Config.RedLedRelay.DeviceKey) as GenericRelayDevice;\n\n            if (redLed != null)\n                RedLedRelay = redLed;\n            else\n                Debug.LogMessage(LogEventLevel.Information, this, \"Unable to add Red LED device\");\n\n            AddPostActivationAction(() => {\n                PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange -= PrivacyModeIsOnFeedback_OutputChange;\n                PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange;\n            });\n\n            initialized = true;\n\n            return base.CustomActivate();\n        }\n\n        #region Overrides of Device\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        /// <inheritdoc />\n        public override void Initialize()\n        {\n            CheckPrivacyMode();\n        }\n\n        #endregion\n\n        /// <summary>\n        /// SetPrivacyDevice method\n        /// </summary>\n        public void SetPrivacyDevice(IPrivacy privacyDevice)\n        {\n            PrivacyDevice = privacyDevice;\n        }\n\n        void PrivacyModeIsOnFeedback_OutputChange(object sender, EventArgs e)\n        {\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Privacy mode change: {0}\", sender as BoolFeedback);\n            CheckPrivacyMode();\n        }\n\n        void CheckPrivacyMode()\n        {\n            if (PrivacyDevice != null)\n            {\n                var privacyState = PrivacyDevice.PrivacyModeIsOnFeedback.BoolValue;\n\n                if (privacyState)\n                    TurnOnRedLeds();\n                else\n                    TurnOnGreenLeds();\n            }\n        }\n\n        void AddInput(IDigitalInput input)\n        {\n            Inputs.Add(input);\n\n            input.InputStateFeedback.OutputChange += InputStateFeedback_OutputChange;\n        }\n\n        void RemoveInput(IDigitalInput input)\n        {\n            var tempInput = Inputs.FirstOrDefault(i => i.Equals(input));\n\n            if (tempInput != null)\n                tempInput.InputStateFeedback.OutputChange -= InputStateFeedback_OutputChange;\n\n            Inputs.Remove(input);\n        }\n\n        void SetRedLedRelay(GenericRelayDevice relay)\n        {\n            RedLedRelay = relay;\n        }\n\n        void SetGreenLedRelay(GenericRelayDevice relay)\n        {\n            GreenLedRelay = relay;\n        }\n\n        /// <summary>\n        /// Check the state of the input change and handle accordingly\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        void InputStateFeedback_OutputChange(object sender, EventArgs e)\n        {\n            if ((sender as BoolFeedback).BoolValue == true)\n                TogglePrivacyMute();\n        }\n\n        /// <summary>\n        /// Toggles the state of the privacy mute\n        /// </summary>\n        public void TogglePrivacyMute()\n        {\n            PrivacyDevice.PrivacyModeToggle();\n        }\n\n        void TurnOnRedLeds()\n        {\n            _greenLedRelayState = false;\n            _redLedRelayState = true;\n            SetLedStates();\n        }\n\n        void TurnOnGreenLeds()\n        {\n            _redLedRelayState = false;\n            _greenLedRelayState = true;\n            SetLedStates();\n        }\n\n        /// <summary>\n        /// If enabled, sets the actual state of the relays\n        /// </summary>\n        void SetLedStates()\n        {\n            if (_enableLeds)\n            {\n                SetRelayStates();\n            }\n            else\n                TurnOffAllLeds();\n        }\n\n        /// <summary>\n        /// Turns off all LEDs\n        /// </summary>\n        void TurnOffAllLeds()\n        {\n            _redLedRelayState = false;\n            _greenLedRelayState = false;\n\n            SetRelayStates();\n        }\n\n        void SetRelayStates()\n        {\n            if (RedLedRelay != null)\n            {\n                if (_redLedRelayState)\n                    RedLedRelay.CloseRelay();\n                else\n                    RedLedRelay.OpenRelay();\n            }\n\n            if(GreenLedRelay != null)\n            {\n                if (_greenLedRelayState)\n                    GreenLedRelay.CloseRelay();\n                else\n                    GreenLedRelay.OpenRelay();\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a MicrophonePrivacyControllerFactory\n    /// </summary>\n    public class MicrophonePrivacyControllerFactory : EssentialsDeviceFactory<MicrophonePrivacyController>\n    {\n        /// <summary>\n        /// Constructor for MicrophonePrivacyControllerFactory\n        /// </summary>\n        public MicrophonePrivacyControllerFactory()\n        {\n            TypeNames = new List<string>() { \"microphoneprivacycontroller\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new MIcrophonePrivacyController Device\");\n            var props = Newtonsoft.Json.JsonConvert.DeserializeObject<Core.Privacy.MicrophonePrivacyControllerConfig>(dc.Properties.ToString());\n\n            return new Core.Privacy.MicrophonePrivacyController(dc.Key, props);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Microphone Privacy/MicrophonePrivacyControllerConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Essentials.Core.CrestronIO;\n\nnamespace PepperDash.Essentials.Core.Privacy\n{\n    /// <summary>\n    /// Represents a MicrophonePrivacyControllerConfig\n    /// </summary>\n    public class MicrophonePrivacyControllerConfig\n    {\n        /// <summary>\n        /// Gets or sets the Inputs\n        /// </summary>\n        public List<KeyedDevice> Inputs { get; set; }\n        /// <summary>\n        /// Gets or sets the GreenLedRelay\n        /// </summary>\n        public KeyedDevice GreenLedRelay { get; set; }\n        /// <summary>\n        /// Gets or sets the RedLedRelay\n        /// </summary>\n        public KeyedDevice RedLedRelay { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a KeyedDevice\n    /// </summary>\n    public class KeyedDevice\n    {\n        /// <summary>\n        /// Gets or sets the DeviceKey\n        /// </summary>\n        public string DeviceKey { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Monitoring/CrestronGenericBaseCommunicationMonitor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing System.ComponentModel;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic class CrestronGenericBaseCommunicationMonitor : StatusMonitorBase\n\t{\n\t\tGenericBase Device;\n\n\t\t/// <summary>\n\t\t/// Constructor for CrestronGenericBaseCommunicationMonitor\n\t\t/// </summary>\n\t\t/// <param name=\"parent\">parent device</param>\n\t\t/// <param name=\"device\">device to monitor</param>\n\t\t/// <param name=\"warningTime\">time before warning status</param>\n\t\t/// <param name=\"errorTime\">time before error status</param>\n\t\tpublic CrestronGenericBaseCommunicationMonitor(IKeyed parent, GenericBase device, long warningTime, long errorTime)\n\t\t\t: base(parent, warningTime, errorTime)\n\t\t{\n\t\t\tDevice = device;\n\t\t}\n\n  /// <summary>\n  /// Start method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic override void Start()\n\t\t{\n\t\t\tDevice.OnlineStatusChange -= Device_OnlineStatusChange;\n\t\t\tDevice.OnlineStatusChange += Device_OnlineStatusChange;\n\t\t\tGetStatus();\n\t\t}\n\n  /// <summary>\n  /// Stop method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic override void Stop()\n\t\t{\n\t\t\tDevice.OnlineStatusChange -= Device_OnlineStatusChange;\n\t\t}\t\n\t\t\n\t\tvoid Device_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)\n\t\t{\n\t\t\tGetStatus();\n\t\t}\n\n\t\tvoid GetStatus()\n\t\t{\n\t\t\tif (Device.IsOnline)\n\t\t\t{\n\t\t\t\tStatus = MonitorStatus.IsOk;\n\t\t\t\tStopErrorTimers();\n\t\t\t}\n\t\t\telse\n\t\t\t\tStartErrorTimers();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Monitoring/GenericCommunicationMonitor.cs",
    "content": "﻿using System;\nusing PepperDash.Core;\nusing System.Threading;\nusing PepperDash.Core.Logging;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Used for monitoring comms that are IBasicCommunication. Will send a poll string and provide an event when\n\t/// statuses change.\n    /// Default monitoring uses TextReceived event on Client.\n\t/// </summary>\n\tpublic class GenericCommunicationMonitor : StatusMonitorBase\n\t{\n        /// <summary>\n        /// Gets the Client being monitored\n        /// </summary>\n\t\tpublic IBasicCommunication Client { get; private set; }\n\n        /// <summary>\n        /// Will monitor Client.BytesReceived if set to true.  Otherwise the default is to monitor Client.TextReceived\n        /// </summary>\n        public bool MonitorBytesReceived { get; private set; }\n\n        /// <summary>\n        /// Return true if the Client is ISocketStatus\n        /// </summary>\n        public bool IsSocket => Client is ISocketStatus;\n\n        private readonly string PollString;\n        private readonly Action PollAction;\n        private readonly long PollTime;\n\n\t\tprivate Timer PollTimer;\n\n        private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);\n\n        /// <summary>\n        /// GenericCommunicationMonitor constructor\n        /// \n        /// Note: If the client is a socket, the connection status will be monitored and the PollTimer will be started automatically when the client is connected\n        /// </summary>\n        /// <param name=\"parent\">Parent device</param>\n        /// <param name=\"client\">Communications Client</param>\n        /// <param name=\"pollTime\">Time in MS for polling</param>\n        /// <param name=\"warningTime\">Warning time in MS. If a message is not received before this elapsed time the status will be Warning</param>\n        /// <param name=\"errorTime\">Error time in MS. If a message is not received before this elapsed time the status will be Error</param>\n        /// <param name=\"pollString\">string to send for polling</param>\n        /// <exception cref=\"ArgumentException\">Poll time must be less than warning and error time</exception>\n        public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, \n\t\t\tlong warningTime, long errorTime, string pollString) :\n\t\t\tbase(parent, warningTime, errorTime)\n\t\t{\n\t\t\tif (pollTime > warningTime || pollTime > errorTime)\n\t\t\t\tthrow new ArgumentException(\"pollTime must be less than warning or errorTime\");\n\n\t\t\tClient = client;\n\t\t\tPollTime = pollTime;\n\t\t\tPollString = pollString;\n\n            if (IsSocket)\n            {\n                (Client as ISocketStatus).ConnectionChange += Socket_ConnectionChange;\n            }\n\t\t}\n\n        /// <summary>\n        /// GenericCommunicationMonitor constructor with a bool to specify whether to monitor BytesReceived\n        /// \n        /// Note: If the client is a socket, the connection status will be monitored and the PollTimer will be started automatically when the client is connected\n        /// </summary>\n        /// <param name=\"parent\">Parent device</param>\n        /// <param name=\"client\">Communications Client</param>\n        /// <param name=\"pollTime\">Time in MS for polling</param>\n        /// <param name=\"warningTime\">Warning time in MS. If a message is not received before this elapsed time the status will be Warning</param>\n        /// <param name=\"errorTime\">Error time in MS. If a message is not received before this elapsed time the status will be Error</param>\n        /// <param name=\"pollString\">string to send for polling</param>\n        /// <param name=\"monitorBytesReceived\">Use bytesReceived event instead of textReceived when true</param>\n        public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime,\n            long warningTime, long errorTime, string pollString, bool monitorBytesReceived) :\n            this(parent, client, pollTime, warningTime, errorTime, pollString)\n        {\n            MonitorBytesReceived = monitorBytesReceived;\n        }\n\n        /// <summary>\n        /// GenericCommunicationMonitor constructor with a poll action instead of a poll string\n        /// \n        /// Note: If the client is a socket, the connection status will be monitored and the PollTimer will be started automatically when the client is connected\n        /// </summary>\n        /// <param name=\"parent\">Parent device</param>\n        /// <param name=\"client\">Communications Client</param>\n        /// <param name=\"pollTime\">Time in MS for polling</param>\n        /// <param name=\"warningTime\">Warning time in MS. If a message is not received before this elapsed time the status will be Warning</param>\n        /// <param name=\"errorTime\">Error time in MS. If a message is not received before this elapsed time the status will be Error</param>\n        /// <param name=\"pollAction\">Action to execute for polling</param>\n        /// <exception cref=\"ArgumentException\">Poll time must be less than warning and error time</exception>\n        public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime,\n            long warningTime, long errorTime, Action pollAction) :\n            base(parent, warningTime, errorTime)\n        {\n            if (pollTime > warningTime || pollTime > errorTime)\n                throw new ArgumentException(\"pollTime must be less than warning or errorTime\");\n            //if (pollTime < 5000)\n            //    throw new ArgumentException(\"pollTime cannot be less than 5000 ms\");\n\n            Client = client;\n            PollTime = pollTime;\n            PollAction = pollAction;\n\n            if (IsSocket)\n            {\n                (Client as ISocketStatus).ConnectionChange += Socket_ConnectionChange;\n            }\n\n        }\n\n        /// <summary>\n        /// GenericCommunicationMonitor constructor with a poll action instead of a poll string and a bool to specify whether to monitor BytesReceived\n        /// \n        /// Note: If the client is a socket, the connection status will be monitored and the PollTimer will be started automatically when the client is connected\n        /// </summary>\n        /// <param name=\"parent\">Parent device</param>\n        /// <param name=\"client\">Communications Client</param>\n        /// <param name=\"pollTime\">Time in MS for polling</param>\n        /// <param name=\"warningTime\">Warning time in MS. If a message is not received before this elapsed time the status will be Warning</param>\n        /// <param name=\"errorTime\">Error time in MS. If a message is not received before this elapsed time the status will be Error</param>\n        /// <param name=\"pollAction\">Action to execute for polling</param>\n        /// <param name=\"monitorBytesReceived\">Use bytesReceived event instead of textReceived when true</param>\n        public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime,\n            long warningTime, long errorTime, Action pollAction, bool monitorBytesReceived) :\n            this(parent, client, pollTime, warningTime, errorTime, pollAction)\n        {\n            MonitorBytesReceived = monitorBytesReceived;\n        }\n\n\n        /// <summary>\n        /// GenericCommunicationMonitor constructor with a config object\n        /// \n        /// Note: If the client is a socket, the connection status will be monitored and the PollTimer will be started automatically when the client is connected\n        /// </summary>\n        /// <param name=\"parent\">Parent Device</param>\n        /// <param name=\"client\">Communications Client</param>\n        /// <param name=\"props\"><see cref=\"CommunicationMonitorConfig\">Communication Monitor Config</see> object</param>\n        public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, \n\t\t\tCommunicationMonitorConfig props) :\n\t\t\tthis(parent, client, props.PollInterval, props.TimeToWarning, props.TimeToError, props.PollString)\n\t\t{\n            if (IsSocket)\n            {\n                (Client as ISocketStatus).ConnectionChange += Socket_ConnectionChange;\n            }\n\t\t}\n\n        /// <summary>\n        /// GenericCommunicationMonitor constructor with a config object and a bool to specify whether to monitor BytesReceived\n        /// \n        /// Note: If the client is a socket, the connection status will be monitored and the PollTimer will be started automatically when the client is connected\n        /// </summary>\n        /// <param name=\"parent\">Parent Device</param>\n        /// <param name=\"client\">Communications Client</param>\n        /// <param name=\"props\"><see cref=\"CommunicationMonitorConfig\">Communication Monitor Config</see> object</param>\n        /// <param name=\"monitorBytesReceived\">Use bytesReceived event instead of textReceived when true</param>\n        public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, CommunicationMonitorConfig props, bool monitorBytesReceived) :\n            this(parent, client, props.PollInterval, props.TimeToWarning, props.TimeToError, props.PollString)\n        {\n            MonitorBytesReceived = monitorBytesReceived;\n        }\n\n        /// <summary>\n        /// Start the poll cycle\n        /// </summary>\n\t\tpublic override void Start()\n\t\t{\n            if (MonitorBytesReceived) \n            {\n\t\t\t    Client.BytesReceived -= Client_BytesReceived;\n                Client.BytesReceived += Client_BytesReceived;\n            }\n            else\n            {\n                Client.TextReceived -= Client_TextReceived;\n                Client.TextReceived += Client_TextReceived;\n            }\n\n            BeginPolling();\n\t\t}\n\n        private void Socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)\n        {\n            if (!e.Client.IsConnected)\n            {\n                // Immediately stop polling and notify that device is offline\n                Stop();\n                Status = MonitorStatus.InError;\n                ResetErrorTimers();\n            }\n            else\n            {\n                // Start polling and set status to unknow and let poll result update the status to IsOk when a response is received\n                Status = MonitorStatus.StatusUnknown;\n                Start();                \n            }\n        }\n\n        private void BeginPolling()\n        {\n            try\n            {\n                semaphore.Wait();\n                {\n                    if (PollTimer != null)\n                    {\n                        return;\n                    }\n\n                    PollTimer = new Timer(o => Poll(), null, 0, PollTime);\n                }\n            }\n            finally\n            {\n                semaphore.Release();\n            }\n        }\n\n  /// <summary>\n  /// Stop method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic override void Stop()\n\t\t{\n            if(MonitorBytesReceived)\n            {\n\t\t\t    Client.BytesReceived -= Client_BytesReceived;\n            }\n            else\n            {\n                Client.TextReceived -= Client_TextReceived;\n            }\n\n            StopErrorTimers();\n\n            if (PollTimer == null)\n            {\n                return;\n            }\n            \n            PollTimer.Dispose();\n            PollTimer = null;\n\t\t}\n\n        private void Client_TextReceived(object sender, GenericCommMethodReceiveTextArgs e)\n        {\n            DataReceived();\n        }\n\n\t\tprivate void Client_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e)\n\t\t{\n            DataReceived();\n        }\n\n        private void DataReceived()\n        {\n            Status = MonitorStatus.IsOk;\n            ResetErrorTimers();\n        }\n\n\t\tprivate void Poll()\n\t\t{\n\t\t\tStartErrorTimers();\n\t\t\tif (Client.IsConnected)\n\t\t\t{\n\t\t\t\t//Debug.LogMessage(LogEventLevel.Verbose, this, \"Polling\");\n                if(PollAction != null)\n                    PollAction.Invoke();\n                else\n                    Client.SendText(PollString);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tthis.LogVerbose(\"Comm not connected\");\n\t\t\t}\n\t\t}\n\t}\n\n    /// <summary>\n    /// Represents a CommunicationMonitorConfig\n    /// </summary>\n\tpublic class CommunicationMonitorConfig\n\t{\n        /// <summary>\n        /// Gets or sets the PollInterval\n        /// </summary>\n\t\tpublic int PollInterval { get; set; }\n\n        /// <summary>\n        /// Gets or sets the TimeToWarning\n        /// </summary>\n\t\tpublic int TimeToWarning { get; set; }\n\n        /// <summary>\n        /// Gets or sets the TimeToError\n        /// </summary>\n\t\tpublic int TimeToError { get; set; }\n\n        /// <summary>\n        /// Gets or sets the PollString\n        /// </summary>\n\t\tpublic string PollString { get; set; }\n\n        /// <summary>\n        /// Default constructor. Sets pollInterval to 30s, TimeToWarning to 120s, and TimeToError to 300s\n        /// </summary>\n\t\tpublic CommunicationMonitorConfig()\n\t\t{\n\t\t\tPollInterval = 30000;\n\t\t\tTimeToWarning = 120000;\n\t\t\tTimeToError = 300000;\n\t\t\tPollString = \"\";\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Monitoring/Interfaces.cs",
    "content": "﻿\nusing System;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Defines the contract for IStatusMonitor\n /// </summary>\n\tpublic interface IStatusMonitor\n\t{\n\t\t/// <summary>\n\t\t/// Gets the Parent\n\t\t/// </summary>\n\t\tIKeyed Parent { get; }\n\n\t\t/// <summary>\n\t\t/// Fires when the status changes\n\t\t/// </summary>\n\t\tevent EventHandler<MonitorStatusChangeEventArgs> StatusChange;\n\n\t\t/// <summary>\n\t\t/// Gets the Status\n\t\t/// </summary>\n\t\tMonitorStatus Status { get; }\n\n\t\t/// <summary>\n\t\t/// Gets the Message\n\t\t/// </summary>\n\t\tstring Message { get; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IsOnlineFeedback\n\t\t/// </summary>\n\t\tBoolFeedback IsOnlineFeedback { get; set; }\n\n\t\t/// <summary>\n\t\t/// Start method\n\t\t/// </summary>\n\t\tvoid Start();\n\n\t\t/// <summary>\n\t\t/// Stop method\n\t\t/// </summary>\n\t\tvoid Stop();\n\t}\n\n\n /// <summary>\n /// Defines the contract for ICommunicationMonitor\n /// </summary>\n\tpublic interface ICommunicationMonitor\n\t{\n\t\t/// <summary>\n\t\t/// Gets the CommunicationMonitor\n\t\t/// </summary>\n\t\tStatusMonitorBase CommunicationMonitor { get; }\n\t}\n\n\t/// <summary>\n\t/// StatusUnknown = 0, IsOk = 1, InWarning = 2, InError = 3\n\t/// </summary>\n\tpublic enum MonitorStatus\n\t{\n\t\t/// <summary>\n\t\t/// Status Unknown\n\t\t/// </summary>\n        StatusUnknown = 0,\n\n\t\t/// <summary>\n\t\t/// Is Ok\n\t\t/// </summary>\n\t\tIsOk = 1,\n\n\t\t/// <summary>\n\t\t/// In Warning\n\t\t/// </summary>\n        InWarning = 2,\n\n\t\t/// <summary>\n\t\t/// In Error\n\t\t/// </summary>\n        InError = 3\n\t}\n\n\t/// <summary>\n\t/// Represents a MonitorStatusChangeEventArgs\n\t/// </summary>\n\tpublic class MonitorStatusChangeEventArgs : EventArgs\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the Status\n\t\t/// </summary>\n\t\tpublic MonitorStatus Status { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Message\n\t\t/// </summary>\n\t\tpublic string Message { get; private set; }\n\t\n\t\t/// <summary>\n\t\t/// Constructor for MonitorStatusChangeEventArgs\n\t\t/// </summary>\n\t\t/// <param name=\"status\">monitor status</param>\n\t\tpublic MonitorStatusChangeEventArgs(MonitorStatus status)\n\t\t{\n\t\t\tStatus = status;\n\t\t\tMessage = status == MonitorStatus.IsOk ? \"\" : status.ToString();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor for MonitorStatusChangeEventArgs\n\t\t/// </summary>\n\t\t/// <param name=\"status\">monitor status</param>\n\t\t/// <param name=\"message\">status message</param>\n\t\tpublic MonitorStatusChangeEventArgs(MonitorStatus status, string message)\n\t\t{\n\t\t\tStatus = status;\n\t\t\tMessage = message;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Monitoring/StatusMonitorBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing System.ComponentModel;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Base class for status monitors\n\t/// </summary>\n\tpublic abstract class StatusMonitorBase : IStatusMonitor, IKeyName\n\t{\n\t\t/// <summary>\n\t\t/// Event fired when status changes\n\t\t/// </summary>\n\t\tpublic event EventHandler<MonitorStatusChangeEventArgs> StatusChange;\n\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get { return Parent.Key + \"-comMonitor\"; } }\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get { return \"Comm. monitor\"; } }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Parent\n\t\t/// </summary>\n\t\tpublic IKeyed Parent { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Bool feedback for online status\n\t\t/// </summary>\n\t\tpublic BoolFeedback IsOnlineFeedback { get; set; }\n\n\t\t/// <summary>\n\t\t/// Indicates whether the monitored device is online\n\t\t/// </summary>\n\t\tpublic bool IsOnline;\n\n\t\t/// <summary>\n\t\t/// Current monitor status\n\t\t/// </summary>\n\t\tpublic MonitorStatus Status\n\t\t{\n\t\t\tget { return _Status; }\n\t\t\tprotected set\n\t\t\t{\n\t\t\t\tif (value != _Status)\n\t\t\t\t{\n\t\t\t\t\t_Status = value;\n\t\t\t\t\t\n\t\t\t\t\tOnStatusChange(value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tMonitorStatus _Status;\n\n\t\t/// <summary>\n\t\t/// Current status message\n\t\t/// </summary>\n\t\tpublic string Message\n\t\t{\n\t\t\tget { return _Message; }\n\t\t\tset \n\t\t\t{\n\t\t\t\tif (value == _Message) return;\n\t\t\t\t_Message = value;\n\t\t\t\tOnStatusChange(Status, value);\n\t\t\t\t\t\n\t\t\t}\n\t\t}\n\t\tstring _Message;\n\n\t\tlong WarningTime;\n\t\tlong ErrorTime;\n\t\tCTimer WarningTimer;\n\t\tCTimer ErrorTimer;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"parent\">parent device</param>\n\t\t/// <param name=\"warningTime\">time in milliseconds before warning status</param>\n\t\t/// <param name=\"errorTime\">time in milliseconds before error status</param>\n\t\tpublic StatusMonitorBase(IKeyed parent, long warningTime, long errorTime)\n\t\t{\n\t\t\tParent = parent;\n\t\t\tif (warningTime > errorTime)\n\t\t\t\tthrow new ArgumentException(\"warningTime must be less than errorTime\");\n\t\t\tif (warningTime < 5000 || errorTime < 5000)\n\t\t\t\tthrow new ArgumentException(\"time values cannot be less that 5000 ms\");\n\n\t\t\tIsOnlineFeedback = new BoolFeedback(() => { return IsOnline; });\n\t\t\tStatus = MonitorStatus.StatusUnknown;\n\t\t\tWarningTime = warningTime;\n\t\t\tErrorTime = errorTime;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Starts the monitor\n\t\t/// </summary>\n\t\tpublic abstract void Start();\n\n\t\t/// <summary>\n\t\t/// Stops the monitor\n\t\t/// </summary>\n\t\tpublic abstract void Stop();\n\n\t\t/// <summary>\n\t\t/// Fires the StatusChange event\n\t\t/// </summary>\n\t\t/// <param name=\"status\">monitor status</param>\n\t\tprotected void OnStatusChange(MonitorStatus status)\n\t\t{\n\t\t\tif (_Status == MonitorStatus.IsOk)\n\t\t\t\tIsOnline = true;\n\t\t\telse\n\t\t\t\tIsOnline = false;\n\t\t\tIsOnlineFeedback.FireUpdate();\n\t\t\tvar handler = StatusChange;\n\t\t\tif (handler != null)\n\t\t\t\thandler(this, new MonitorStatusChangeEventArgs(status));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Fires the StatusChange event with message\n\t\t/// </summary>\n\t\t/// <param name=\"status\">monitor status</param>\n\t\t/// <param name=\"message\">status message</param>\n\t\tprotected void OnStatusChange(MonitorStatus status, string message)\n\t\t{\n\t\t\tif (_Status == MonitorStatus.IsOk)\n\t\t\t\tIsOnline = true;\n\t\t\telse\n\t\t\t\tIsOnline = false;\n\t\t\tIsOnlineFeedback.FireUpdate();\n\t\t\tvar handler = StatusChange;\n\t\t\tif (handler != null)\n\t\t\t\thandler(this, new MonitorStatusChangeEventArgs(status, message));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Starts the error timers\n\t\t/// </summary>\n\t\tprotected void StartErrorTimers()\n\t\t{\n\t\t\tif (WarningTimer == null) WarningTimer = new CTimer(o => { Status = MonitorStatus.InWarning; }, WarningTime);\n\t\t\tif (ErrorTimer == null) ErrorTimer = new CTimer(o => { Status = MonitorStatus.InError; }, ErrorTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Stops the error timers\n\t\t/// </summary>\n\t\tprotected void StopErrorTimers()\n\t\t{\n\t\t\tif (WarningTimer != null) WarningTimer.Stop();\n\t\t\tif (ErrorTimer != null) ErrorTimer.Stop();\n\t\t\tWarningTimer = null;\n\t\t\tErrorTimer = null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Resets the error timers\n\t\t/// </summary>\n        protected void ResetErrorTimers()\n        {\n            if(WarningTimer != null)\n                WarningTimer.Reset(WarningTime, WarningTime);\n            if(ErrorTimer != null)\n                ErrorTimer.Reset(ErrorTime, ErrorTime);\n\n        }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Monitoring/StatusMonitorCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing System.ComponentModel;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Collection class for multiple status monitors\n\t/// </summary>\n\tpublic class StatusMonitorCollection : IStatusMonitor\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the Parent\n\t\t/// </summary>\n\t\tpublic IKeyed Parent { get; private set; }\n\n\t\tList<IStatusMonitor> Monitors = new List<IStatusMonitor>();\n\n\t\t#region IStatusMonitor Members\n\n\t\t/// <summary>\n\t\t/// Event fired when status changes\n\t\t/// </summary>\n\t\tpublic event EventHandler<MonitorStatusChangeEventArgs> StatusChange;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Status\n\t\t/// </summary>\n\t\tpublic MonitorStatus Status { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Message\n\t\t/// </summary>\n\t\tpublic string Message { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IsOnlineFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback IsOnlineFeedback { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"parent\">parent device</param>\n\t\tpublic StatusMonitorCollection(IKeyed parent)\n\t\t{\n\t\t\tParent = parent;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Start method\n\t\t/// </summary>\n\t\tpublic void Start()\n\t\t{\n\t\t\tforeach (var mon in Monitors)\n\t\t\t\tmon.StatusChange += mon_StatusChange;\n\t\t\tProcessStatuses();\n\t\t}\n\n\n\t\tvoid ProcessStatuses()\n\t\t{\n\t\t\tvar InError = Monitors.Where(m => m.Status == MonitorStatus.InError).ToList();\n\t\t\tvar InWarning = Monitors.Where(m => m.Status == MonitorStatus.InWarning).ToList();\n\t\t\tvar IsOk = Monitors.Where(m => m.Status == MonitorStatus.IsOk).ToList();\n\n\n\t\t\tMonitorStatus initialStatus;\n\t\t\tstring prefix = \"0:\";\n\t\t\tif (InError.Any())\n\t\t\t{\n\t\t\t\tinitialStatus = MonitorStatus.InError;\n\t\t\t\tprefix = \"3:\";\n\t\t\t}\n\t\t\telse if (InWarning.Any())\n\t\t\t{\n\t\t\t\tinitialStatus = MonitorStatus.InWarning;\n\t\t\t\tprefix = \"2:\";\n\t\t\t}\n\t\t\telse if (IsOk.Any())\n\t\t\t\tinitialStatus = MonitorStatus.IsOk;\n\t\t\telse\n\t\t\t\tinitialStatus = MonitorStatus.StatusUnknown;\n\n\t\t\t// Build the error message string\n\t\t\tif (InError.Any() || InWarning.Any())\n\t\t\t{\n\t\t\t\tvar errorNames = InError\n\t\t\t\t\t.Select(mon => mon.Parent is IKeyName keyName ? keyName.Name : mon.Parent.Key)\n\t\t\t\t\t.ToList();\n\t\t\t\tvar warningNames = InWarning\n\t\t\t\t\t.Select(mon => mon.Parent is IKeyName keyName ? keyName.Name : mon.Parent.Key)\n\t\t\t\t\t.ToList();\n\n\t\t\t\tvar sb = new StringBuilder(prefix);\n\n\t\t\t\tif (errorNames.Count > 0)\n\t\t\t\t{\n\t\t\t\t\tsb.Append($\"{errorNames.Count} Error{(errorNames.Count > 1 ? \"s\" : \"\")}: \");\n\t\t\t\t\tsb.Append(string.Join(\", \", errorNames));\n\t\t\t\t}\n\t\t\t\tif (warningNames.Count > 0)\n\t\t\t\t{\n\t\t\t\t\tif (errorNames.Count > 0)\n\t\t\t\t\t\tsb.Append(\"; \");\n\n\t\t\t\t\tsb.Append($\"{warningNames.Count} Warning{(warningNames.Count > 1 ? \"s\" : \"\")}: \");\n\t\t\t\t\tsb.Append(string.Join(\", \", warningNames));\n\t\t\t\t}\n\n                sb.Append(\" Offline\");\n\t\t\t\tMessage = sb.ToString();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMessage = \"Room Ok.\";\n\t\t\t}\n\n\t\t\t// Want to fire even if status doesn't change because the message may.\n\t\t\tStatus = initialStatus;\n\t\t\tOnStatusChange(initialStatus, Message);\n\t\t}\n\n\n\t\tvoid mon_StatusChange(object sender, MonitorStatusChangeEventArgs e)\n\t\t{\n\t\t\tProcessStatuses();\n\t\t}\n\n  /// <summary>\n  /// Stop method\n  /// </summary>\n\t\tpublic void Stop()\n\t\t{\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t#endregion\n\n  /// <summary>\n  /// AddMonitor method\n  /// </summary>\n\t\tpublic void AddMonitor(IStatusMonitor monitor)\n\t\t{\n\t\t\tif (!Monitors.Contains(monitor))\n\t\t\t\tMonitors.Add(monitor);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Fires the StatusChange event\n\t\t/// </summary>\n\t\t/// <param name=\"status\">monitor status</param>\n\t\t/// <param name=\"message\">status message</param>\n\t\tprotected void OnStatusChange(MonitorStatus status, string message)\n\t\t{\n\t\t\tvar handler = StatusChange;\n\t\t\tif (handler != null)\n\t\t\t\thandler(this, new MonitorStatusChangeEventArgs(status, message));\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Monitoring/SystemMonitorController.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Crestron.SimplSharpPro.Diagnostics;\nusing PepperDash.Core;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing PepperDash.Essentials.Core.Bridges;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Monitoring\n{\n    /// <summary>\n    /// Wrapper for the static SystemMonitor class to extend functionality and provide external access\n    /// to SystemMonitor via APIs\n    /// </summary>\n    public class SystemMonitorController : EssentialsBridgeableDevice\n    {\n        private const long UptimePollTime = 300000;\n        private CTimer _uptimePollTimer;\n\n        private string _uptime;\n        private string _lastStart;\n\n        /// <summary>\n        /// Event fired when any SystemMonitor property changes\n        /// </summary>\n        public event EventHandler<EventArgs> SystemMonitorPropertiesChanged;\n\n        /// <summary>\n        /// Gets or sets the ProgramStatusFeedbackCollection\n        /// </summary>\n        public Dictionary<uint, ProgramStatusFeedbacks> ProgramStatusFeedbackCollection;\n\n        /// <summary>\n        /// Gets or sets the EthernetStatusFeedbackCollection\n        /// </summary>\n        public Dictionary<short, EthernetStatusFeedbacks> EthernetStatusFeedbackCollection;\n\n        /// <summary>\n        /// Gets or sets the TimeZoneFeedback\n        /// </summary>\n        public IntFeedback TimeZoneFeedback { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the TimeZoneTextFeedback\n        /// </summary>\n        public StringFeedback TimeZoneTextFeedback { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the IoControllerVersionFeedback\n        /// </summary>\n        public StringFeedback IoControllerVersionFeedback { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the SnmpVersionFeedback\n        /// </summary>\n        public StringFeedback SnmpVersionFeedback { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the BaCnetAppVersionFeedback\n        /// </summary>\n        public StringFeedback BaCnetAppVersionFeedback { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the ControllerVersionFeedback\n        /// </summary>\n        public StringFeedback ControllerVersionFeedback { get; protected set; }\n\n        //new feedbacks. Issue #50\n        /// <summary>\n        /// Gets or sets the SerialNumberFeedback\n        /// </summary>\n        public StringFeedback SerialNumberFeedback { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the ModelFeedback\n        /// </summary>\n        public StringFeedback ModelFeedback { get; set; }\n\n        /// <summary>\n        /// Gets or sets the UptimeFeedback\n        /// </summary>\n        public StringFeedback UptimeFeedback { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LastStartFeedback\n        /// </summary>\n        public StringFeedback LastStartFeedback { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IsApplianceFeedback\n        /// </summary>\n\t\tpublic BoolFeedback IsApplianceFeedback { get; protected set; }\n\t    private bool _isApplianceFb\n\t    {\n\t\t\tget { return CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance; }\n\t    }\n\n        /// <summary>\n        /// Gets or sets the IsServerFeedback\n        /// </summary>\n\t\tpublic BoolFeedback IsServerFeedback { get; protected set; }\n\t    private bool _isServerFb\n\t    {\n\t\t\tget { return CrestronEnvironment.DevicePlatform == eDevicePlatform.Server; }\n\t    }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">device key</param>\n\t    public SystemMonitorController(string key)\n            : base(key)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Adding SystemMonitorController.\");\n\n            SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true;\n\n            TimeZoneFeedback = new IntFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneNumber);\n            TimeZoneTextFeedback = new StringFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneName);\n\n            IoControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.IOPVersion);\n            SnmpVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.SNMPVersion);\n            BaCnetAppVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.BACNetVersion);\n            ControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.ControlSystemVersion);\n\n            SerialNumberFeedback = new StringFeedback(() => CrestronEnvironment.SystemInfo.SerialNumber);\n            ModelFeedback = new StringFeedback(() => InitialParametersClass.ControllerPromptName);\n            UptimeFeedback = new StringFeedback(() => _uptime);\n            LastStartFeedback = new StringFeedback(()=> _lastStart);\n\n\t\t\tIsApplianceFeedback = new BoolFeedback(() => _isApplianceFb);\n\t\t\tIsServerFeedback = new BoolFeedback(() => _isServerFb);\n\n            ProgramStatusFeedbackCollection = new Dictionary<uint, ProgramStatusFeedbacks>();\n\n            foreach (var prog in SystemMonitor.ProgramCollection)\n            {\n                var program = new ProgramStatusFeedbacks(prog);\n                ProgramStatusFeedbackCollection.Add(prog.Number, program);\n            }\n\n            CreateEthernetStatusFeedbacks();\n            UpdateEthernetStatusFeeedbacks();\n\n            _uptimePollTimer = new CTimer(PollUptime,null,0, UptimePollTime);\n\n            SystemMonitor.ProgramChange += SystemMonitor_ProgramChange;\n            SystemMonitor.TimeZoneInformation.TimeZoneChange += TimeZoneInformation_TimeZoneChange;\n            CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler;\n            CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler;\n        }\n\n        private void CrestronEnvironmentOnProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType != eProgramStatusEventType.Stopping) return;\n\n            _uptimePollTimer.Stop();\n            _uptimePollTimer.Dispose();\n            _uptimePollTimer = null;\n        }\n\n        /// <summary>\n        /// PollUptime method\n        /// </summary>\n        public void PollUptime(object obj)\n        {\n            var consoleResponse = string.Empty;\n\n            CrestronConsole.SendControlSystemCommand(\"uptime\", ref consoleResponse);\n\n            ParseUptime(consoleResponse);\n\n            UptimeFeedback.FireUpdate();\n            LastStartFeedback.FireUpdate();\n        }\n\n        private void ParseUptime(string response)\n        {\n            var splitString = response.Trim().Split('\\r', '\\n');\n\n            var lastStartRaw = splitString.FirstOrDefault(o => o.Contains(\"started\"));\n            var uptimeRaw = splitString.FirstOrDefault(o => o.Contains(\"running\"));\n\n            if (!String.IsNullOrEmpty(lastStartRaw))\n            {\n                var lastStartIndex = lastStartRaw.IndexOf(':');\n                _lastStart = lastStartRaw.Substring(lastStartIndex + 1).Trim();\n            }\n\n            if (String.IsNullOrEmpty(uptimeRaw)) return;\n            var forIndex = uptimeRaw.IndexOf(\"for\", StringComparison.Ordinal);\n\n            //4 => \"for \" to get what's on the right\n            _uptime = uptimeRaw.Substring(forIndex + 4);\n        }\n\n     /// <summary>\n     /// ProcessorReboot method\n     /// </summary>\n\t    public static void ProcessorReboot()\n\t    {\n\t\t    if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return;\n            Debug.LogMessage(LogEventLevel.Information, \"Rebooting...\");\n\n\n            var response = string.Empty;\n\t\t    CrestronConsole.SendControlSystemCommand(\"reboot\", ref response);\n\t    }\n\n  /// <summary>\n  /// ProgramReset method\n  /// </summary>\n\t\tpublic static void ProgramReset(uint index)\n\t\t{\n\t\t\tif (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return;\n            Debug.LogMessage(LogEventLevel.Information, \"Resetting Program {0}...\", index);\n\n            if (index <= 0 || index > 10) return;\n\n\t\t\tvar cmd = string.Format(\"progreset -p:{0}\", index);\n\n\t\t\tvar response = string.Empty;\n\t\t\tCrestronConsole.SendControlSystemCommand(cmd, ref response);\n\t\t}\n\n        private void CrestronEnvironmentOnEthernetEventHandler(EthernetEventArgs ethernetEventArgs)\n        {\n            if (ethernetEventArgs.EthernetEventType != eEthernetEventType.LinkUp) return;\n\n            foreach (var fb in EthernetStatusFeedbackCollection)\n            {\n                fb.Value.UpdateEthernetStatus();\n            }\n        }\n\n        private void CreateEthernetStatusFeedbacks()\n        {\n            EthernetStatusFeedbackCollection = new Dictionary<short, EthernetStatusFeedbacks>();\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Creating {0} EthernetStatusFeedbacks\", InitialParametersClass.NumberOfEthernetInterfaces);\n\n            for (short i = 0; i < InitialParametersClass.NumberOfEthernetInterfaces; i++)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Creating EthernetStatusFeedback for Interface {0}\", i);\n                var ethernetInterface = new EthernetStatusFeedbacks(i);\n                EthernetStatusFeedbackCollection.Add(i, ethernetInterface);\n            }\n        }\n\n        private void UpdateEthernetStatusFeeedbacks()\n        {\n            foreach (var iface in EthernetStatusFeedbackCollection)\n            {\n                iface.Value.CurrentIpAddressFeedback.FireUpdate();\n                iface.Value.CurrentSubnetMaskFeedback.FireUpdate();\n                iface.Value.CurrentDefaultGatewayFeedback.FireUpdate();\n                iface.Value.StaticIpAddressFeedback.FireUpdate();\n                iface.Value.StaticSubnetMaskFeedback.FireUpdate();\n                iface.Value.StaticDefaultGatewayFeedback.FireUpdate();\n                iface.Value.HostNameFeedback.FireUpdate();\n                iface.Value.DnsServerFeedback.FireUpdate();\n                iface.Value.DomainFeedback.FireUpdate();\n                iface.Value.DhcpStatusFeedback.FireUpdate();\n                iface.Value.MacAddressFeedback.FireUpdate();\n            }\n        }\n\n        /// <summary>\n        /// Gets data in separate thread\n        /// </summary>\n        private void RefreshSystemMonitorData()\n        {\n            // this takes a while, launch a new thread\n            CrestronInvoke.BeginInvoke(UpdateFeedback);\n        }\n\n        private void UpdateFeedback(object o)\n        {\n            TimeZoneFeedback.FireUpdate();\n            TimeZoneTextFeedback.FireUpdate();\n            IoControllerVersionFeedback.FireUpdate();\n            SnmpVersionFeedback.FireUpdate();\n            BaCnetAppVersionFeedback.FireUpdate();\n            ControllerVersionFeedback.FireUpdate();\n            SerialNumberFeedback.FireUpdate();\n            ModelFeedback.FireUpdate();\n\n\t\t\tIsApplianceFeedback.FireUpdate();\n\t\t\tIsServerFeedback.FireUpdate();\n\n            OnSystemMonitorPropertiesChanged();\n        }\n\n        private void OnSystemMonitorPropertiesChanged()\n        {\n            var handler = SystemMonitorPropertiesChanged;\n            if (handler != null)\n            {\n                handler(this, new EventArgs());\n            }\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            RefreshSystemMonitorData();\n\n            return base.CustomActivate();\n        }\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new SystemMonitorJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<SystemMonitorJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            Debug.LogMessage(LogEventLevel.Debug, \"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Linking API starting at join: {0}\", joinStart);\n\n            TimeZoneFeedback.LinkInputSig(trilist.UShortInput[joinMap.TimeZone.JoinNumber]);\n            TimeZoneTextFeedback.LinkInputSig(trilist.StringInput[joinMap.TimeZoneName.JoinNumber]);\n\n            IoControllerVersionFeedback.LinkInputSig(trilist.StringInput[joinMap.IOControllerVersion.JoinNumber]);\n            SnmpVersionFeedback.LinkInputSig(trilist.StringInput[joinMap.SnmpAppVersion.JoinNumber]);\n            BaCnetAppVersionFeedback.LinkInputSig(trilist.StringInput[joinMap.BACnetAppVersion.JoinNumber]);\n            ControllerVersionFeedback.LinkInputSig(trilist.StringInput[joinMap.ControllerVersion.JoinNumber]);\n            SerialNumberFeedback.LinkInputSig(trilist.StringInput[joinMap.SerialNumber.JoinNumber]);\n            ModelFeedback.LinkInputSig(trilist.StringInput[joinMap.Model.JoinNumber]);\n            UptimeFeedback.LinkInputSig(trilist.StringInput[joinMap.Uptime.JoinNumber]);\n            LastStartFeedback.LinkInputSig(trilist.StringInput[joinMap.LastBoot.JoinNumber]);\n\n\t        trilist.SetSigHeldAction(joinMap.ProcessorReboot.JoinNumber, 10000, ProcessorReboot);\n\n\t\t\tIsApplianceFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsAppliance.JoinNumber]);\n\t\t\tIsServerFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsServer.JoinNumber]);\n\n            // iterate the program status feedback collection and map all the joins\n            LinkProgramInfoJoins(this, trilist, joinMap);\n\n            LinkEthernetInfoJoins(this, trilist, joinMap);\n        }\n\n        private static void LinkEthernetInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, SystemMonitorJoinMap joinMap)\n        {\n            uint ethernetSlotJoinStart = 0;\n            foreach (var fb in systemMonitorController.EthernetStatusFeedbackCollection)\n            {\n                fb.Value.CurrentIpAddressFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.CurrentIpAddress.JoinNumber]);\n                fb.Value.CurrentSubnetMaskFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.CurrentSubnetMask.JoinNumber]);\n                fb.Value.CurrentDefaultGatewayFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.CurrentDefaultGateway.JoinNumber]);\n                fb.Value.StaticIpAddressFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.StaticIpAddress.JoinNumber]);\n                fb.Value.StaticSubnetMaskFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.StaticSubnetMask.JoinNumber]);\n                fb.Value.StaticDefaultGatewayFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.StaticDefaultGateway.JoinNumber]);\n                fb.Value.HostNameFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.HostName.JoinNumber]);\n                fb.Value.MacAddressFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.MacAddress.JoinNumber]);\n                fb.Value.DomainFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.Domain.JoinNumber]);\n                fb.Value.DnsServerFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.DnsServer.JoinNumber]);\n                fb.Value.DhcpStatusFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.DhcpStatus.JoinNumber]);\n\n                ethernetSlotJoinStart += joinMap.EthernetOffsetJoin.JoinNumber;\n            }\n        }\n\n        private static void LinkProgramInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist,\n            SystemMonitorJoinMap joinMap)\n        {\n            uint programSlotJoinStart = 0;\n\n            foreach (var p in systemMonitorController.ProgramStatusFeedbackCollection)\n            {\n                var programNumber = p.Value.Program.Number;\n\n                trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStart.JoinNumber,\n                    b => SystemMonitor.ProgramCollection[programNumber].OperatingState = eProgramOperatingState.Start);\n                p.Value.ProgramStartedFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramStart.JoinNumber]);\n\n                trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStop.JoinNumber,\n                    b => SystemMonitor.ProgramCollection[programNumber].OperatingState = eProgramOperatingState.Stop);\n                p.Value.ProgramStoppedFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramStop.JoinNumber]);\n\n                trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramRegister.JoinNumber,\n                    b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Register);\n                p.Value.ProgramRegisteredFeedback.LinkInputSig(\n                    trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramRegister.JoinNumber]);\n\n                trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramUnregister.JoinNumber,\n                    b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Unregister);\n                p.Value.ProgramUnregisteredFeedback.LinkInputSig(\n                    trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramUnregister.JoinNumber]);\n\n                p.Value.ProgramNameFeedback.LinkInputSig(trilist.StringInput[programSlotJoinStart + joinMap.ProgramName.JoinNumber]);\n                p.Value.ProgramCompileTimeFeedback.LinkInputSig(\n                    trilist.StringInput[programSlotJoinStart + joinMap.ProgramCompiledTime.JoinNumber]);\n                p.Value.CrestronDataBaseVersionFeedback.LinkInputSig(\n                    trilist.StringInput[programSlotJoinStart + joinMap.ProgramCrestronDatabaseVersion.JoinNumber]);\n                p.Value.EnvironmentVersionFeedback.LinkInputSig(\n                    trilist.StringInput[programSlotJoinStart + joinMap.ProgramEnvironmentVersion.JoinNumber]);\n                p.Value.AggregatedProgramInfoFeedback.LinkInputSig(\n                    trilist.StringInput[programSlotJoinStart + joinMap.AggregatedProgramInfo.JoinNumber]);\n\n\t\t\t\ttrilist.SetSigHeldAction(programSlotJoinStart + joinMap.ProgramReset.JoinNumber, 10000, () => ProgramReset(programNumber));\n\n                programSlotJoinStart = programSlotJoinStart + joinMap.ProgramOffsetJoin.JoinSpan;\n            }\n        }\t    \n\n\t    //// Sets the time zone\n        //public void SetTimeZone(int timeZone)\n        //{\n        //    SystemMonitor.TimeZoneInformation.TimeZoneNumber = timeZone;\n        //}\n\n        /// <summary>\n        /// Responds to program change events and triggers the appropriate feedbacks to update\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"args\"></param>\n        private void SystemMonitor_ProgramChange(Program sender, ProgramEventArgs args)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Program Change Detected for slot: {0}\", sender.Number);\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Event Type: {0}\", args.EventType);\n\n            var program = ProgramStatusFeedbackCollection[sender.Number];\n\n            switch (args.EventType)\n            {\n                case eProgramChangeEventType.OperatingState:\n                    program.ProgramStartedFeedback.FireUpdate();\n                    program.ProgramStoppedFeedback.FireUpdate();\n                    program.ProgramInfo.OperatingState = args.OperatingState;\n                    if (args.OperatingState == eProgramOperatingState.Start)\n                        program.GetProgramInfo();\n                    else\n                    {\n                        program.AggregatedProgramInfoFeedback.FireUpdate();\n                        program.OnProgramInfoChanged();\n                    }\n                    break;\n                case eProgramChangeEventType.RegistrationState:\n                    program.ProgramRegisteredFeedback.FireUpdate();\n                    program.ProgramUnregisteredFeedback.FireUpdate();\n                    program.ProgramInfo.RegistrationState = args.RegistrationState;\n                    program.GetProgramInfo();\n                    break;\n            }\n        }\n\n        /// <summary>\n        /// Responds to time zone changes and updates the appropriate feedbacks\n        /// </summary>\n        /// <param name=\"args\"></param>\n        private void TimeZoneInformation_TimeZoneChange(TimeZoneEventArgs args)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Time Zone Change Detected.\");\n            TimeZoneFeedback.FireUpdate();\n            TimeZoneTextFeedback.FireUpdate();\n\n            OnSystemMonitorPropertiesChanged();\n        }\n\n        /// <summary>\n        /// Represents an EthernetStatusFeedbacks\n        /// </summary>\n        public class EthernetStatusFeedbacks\n        {\n            /// <summary>\n            /// Gets or sets the HostNameFeedback\n            /// </summary>\n            public StringFeedback HostNameFeedback { get; protected set; }\n\n            /// <summary>\n            /// Gets or sets the DnsServerFeedback\n            /// </summary>\n            public StringFeedback DnsServerFeedback { get; protected set; }\n\n            /// <summary>\n            /// Gets or sets the DomainFeedback\n            /// </summary>\n            public StringFeedback DomainFeedback { get; protected set; }\n\n            /// <summary>\n            /// Gets or sets the MacAddressFeedback\n            /// </summary>\n            public StringFeedback MacAddressFeedback { get; protected set; }\n\n            /// <summary>\n            /// Gets or sets the DhcpStatusFeedback\n            /// </summary>\n            public StringFeedback DhcpStatusFeedback { get; protected set; }\n\n\n            /// <summary>\n            /// Gets or sets the CurrentIpAddressFeedback\n            /// </summary>\n            public StringFeedback CurrentIpAddressFeedback { get; protected set; }\n\n            /// <summary>\n            /// Gets or sets the CurrentSubnetMaskFeedback\n            /// </summary>\n            public StringFeedback CurrentSubnetMaskFeedback { get; protected set; }\n\n            /// <summary>\n            /// Gets or sets the CurrentDefaultGatewayFeedback\n            /// </summary>\n            public StringFeedback CurrentDefaultGatewayFeedback { get; protected set; }\n                        \n            /// <summary>\n            /// Gets or sets the StaticIpAddressFeedback\n            /// </summary>\n            public StringFeedback StaticIpAddressFeedback { get; protected set; }\n\n            /// <summary>\n            /// Gets or sets the StaticSubnetMaskFeedback\n            /// </summary>\n            public StringFeedback StaticSubnetMaskFeedback { get; protected set; }\n\n            /// <summary>\n            /// Gets or sets the StaticDefaultGatewayFeedback\n            /// </summary>\n            public StringFeedback StaticDefaultGatewayFeedback { get; protected set; }\n\n            /// <summary>\n            /// Constructor\n            /// </summary>\n            /// <param name=\"adapterIndex\">index of the ethernet addapter</param>\n            public EthernetStatusFeedbacks(short adapterIndex)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Ethernet Information for interface {0}\", adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} Hostname: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} Current IP Address: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} Current Subnet Mask: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} Current Router: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} Static IP Address: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPADDRESS, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} Static Subnet Mask: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPMASK, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} Static Router: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_ROUTER, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} DNS Servers: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} DHCP State: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} Domain Name: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex), adapterIndex);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Adapter Index: {1} MAC Address: {0}\", CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex), adapterIndex);\n                HostNameFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex));\n\n                CurrentIpAddressFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex));\n                CurrentDefaultGatewayFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex));\n                CurrentSubnetMaskFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex));\n                StaticIpAddressFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex));\n                StaticDefaultGatewayFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex));\n                StaticSubnetMaskFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex));\n                DomainFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex));\n                DnsServerFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex));\n                MacAddressFeedback =\n                    new StringFeedback(\n                        () =>\n                            CrestronEthernetHelper.GetEthernetParameter(\n                                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex));\n\n                DhcpStatusFeedback = new StringFeedback(\n                    () =>\n                        CrestronEthernetHelper.GetEthernetParameter(\n                            CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex));\n            }\n\n            /// <summary>\n            /// UpdateEthernetStatus method\n            /// </summary>\n            public void UpdateEthernetStatus()\n            {\n                HostNameFeedback.FireUpdate();\n                CurrentIpAddressFeedback.FireUpdate();\n                CurrentSubnetMaskFeedback.FireUpdate();\n                CurrentDefaultGatewayFeedback.FireUpdate();\n                StaticIpAddressFeedback.FireUpdate();\n                StaticSubnetMaskFeedback.FireUpdate();\n                StaticDefaultGatewayFeedback.FireUpdate();\n                DomainFeedback.FireUpdate();\n                DnsServerFeedback.FireUpdate();\n                MacAddressFeedback.FireUpdate();\n                DhcpStatusFeedback.FireUpdate();\n            }\n        }\n\n\n        /// <summary>\n        /// Represents a ProgramStatusFeedbacks\n        /// </summary>\n        public class ProgramStatusFeedbacks\n        {\n            /// <summary>\n            /// Event fired when ProgramInfo changes\n            /// </summary>\n            public event EventHandler<ProgramInfoEventArgs> ProgramInfoChanged;\n\n            /// <summary>\n            /// Gets or sets the Program\n            /// </summary>\n            public Program Program;\n\n            /// <summary>\n            /// Gets or sets the ProgramInfo\n            /// </summary>\n            public ProgramInfo ProgramInfo { get; set; }\n\n            /// <summary>\n            /// Gets or sets the ProgramStartedFeedback\n            /// </summary>\n            public BoolFeedback ProgramStartedFeedback;\n\n            /// <summary>\n            /// Gets or sets the ProgramStoppedFeedback\n            /// </summary>\n            public BoolFeedback ProgramStoppedFeedback;\n\n            /// <summary>\n            /// Gets or sets the ProgramRegisteredFeedback\n            /// </summary>\n            public BoolFeedback ProgramRegisteredFeedback;\n\n            /// <summary>\n            /// Gets or sets the ProgramUnregisteredFeedback\n            /// </summary>\n            public BoolFeedback ProgramUnregisteredFeedback;\n\n            /// <summary>\n            /// Gets or sets the ProgramNameFeedback\n            /// </summary>\n            public StringFeedback ProgramNameFeedback;\n\n            /// <summary>\n            /// Gets or sets the ProgramCompileTimeFeedback\n            /// </summary>\n            public StringFeedback ProgramCompileTimeFeedback;\n\n            /// <summary>\n            /// Gets or sets the CrestronDataBaseVersionFeedback\n            /// </summary>\n            public StringFeedback CrestronDataBaseVersionFeedback;\n\n            // SIMPL windows version\n            /// <summary>\n            /// Gets or sets the EnvironmentVersionFeedback\n            /// </summary>\n            public StringFeedback EnvironmentVersionFeedback;\n\n            /// <summary>\n            /// Gets or sets the AggregatedProgramInfoFeedback\n            /// </summary>\n            public StringFeedback AggregatedProgramInfoFeedback;\n\n            /// <summary>\n            /// Constructor\n            /// </summary>\n            /// <param name=\"program\">program to get status about</param>\n            public ProgramStatusFeedbacks(Program program)\n            {\n                ProgramInfo = new ProgramInfo(program.Number);\n\n                Program = program;\n\n                ProgramInfo.OperatingState = Program.OperatingState;\n                ProgramInfo.RegistrationState = Program.RegistrationState;\n\n                ProgramStartedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Start);\n                ProgramStartedFeedback.FireUpdate();\n\n                ProgramStoppedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Stop);\n                ProgramStoppedFeedback.FireUpdate();\n\n                ProgramRegisteredFeedback =\n                    new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Register);\n                ProgramRegisteredFeedback.FireUpdate();\n\n                ProgramUnregisteredFeedback =\n                    new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Unregister);\n                ProgramUnregisteredFeedback.FireUpdate();\n\t            \n\t\t\t\tProgramNameFeedback = new StringFeedback(() => ProgramInfo.ProgramFile);\n\t\t\t\tCrestronDataBaseVersionFeedback = new StringFeedback(() => ProgramInfo.CrestronDb);\n\t\t\t\tEnvironmentVersionFeedback = new StringFeedback(() => ProgramInfo.Environment);\n                ProgramCompileTimeFeedback = new StringFeedback(() => ProgramInfo.CompileTime);\n                AggregatedProgramInfoFeedback = new StringFeedback(() => JsonConvert.SerializeObject(ProgramInfo));\n\n                GetProgramInfo();\n            }\n\n            /// <summary>\n            /// GetProgramInfo method\n            /// </summary>\n            public void GetProgramInfo()\n            {\n                CrestronInvoke.BeginInvoke(GetProgramInfo);\n            }\n\n            private void GetProgramInfo(object o)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Attempting to get program info for slot: {0}\", Program.Number);\n\n                string response = null;\n\n                if (Program.RegistrationState == eProgramRegistrationState.Unregister || Program.OperatingState == eProgramOperatingState.Stop)\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Program {0} not registered. Setting default values for program information.\",\n                        Program.Number);\n\n                    ProgramInfo = new ProgramInfo(Program.Number)\n                    {\n                        OperatingState = Program.OperatingState,\n                        RegistrationState = Program.RegistrationState\n                    };\n\n                    return;\n                }\n\n                var success = CrestronConsole.SendControlSystemCommand(\n                    string.Format(\"progcomments:{0}\", Program.Number), ref response);\n\n                if (!success)\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Progcomments Attempt Unsuccessful for slot: {0}\", Program.Number);\n                    UpdateFeedbacks();\n                    return;\n                }\n\n                if (response.ToLower().Contains(\"bad or incomplete\"))\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose,\n                        \"Program in slot {0} not running.  Setting default ProgramInfo for slot: {0}\",\n                        Program.Number);\n\n                    // Assume no valid program info.  Constructing a new object will wipe all properties\n                    ProgramInfo = new ProgramInfo(Program.Number)\n                    {\n\t\t\t\t\t\tOperatingState = Program.OperatingState,\n                        RegistrationState = Program.RegistrationState\n                    };\t\t\t\t\t\n\n                    UpdateFeedbacks();\n\n                    return;\n                }\n\n\n                // Shared properteis\n                ProgramInfo.ProgramFile = ParseConsoleData(response, \"Program File\", \": \", \"\\n\");\n                ProgramInfo.CompilerRevision = ParseConsoleData(response, \"Compiler Rev\", \": \", \"\\n\");\n                ProgramInfo.CompileTime = ParseConsoleData(response, \"Compiled On\", \": \", \"\\n\");\n                ProgramInfo.Include4Dat = ParseConsoleData(response, \"Include4.dat\", \": \", \"\\n\");\n\n\n                if (ProgramInfo.ProgramFile.Contains(\".dll\"))\n                {\n                    // SSP Program\t                \n                    ProgramInfo.FriendlyName = ParseConsoleData(response, \"Friendly Name\", \": \", \"\\n\");\n                    ProgramInfo.ApplicationName = ParseConsoleData(response, \"Application Name\", \": \", \"\\n\");\n                    ProgramInfo.ProgramTool = ParseConsoleData(response, \"Program Tool\", \": \", \"\\n\");\n                    ProgramInfo.MinFirmwareVersion = ParseConsoleData(response, \"Min Firmware Version\", \": \",\n                        \"\\n\");\n                    ProgramInfo.PlugInVersion = ParseConsoleData(response, \"PlugInVersion\", \": \", \"\\n\");\n\n\t\t\t\t\tProgramInfo.ProgramFile += string.Format(\" {0}.{1}.{2}\",\n\t\t                ProgramInfo.CompilerRevisionInfo.Major,\n\t\t                ProgramInfo.CompilerRevisionInfo.Minor,\n\t\t                ProgramInfo.CompilerRevisionInfo.Build);\n\n\t                ProgramInfo.Environment = ProgramInfo.ProgramTool;\n                }\n                else if (ProgramInfo.ProgramFile.Contains(\".smw\"))\n                {\n                    // SIMPL Windows Program\n                    ProgramInfo.FriendlyName = ParseConsoleData(response, \"Friendly Name\", \":\", \"\\n\");\n                    ProgramInfo.SystemName = ParseConsoleData(response, \"System Name\", \": \", \"\\n\");\n                    ProgramInfo.CrestronDb = ParseConsoleData(response, \"CrestronDB\", \": \", \"\\n\");\n                    ProgramInfo.Environment = ParseConsoleData(response, \"Source Env\", \": \", \"\\n\");\n                    ProgramInfo.Programmer = ParseConsoleData(response, \"Programmer\", \": \", \"\\n\");\n                }\n                Debug.LogMessage(LogEventLevel.Verbose, \"Program info for slot {0} successfully updated\", Program.Number);\n\n                UpdateFeedbacks();\n            }\n\n            private void UpdateFeedbacks()\n            {\n                ProgramNameFeedback.FireUpdate();\n                ProgramCompileTimeFeedback.FireUpdate();\n                CrestronDataBaseVersionFeedback.FireUpdate();\n                EnvironmentVersionFeedback.FireUpdate();\n\n                AggregatedProgramInfoFeedback.FireUpdate();\n\n                OnProgramInfoChanged();\n            }\n\n            /// <summary>\n            /// OnProgramInfoChanged method\n            /// </summary>\n            public void OnProgramInfoChanged()\n            {\n                //Debug.LogMessage(LogEventLevel.Debug, \"Firing ProgramInfoChanged for slot: {0}\", Program.Number);\n                var handler = ProgramInfoChanged;\n                if (handler != null)\n                {\n                    handler(this, new ProgramInfoEventArgs(ProgramInfo));\n                }\n            }\n\n            private string ParseConsoleData(string data, string line, string startString, string endString)\n            {                \n                var outputData = \"\";\n\n                if (data.Length <= 0) return outputData;\n\n                if (!data.Contains(line))\n                {\n                    return outputData;\n                }\n\n                try\n                {\n                    //Debug.LogMessage(LogEventLevel.Verbose, \"ParseConsoleData Data: {0}, Line {1}, startStirng {2}, endString {3}\", data, line, startString, endString);\n                    var linePosition = data.IndexOf(line, StringComparison.Ordinal);\n                    var startPosition = data.IndexOf(startString, linePosition, StringComparison.Ordinal) +\n                                        startString.Length;\n                    var endPosition = data.IndexOf(endString, startPosition, StringComparison.Ordinal);\n                    outputData = data.Substring(startPosition, endPosition - startPosition).Trim();\n                    //Debug.LogMessage(LogEventLevel.Verbose, \"ParseConsoleData Return: {0}\", outputData);\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Error, \"Error Parsing Console Data: {0}\", e);\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Stack Trace: {stackTrace}\", e.StackTrace);\n                }\n\n                return outputData;\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a ProgramInfo\n    /// </summary>\n    public class ProgramInfo\n    {\n        // Shared properties\n\n        /// <summary>\n        /// Gets the ProgramNumber\n        /// </summary>\n        [JsonProperty(\"programNumber\")]\n        public uint ProgramNumber { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the OperatingState\n        /// </summary>\n        [JsonConverter(typeof (StringEnumConverter))]\n        [JsonProperty(\"operatingState\")]\n        public eProgramOperatingState OperatingState { get; set; }\n\n        /// <summary>\n        /// Gets or sets the RegistrationState\n        /// </summary>\n        [JsonConverter(typeof (StringEnumConverter))]\n        [JsonProperty(\"registrationState\")]\n        public eProgramRegistrationState RegistrationState { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ProgramFile\n        /// </summary>\n        [JsonProperty(\"programFile\")]\n        public string ProgramFile { get; set; }\n\n        /// <summary>\n        /// Gets or sets the FriendlyName\n        /// </summary>\n        [JsonProperty(\"friendlyName\")]\n        public string FriendlyName { get; set; }\n\n        /// <summary>\n        /// Gets or sets the CompilerRevision\n        /// </summary>\n        [JsonProperty(\"compilerRevision\")]\n        public string CompilerRevision { get; set; }\n\n        /// <summary>\n        /// Gets the CompilerRevisionInfo\n        /// </summary>\n\t    [JsonIgnore]\n\t    public Version CompilerRevisionInfo\n\t    {\n\t\t    get\n\t\t    {\n\t\t\t    return new Version(CompilerRevision);\n\t\t    }\n\t    }\n\n        /// <summary>\n        /// Gets or sets the CompileTime\n        /// </summary>\n        [JsonProperty(\"compileTime\")]\n        public string CompileTime { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Include4Dat\n        /// </summary>\n        [JsonProperty(\"include4Dat\")]\n        public string Include4Dat { get; set; }\n\n        // SIMPL Windows properties\n\n        /// <summary>\n        /// Gets or sets the SystemName\n        /// </summary>\n        [JsonProperty(\"systemName\")]\n        public string SystemName { get; set; }\n\n        /// <summary>\n        /// Gets or sets the CrestronDb\n        /// </summary>\n        [JsonProperty(\"crestronDb\")]\n        public string CrestronDb { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Environment\n        /// </summary>\n        [JsonProperty(\"environment\")]\n        public string Environment { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Programmer\n        /// </summary>\n        [JsonProperty(\"programmer\")]\n        public string Programmer { get; set; }\n\n\n        // SSP Properties\n\n        /// <summary>\n        /// Gets or sets the ApplicationName\n        /// </summary>\n        [JsonProperty(\"applicationName\")]\n        public string ApplicationName { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ProgramTool\n        /// </summary>\n        [JsonProperty(\"programTool\")]\n        public string ProgramTool { get; set; }\n\n        /// <summary>\n        /// Gets or sets the MinFirmwareVersion\n        /// </summary>\n        [JsonProperty(\"minFirmwareVersion\")]\n        public string MinFirmwareVersion { get; set; }\n\n        /// <summary>\n        /// Gets or sets the PlugInVersion\n        /// </summary>\n        [JsonProperty(\"plugInVersion\")]\n        public string PlugInVersion { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"number\">program slot to get info about</param>\n        public ProgramInfo(uint number)\n        {\n            ProgramNumber = number;\n\n            ProgramFile = \"\";\n            FriendlyName = \"\";\n            CompilerRevision = \"\";\n\t        CompileTime = \"\";\n            Include4Dat = \"\";\n\n            SystemName = \"\";\n            CrestronDb = \"\";\n            Environment = \"\";\n            Programmer = \"\";\n\n            ApplicationName = \"\";\n            ProgramTool = \"\";\n            MinFirmwareVersion = \"\";\n            PlugInVersion = \"\";\n        }\n    }\n\n    /// <summary>\n    /// Represents a ProgramInfoEventArgs\n    /// </summary>\n    public class ProgramInfoEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the ProgramInfo\n        /// </summary>\n        public ProgramInfo ProgramInfo;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"progInfo\">program info</param>\n        public ProgramInfoEventArgs(ProgramInfo progInfo)\n        {\n            ProgramInfo = progInfo;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/PartitionSensor/EssentialsPartitionController.cs",
    "content": "﻿using PepperDash.Core;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents an abstract controller device for a partition dividing rooms that are combinable\n    /// \n    /// In Auto mode, it can use a partition sensor to automatically determine whether the partition is present.\n    /// \n    /// In Manual mode it accepts user input to tell it whether the partition is present.\n    /// </summary>\n    public class EssentialsPartitionController : IPartitionController\n    {\n        private IPartitionStateProvider _partitionSensor;\n\n        /// <summary>\n        /// Indicates whether the controller is in Auto mode or Manual mode\n        /// </summary>\n        public bool IsInAutoMode { get; private set; }\n\n        private bool _partitionPresent;\n\n        /// <summary>\n        /// Gets or sets the PartitionPresent state\n        /// </summary>\n        public bool PartitionPresent\n        {\n            get\n            {\n                if (IsInAutoMode)\n                {\n                    return _partitionSensor.PartitionPresentFeedback.BoolValue;\n                }\n\n                return _partitionPresent;\n            }\n            set\n            {\n                if (_partitionPresent == value)\n                {\n                    return;\n                }\n\n                _partitionPresent = value;\n\n                if (PartitionPresentFeedback != null)\n                {\n                    PartitionPresentFeedback.FireUpdate();\n                }\n            }\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key for the partition controller</param>\n        /// <param name=\"name\">name of the partition controller</param>\n        /// <param name=\"sensor\">partition state provider sensor</param>\n        /// <param name=\"defaultToManualMode\">whether to default to manual mode</param>\n        /// <param name=\"adjacentRoomKeys\">list of adjacent room keys</param>\n        public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List<string> adjacentRoomKeys)\n        {\n            Key = key;\n\n            Name = name;\n\n            AdjacentRoomKeys = adjacentRoomKeys;\n\n            if (sensor != null)\n            {\n                _partitionSensor = sensor;\n\n                if (!defaultToManualMode)\n                {\n                    SetAutoMode();\n                }\n                else\n                {\n                    SetManualMode();\n                }\n            }\n            else\n            {\n                SetManualMode();\n            }\n\n            PartitionPresentFeedback.FireUpdate();\n        }\n\n        private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            if (IsInAutoMode)\n            {\n                PartitionPresent = e.BoolValue;\n            }\n        }\n\n        #region IPartitionController Members\n\n        /// <summary>\n        /// Gets or sets the AdjacentRoomKeys\n        /// </summary>\n        public List<string> AdjacentRoomKeys { get; private set; }\n\n        /// <summary>\n        /// SetAutoMode method\n        /// </summary>\n        public void SetAutoMode()\n        {\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $\"Setting {Key} to Auto Mode\", this);\n\n            IsInAutoMode = true;\n            if (PartitionPresentFeedback != null)\n            {\n                PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);\n            }\n            else\n            {\n                PartitionPresentFeedback = new BoolFeedback(() => _partitionSensor.PartitionPresentFeedback.BoolValue);\n            }\n\n            if (_partitionSensor != null)\n            {\n                _partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;\n                _partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;\n                PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue;\n            }\n\n            PartitionPresentFeedback.FireUpdate();\n        }\n\n        /// <summary>\n        /// SetManualMode method\n        /// </summary>\n        public void SetManualMode()\n        {\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $\"Setting {Key} to Manual Mode\", this);\n\n            IsInAutoMode = false;\n            if (PartitionPresentFeedback != null)\n            {\n                PartitionPresentFeedback.SetValueFunc(() => _partitionPresent);\n            }\n            else\n            {\n                PartitionPresentFeedback = new BoolFeedback(() => _partitionPresent);\n            }\n\n            if (_partitionSensor != null)\n            {\n                _partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;\n                PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue;\n            }\n\n            PartitionPresentFeedback.FireUpdate();\n        }\n\n\n        /// <summary>\n        /// SetPartitionStatePresent method\n        /// </summary>\n        public void SetPartitionStatePresent()\n        {\n            if (!IsInAutoMode)\n            {\n                PartitionPresent = true;\n                PartitionPresentFeedback.FireUpdate();\n            }\n        }\n\n        /// <summary>\n        /// SetPartitionStateNotPresent method\n        /// </summary>\n        public void SetPartitionStateNotPresent()\n        {\n            if (!IsInAutoMode)\n            {\n                PartitionPresent = false;\n                PartitionPresentFeedback.FireUpdate();\n            }\n        }\n\n        /// <summary>\n        /// ToggglePartitionState method\n        /// </summary>\n        public void ToggglePartitionState()\n        {\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $\"Toggling Partition State for {Key}\", this);\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $\"IsInAutoMode: {IsInAutoMode}\", this);\n\n            if (!IsInAutoMode)\n            {\n                PartitionPresent = !PartitionPresent;\n                PartitionPresentFeedback.FireUpdate();\n            }\n        }\n\n        #endregion\n\n        #region IPartitionStateProvider Members\n\n        /// <summary>\n        /// Gets or sets the PartitionPresentFeedback\n        /// </summary>\n        public BoolFeedback PartitionPresentFeedback { get; private set; }\n\n        #endregion\n\n        #region IKeyName Members\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; private set; }\n\n        #endregion\n\n        #region IKeyed Members\n\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; private set; }\n\n        #endregion\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/PartitionSensor/IPartitionStateProvider.cs",
    "content": "﻿using System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IPartitionStateProvider\n    /// </summary>\n    public interface IPartitionStateProvider : IKeyName\n    {\n        /// <summary>\n        /// Feedback indicating whether the partition is present\n        /// </summary>\n        [JsonIgnore]\n        BoolFeedback PartitionPresentFeedback { get; }\n\n        /// <summary>\n        /// Indicates whether the partition is present\n        /// </summary>\n        [JsonProperty(\"partitionPresent\")]\n        bool PartitionPresent { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IPartitionController\n    /// </summary>\n    public interface IPartitionController : IPartitionStateProvider\n    {\n        /// <summary>\n        /// List of adjacent room keys\n        /// </summary>\n        [JsonProperty(\"adjacentRoomKeys\")]\n        List<string> AdjacentRoomKeys { get; }\n\n        /// <summary>\n        /// Indicates whether the controller is in Auto mode or Manual mode\n        /// </summary>\n        [JsonProperty(\"isInAutoMode\")]\n        bool IsInAutoMode { get; }\n\n        /// <summary>\n        /// Sets the PartitionPresent state\n        /// </summary>\n        void SetPartitionStatePresent();\n\n        /// <summary>\n        /// Sets the PartitionPresent state to not present\n        /// </summary>\n        void SetPartitionStateNotPresent();\n\n        /// <summary>\n        /// Toggles the PartitionPresent state\n        /// </summary>\n        void ToggglePartitionState();\n\n        /// <summary>\n        /// Sets the controller to Manual mode\n        /// </summary>\n        void SetManualMode();\n\n        /// <summary>\n        /// Sets the controller to Auto mode\n        /// </summary>\n        void SetAutoMode();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <Configurations>Debug;Release;Debug 4.7.2</Configurations>\n  </PropertyGroup>\n  <PropertyGroup>\n    <TargetFramework>net472</TargetFramework>\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n    <AssemblyName>PepperDash_Essentials_Core</AssemblyName>\n    <RootNamespace>PepperDash.Essentials.Core</RootNamespace>\n    <Title>PepperDash Essentials Core</Title>\n    <PackageId>PepperDash.Essentials.Core</PackageId>\n    <InformationalVersion>$(Version)</InformationalVersion>\n    <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug 4.7.2|AnyCPU'\">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <DocumentationFile>bin\\$(Configuration)\\PepperDash_Essentials_Core.xml</DocumentationFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Crestron.SimplSharp.SDK.ProgramLibrary\" Version=\"2.21.90\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Crestron\\CrestronGenericBaseDevice.cs.orig\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Folder Include=\"Display\\\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\PepperDash.Core\\PepperDash.Core.csproj\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "src/PepperDash.Essentials.Core/PepperDash_Essentials_Core.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=Crestron_0020Web_0020Server/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=Web/@EntryIndexedValue\">False</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Plugins/IPluginDeviceFactory.cs",
    "content": "using System;\nusing System.Collections.Generic;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines a class that is capable of loading custom plugin device types\n    /// </summary>\n    public interface IPluginDeviceFactory : IDeviceFactory\n    {\n        /// <summary>\n        /// Required to define the minimum version for Essentials in the format xx.yy.zz\n        /// </summary>\n        string MinimumEssentialsFrameworkVersion { get; }\n\n    }\n\n    /// <summary>\n    /// Defines a class that is capable of loading custom plugin device types for development purposes\n    /// </summary>\n    [Obsolete(\"This interface is obsolete and will be removed in a future version.\" +\n              \" Use IPluginDeviceFactory instead and check Global.IsRunningDevelopmentVersion to determine if the Essentials framework is in development mode.\")]\n    public interface IPluginDevelopmentDeviceFactory : IPluginDeviceFactory\n    {\n        /// <summary>\n        /// Gets a list of all the development versions of the Essentials framework that are supported by this factory.\n        /// </summary>\n        List<string> DevelopmentEssentialsFrameworkVersions { get; }\n    }\n}\n\n\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Deals with loading plugins at runtime\n    /// </summary>\n    public static class PluginLoader\n    {\n        /// <summary>\n        /// The complete list of loaded assemblies. Includes Essentials Framework assemblies and plugins\n        /// </summary>\n        public static List<LoadedAssembly> LoadedAssemblies { get; private set; }\n\n        /// <summary>\n        /// The list of assemblies loaded from the plugins folder\n        /// </summary>\n        static List<LoadedAssembly> LoadedPluginFolderAssemblies;\n\n        /// <summary>\n        /// The assembly for the Essentials Framework\n        /// </summary>\n        public static LoadedAssembly EssentialsAssembly { get; private set; }\n\n        /// <summary>\n        /// The assembly for the PepperDash Core\n        /// </summary>\n        public static LoadedAssembly PepperDashCoreAssembly { get; private set; }\n\n        /// <summary>\n        /// The list of assemblies loaded from the Essentials plugins folder\n        /// </summary>\n        public static List<LoadedAssembly> EssentialsPluginAssemblies { get; private set; }\n\n        /// <summary>\n        /// The directory to look in for .cplz plugin packages\n        /// </summary>\n        static string _pluginDirectory => Global.FilePathPrefix + \"plugins\";\n\n        /// <summary>\n        /// The directory where plugins will be moved to and loaded from\n        /// </summary>\n        static string _loadedPluginsDirectoryPath => _pluginDirectory + Global.DirectorySeparator + \"loadedAssemblies\";\n\n        // The temp directory where .cplz archives will be unzipped to\n        static string _tempDirectory => _pluginDirectory + Global.DirectorySeparator + \"temp\";\n\n\n        static PluginLoader()\n        {\n            LoadedAssemblies = new List<LoadedAssembly>();\n            LoadedPluginFolderAssemblies = new List<LoadedAssembly>();\n            EssentialsPluginAssemblies = new List<LoadedAssembly>();\n        }\n\n        /// <summary>\n        /// AddProgramAssemblies method\n        /// </summary>\n        public static void AddProgramAssemblies()\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, \"Getting Assemblies loaded with Essentials\");\n            // Get the loaded assembly filenames\n            var appDi = new DirectoryInfo(Global.ApplicationDirectoryPathPrefix);\n            var assemblyFiles = appDi.GetFiles(\"*.dll\");\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Found {0} Assemblies\", assemblyFiles.Length);\n\n            foreach (var fi in assemblyFiles.Where(fi => fi.Name.Contains(\"Essentials\") || fi.Name.Contains(\"PepperDash\")))\n            {\n                string version = string.Empty;\n                Assembly assembly = null;\n\n                switch (fi.Name)\n                {\n                    case (\"PepperDashEssentials.dll\"):\n                        {\n                            version = Global.AssemblyVersion;\n                            EssentialsAssembly = new LoadedAssembly(fi.Name, version, assembly);\n                            break;\n                        }\n                    case (\"PepperDash_Essentials_Core.dll\"):\n                        {\n                            version = Global.AssemblyVersion;\n                            break;\n                        }\n                    case (\"Essentials Devices Common.dll\"):\n                        {\n                            version = Global.AssemblyVersion;\n                            break;\n                        }\n                    case (\"PepperDashCore.dll\"):\n                        {\n                            Debug.LogMessage(LogEventLevel.Verbose, \"Found PepperDash_Core.dll\");\n                            version = Debug.PepperDashCoreVersion;\n                            Debug.LogMessage(LogEventLevel.Verbose, \"PepperDash_Core Version: {0}\", version);\n                            PepperDashCoreAssembly = new LoadedAssembly(fi.Name, version, assembly);\n                            break;\n                        }\n                }\n\n                LoadedAssemblies.Add(new LoadedAssembly(fi.Name, version, assembly));\n            }\n\n            if (Debug.Level > 1)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Loaded Assemblies:\");\n\n                foreach (var assembly in LoadedAssemblies)\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Assembly: {0}\", assembly.Name);\n                }\n            }\n        }\n\n\n        /// <summary>\n        /// SetEssentialsAssembly method\n        /// </summary>\n        public static void SetEssentialsAssembly(string name, Assembly assembly)\n        {\n            var loadedAssembly = LoadedAssemblies.FirstOrDefault(la => la.Name.Equals(name));\n\n            if (loadedAssembly != null)\n            {\n                loadedAssembly.SetAssembly(assembly);\n            }\n        }\n\n        /// <summary>\n        /// Loads an assembly via Reflection and adds it to the list of loaded assemblies\n        /// </summary>\n        /// <param name=\"filePath\"></param>\n        static LoadedAssembly LoadAssembly(string filePath)\n        {\n            try\n            {\n                //Debug.LogMessage(LogEventLevel.Verbose, \"Attempting to load {0}\", filePath);\n                var assembly = Assembly.LoadFrom(filePath);\n                if (assembly != null)\n                {\n                    var assyVersion = GetAssemblyVersion(assembly);\n\n                    var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);\n                    LoadedAssemblies.Add(loadedAssembly);\n                    Debug.LogMessage(LogEventLevel.Information, \"Loaded assembly '{0}', version {1}\", loadedAssembly.Name, loadedAssembly.Version);\n                    return loadedAssembly;\n                }\n                else\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Unable to load assembly: '{0}'\", filePath);\n                }\n\n                return null;\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Error loading assembly from {path}\", null, filePath);\n                return null;\n            }\n\n        }\n\n        /// <summary>\n        /// Attempts to get the assembly informational version and if not possible gets the version\n        /// </summary>\n        /// <param name=\"assembly\"></param>\n        /// <returns></returns>\n        public static string GetAssemblyVersion(Assembly assembly)\n        {\n            var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);\n            if (ver != null && ver.Length > 0)\n            {\n                // Get the AssemblyInformationalVersion              \n                AssemblyInformationalVersionAttribute verAttribute = ver[0] as AssemblyInformationalVersionAttribute;\n                return verAttribute.InformationalVersion;\n            }\n            else\n            {\n                // Get the AssemblyVersion\n                var version = assembly.GetName().Version;\n                var verStr = string.Format(\"{0}.{1}.{2}.{3}\", version.Major, version.Minor, version.Build, version.Revision);\n                return verStr;\n            }\n        }\n\n        /// <summary>\n        /// Checks if the filename matches an already loaded assembly file's name\n        /// </summary>\n        /// <param name=\"name\">name of loaded assembly</param>\n        /// <returns>True if file already matches loaded assembly file.</returns>\n        public static bool CheckIfAssemblyLoaded(string name)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, \"Checking if assembly: {0} is loaded...\", name);\n            var loadedAssembly = LoadedAssemblies.FirstOrDefault(s => s.Name.Equals(name));\n\n            if (loadedAssembly != null)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Assembly already loaded.\");\n                return true;\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"Assembly not loaded.\");\n                return false;\n            }\n        }\n\n        /// <summary>\n        /// Associates the specified assembly with the given name in the loaded assemblies collection.\n        /// </summary>\n        /// <remarks>If an assembly with the specified name already exists in the loaded assemblies collection, \n        /// this method updates its associated assembly. If no matching name is found, the method does nothing.</remarks>\n        /// <param name=\"name\">The name used to identify the assembly. This value is case-sensitive and must not be null or empty.</param>\n        /// <param name=\"assembly\">The assembly to associate with the specified name. This value must not be null.</param>\n        public static void AddLoadedAssembly(string name, Assembly assembly)\n        {\n            var loadedAssembly = LoadedAssemblies.FirstOrDefault(la => la.Name.Equals(name));\n\n            loadedAssembly?.SetAssembly(assembly);\n        }\n\n        /// <summary>\n        /// Used by console command to report the currently loaded assemblies and versions\n        /// </summary>\n        /// <param name=\"command\"></param>\n        public static void ReportAssemblyVersions(string command)\n        {\n            CrestronConsole.ConsoleCommandResponse(\"Essentials Version: {0}\" + CrestronEnvironment.NewLine, Global.AssemblyVersion);\n            CrestronConsole.ConsoleCommandResponse(\"PepperDash Core Version: {0}\" + CrestronEnvironment.NewLine, PepperDashCoreAssembly.Version);\n            CrestronConsole.ConsoleCommandResponse(\"Essentials Plugin Versions:\" + CrestronEnvironment.NewLine);\n            foreach (var assembly in EssentialsPluginAssemblies)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"{0} Version: {1}\" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);\n            }\n\n            //CrestronConsole.ConsoleCommandResponse(\"Loaded Assemblies:\" + CrestronEnvironment.NewLine);\n            //foreach (var assembly in LoadedAssemblies)\n            //{\n            //    CrestronConsole.ConsoleCommandResponse(\"{0} Version: {1}\" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);\n            //}\n        }\n        \n        /// <summary>\n        /// Moves any .dll assemblies not already loaded from the plugins folder to loadedPlugins folder\n        /// </summary>\n        static void MoveDllAssemblies()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Looking for .dll assemblies from plugins folder...\");\n\n            var pluginDi = new DirectoryInfo(_pluginDirectory);\n            var pluginFiles = pluginDi.GetFiles(\"*.dll\");\n\n            if (pluginFiles.Length > 0)\n            {\n                if (!Directory.Exists(_loadedPluginsDirectoryPath))\n                {\n                    Directory.CreateDirectory(_loadedPluginsDirectoryPath);\n                }\n            }\n\n            foreach (var pluginFile in pluginFiles)\n            {\n                try\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Found .dll: {0}\", pluginFile.Name);\n\n                    if (!CheckIfAssemblyLoaded(pluginFile.Name))\n                    {\n                        string filePath = string.Empty;\n\n                        filePath = _loadedPluginsDirectoryPath + Global.DirectorySeparator + pluginFile.Name;\n\n                        // Check if there is a previous file in the loadedPlugins directory and delete\n                        if (File.Exists(filePath))\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, \"Found existing file in loadedPlugins: {0} Deleting and moving new file to replace it\", filePath);\n                            File.Delete(filePath);\n                        }\n\n                        // Move the file\n                        File.Move(pluginFile.FullName, filePath);\n                        Debug.LogMessage(LogEventLevel.Verbose, \"Moved {0} to {1}\", pluginFile.FullName, filePath);\n                    }\n                    else\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, \"Skipping assembly: {0}.  There is already an assembly with that name loaded.\", pluginFile.FullName);\n                    }\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Error with plugin file {0} . Exception: {1}\", pluginFile.FullName, e);\n                    continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here\n                }\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, \"Done with .dll assemblies\");\n        }\n\n        /// <summary>\n        /// Unzips each .cplz archive into the temp directory and moves any unloaded files into loadedPlugins\n        /// </summary>\n        static void UnzipAndMoveCplzArchives()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Looking for .cplz archives from user folder...\");\n            //var di = new DirectoryInfo(_pluginDirectory);\n            //var zFiles = di.GetFiles(\"*.cplz\");\n\n            //// Find cplz files at the root of the user folder. Makes development/testing easier for VC-4, and helps with mistakes by end users\n\n            //var userDi = new DirectoryInfo(Global.FilePathPrefix);\n            //var userZFiles = userDi.GetFiles(\"*.cplz\");\n\n            Debug.LogInformation(\"Checking {folder} for .cplz files\", Global.FilePathPrefix);\n            var cplzFiles = Directory.GetFiles(Global.FilePathPrefix, \"*.cplz\", SearchOption.AllDirectories)\n                .Select(f => new FileInfo(f))\n                .ToArray();\n\n            if (cplzFiles.Length > 0)\n            {\n                if (!Directory.Exists(_loadedPluginsDirectoryPath))\n                {\n                    Directory.CreateDirectory(_loadedPluginsDirectoryPath);\n                }\n            }\n\n            foreach (var zfi in cplzFiles)\n            {\n                Directory.CreateDirectory(_tempDirectory);\n                var tempDi = new DirectoryInfo(_tempDirectory);\n\n                Debug.LogMessage(LogEventLevel.Information, \"Found cplz: {0}. Unzipping into temp plugins directory\", zfi.FullName);\n                var result = CrestronZIP.Unzip(zfi.FullName, tempDi.FullName);\n                Debug.LogMessage(LogEventLevel.Information, \"UnZip Result: {0}\", result.ToString());\n\n                var tempFiles = tempDi.GetFiles(\"*.dll\");\n                foreach (var tempFile in tempFiles)\n                {\n                    try\n                    {\n                        if (!CheckIfAssemblyLoaded(tempFile.Name))\n                        {\n                            string filePath = string.Empty;\n\n                            filePath = _loadedPluginsDirectoryPath + Global.DirectorySeparator + tempFile.Name;\n\n                            // Check if there is a previous file in the loadedPlugins directory and delete\n                            if (File.Exists(filePath))\n                            {\n                                Debug.LogMessage(LogEventLevel.Information, \"Found existing file in loadedPlugins: {0} Deleting and moving new file to replace it\", filePath);\n                                File.Delete(filePath);\n                            }\n\n                            // Move the file\n                            File.Move(tempFile.FullName, filePath);\n                            Debug.LogMessage(LogEventLevel.Verbose, \"Moved {0} to {1}\", tempFile.FullName, filePath);\n                        }\n                        else\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, \"Skipping assembly: {0}.  There is already an assembly with that name loaded.\", tempFile.FullName);\n                        }\n                    }\n                    catch (Exception e)\n                    {\n                        Debug.LogMessage(LogEventLevel.Verbose, \"Assembly {0} is not a custom assembly. Exception: {1}\", tempFile.FullName, e);\n                        continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here\n                    }\n                }\n\n                // Delete the .cplz and the temp directory\n                Directory.Delete(_tempDirectory, true);\n                zfi.Delete();\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, \"Done with .cplz archives\");\n        }\n\n        /// <summary>\n        /// Attempts to load the assemblies from the loadedPlugins folder\n        /// </summary>\n        static void LoadPluginAssemblies()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Loading assemblies from loadedPlugins folder...\");\n            var pluginDi = new DirectoryInfo(_loadedPluginsDirectoryPath);\n            var pluginFiles = pluginDi.GetFiles(\"*.dll\");\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Found {0} plugin assemblies to load\", pluginFiles.Length);\n\n            foreach (var pluginFile in pluginFiles)\n            {\n                var loadedAssembly = LoadAssembly(pluginFile.FullName);\n\n                LoadedPluginFolderAssemblies.Add(loadedAssembly);\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, \"All Plugins Loaded.\");\n        }\n\n        /// <summary>\n        /// Iterate the loaded assemblies and try to call the LoadPlugin method\n        /// </summary>\n        static void LoadCustomPluginTypes()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Loading Custom Plugin Types...\");\n            foreach (var loadedAssembly in LoadedPluginFolderAssemblies)\n            {\n                // iteratate this assembly's classes, looking for \"LoadPlugin()\" methods\n                try\n                {\n                    var assy = loadedAssembly.Assembly;\n                    Type[] types = { };\n                    try\n                    {\n                        types = assy.GetTypes();\n                        Debug.LogMessage(LogEventLevel.Debug, $\"Got types for assembly {assy.GetName().Name}\");\n                    }\n                    catch (TypeLoadException e)\n                    {\n                        Debug.LogMessage(LogEventLevel.Error, \"Unable to get types for assembly {0}: {1}\",\n                            loadedAssembly.Name, e.Message);\n                        Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace);\n                        continue;\n                    }\n\n                    foreach (var type in types)\n                    {\n                        try\n                        {\n                            if (typeof(IPluginDeviceFactory).IsAssignableFrom(type) && !type.IsAbstract)\n                            {\n                                var plugin =\n                                    (IPluginDeviceFactory)Activator.CreateInstance(type);\n                                LoadCustomPlugin(plugin, loadedAssembly);\n                            }\n                        }\n                        catch (NotSupportedException)\n                        {\n                            //this happens for dlls that aren't PD dlls, like ports of Mono classes into S#. Swallowing.                               \n                        }\n                        catch (Exception e)\n                        {\n                            Debug.LogMessage(LogEventLevel.Error, \"Load Plugin not found. {0}.{2} is not a plugin factory. Exception: {1}\",\n                                loadedAssembly.Name, e.Message, type.Name);\n                            continue;\n                        }\n\n                    }\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Error Loading assembly {0}: {1}\",\n                           loadedAssembly.Name, e.Message);\n                    Debug.LogMessage(LogEventLevel.Verbose, \"{0}\", e.StackTrace);\n                    continue;\n                }\n            }\n            // plugin dll will be loaded.  Any classes in plugin should have a static constructor\n            // that registers that class with the Core.DeviceFactory\n            Debug.LogMessage(LogEventLevel.Information, \"Done Loading Custom Plugin Types.\");\n        }\n\n        /// <summary>\n        /// Loads a custom plugin and performs a dependency check to ensure compatibility with the required Essentials\n        /// framework version.\n        /// </summary>\n        /// <remarks>This method verifies that the plugin meets the minimum required Essentials framework version\n        /// before loading it.  If the plugin fails the dependency check, it is skipped, and a log message is generated.  If\n        /// the plugin passes the check, it is loaded, and its type factories are initialized.</remarks>\n        /// <param name=\"pluginDeviceFactory\">The plugin to be loaded, implementing the <see cref=\"IPluginDeviceFactory\"/> interface.  If the plugin also\n        /// implements <see cref=\"IPluginDevelopmentDeviceFactory\"/>, additional checks for development versions are\n        /// performed.</param>\n        /// <param name=\"loadedAssembly\">The assembly associated with the plugin being loaded. This is used for logging and tracking purposes.</param>\n        static void LoadCustomPlugin(IPluginDeviceFactory pluginDeviceFactory, LoadedAssembly loadedAssembly)\n        {\n            var passed = pluginDeviceFactory is IPluginDevelopmentDeviceFactory developmentPlugin ? Global.IsRunningDevelopmentVersion\n                (developmentPlugin.DevelopmentEssentialsFrameworkVersions, developmentPlugin.MinimumEssentialsFrameworkVersion)\n                : Global.IsRunningMinimumVersionOrHigher(pluginDeviceFactory.MinimumEssentialsFrameworkVersion);\n\n            if (!passed)\n            {\n                Debug.LogMessage(LogEventLevel.Information,\n                    \"\\r\\n********************\\r\\n\\tPlugin indicates minimum Essentials version {0}.  Dependency check failed.  Skipping Plugin {1}\\r\\n********************\",\n                    pluginDeviceFactory.MinimumEssentialsFrameworkVersion, loadedAssembly.Name);\n                return;\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Passed plugin passed dependency check (required version {0})\", pluginDeviceFactory.MinimumEssentialsFrameworkVersion);\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, \"Loading plugin factory: {0}\", loadedAssembly.Name);\n\n            LoadDeviceFactories(pluginDeviceFactory);\n\n            if (!EssentialsPluginAssemblies.Contains(loadedAssembly))\n                EssentialsPluginAssemblies.Add(loadedAssembly);\n        }\n\n        /// <summary>\n        /// Loads device factories from the specified plugin device factory and registers them for use.\n        /// </summary>\n        /// <remarks>This method retrieves metadata from the provided <paramref name=\"deviceFactory\"/>, including\n        /// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type\n        /// names are converted to lowercase for registration.</remarks>\n        /// <param name=\"deviceFactory\">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>\n        private static void LoadDeviceFactories(IPluginDeviceFactory deviceFactory)\n        {\n            foreach (var typeName in deviceFactory.TypeNames)\n            {\n                string description = deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0\n                    ? descriptionAttribute[0].Description\n                    : \"No description available\";\n\n                DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);\n            }\n        }\n\n\n        /// <summary>\n        /// LoadPlugins method\n        /// </summary>\n        public static void LoadPlugins()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Attempting to Load Plugins from {_pluginDirectory}\", _pluginDirectory);\n\n            if (Directory.Exists(_pluginDirectory))\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Plugins directory found, checking for plugins\");\n\n                // Deal with any .dll files\n                MoveDllAssemblies();\n\n                // Deal with any .cplz files\n                UnzipAndMoveCplzArchives();\n\n                if (Directory.Exists(_loadedPluginsDirectoryPath))\n                {\n                    // Load the assemblies from the loadedPlugins folder into the AppDomain\n                    LoadPluginAssemblies();\n\n                    // Load the types from any custom plugin assemblies\n                    LoadCustomPluginTypes();\n                }\n            }\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a LoadedAssembly\n    /// </summary>\n    public class LoadedAssembly\n    {\n        /// <summary>\n        /// Gets the name of the assembly\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; private set; }\n\n        /// <summary>\n        /// Gets the version of the assembly\n        /// </summary>\n        [JsonProperty(\"version\")]\n        public string Version { get; private set; }\n\n        /// <summary>\n        /// Gets the assembly\n        /// </summary>\n        [JsonIgnore]\n        public Assembly Assembly { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the LoadedAssembly class\n        /// </summary>\n        /// <param name=\"name\">The name of the assembly</param>\n        /// <param name=\"version\">The version of the assembly</param>\n        /// <param name=\"assembly\">The assembly</param>\n        public LoadedAssembly(string name, string version, Assembly assembly)\n        {\n            Name = name;\n            Version = version;\n            Assembly = assembly;\n        }\n\n        /// <summary>\n        /// SetAssembly method\n        /// </summary>\n        public void SetAssembly(Assembly assembly)\n        {\n            Assembly = assembly;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Presets/DevicePresets.cs",
    "content": "﻿ \n\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\n\n//using SSMono.IO;\nusing PepperDash.Core.WebApi.Presets;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Presets\n{\n    /// <summary>\n    /// Class that represents the model behind presets display\n    /// </summary>\n    public class DevicePresetsModel : Device\n    {\n        /// <summary>\n        /// Delegate for preset recalled event\n        /// </summary>\n        /// <param name=\"device\">device that recalled a preset</param>\n        /// <param name=\"channel\">channel that was recalled</param>\n        public delegate void PresetRecalledCallback(ISetTopBoxNumericKeypad device, string channel);\n\n        /// <summary>\n        /// Delegate for presets saved event\n        /// </summary>\n        /// <param name=\"presets\">list of presets that were saved</param>\n        public delegate void PresetsSavedCallback(List<PresetChannel> presets);\n\n        private readonly CCriticalSection _fileOps = new CCriticalSection();\n        private readonly bool _initSuccess;\n\n        private readonly ISetTopBoxNumericKeypad _setTopBox;\n\n        /// <summary>\n        /// The methods on the STB device to call when dialing\n        /// </summary>\n        private Dictionary<char, Action<bool>> _dialFunctions;\n\n        private bool _dialIsRunning;\n        private Action<bool> _enterFunction;\n        private string _filePath;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key for the device</param>\n        /// <param name=\"setTopBox\">set top box device</param>\n        /// <param name=\"fileName\">file name for presets</param>\n        public DevicePresetsModel(string key, ISetTopBoxNumericKeypad setTopBox, string fileName)\n            : this(key, fileName)\n        {\n            try\n            {\n                _setTopBox = setTopBox;\n\n                // Grab the digit functions from the device\n                // If any fail, the whole thing fails peacefully\n                _dialFunctions = new Dictionary<char, Action<bool>>(10)\n                {\n                    {'1', setTopBox.Digit1},\n                    {'2', setTopBox.Digit2},\n                    {'3', setTopBox.Digit3},\n                    {'4', setTopBox.Digit4},\n                    {'5', setTopBox.Digit5},\n                    {'6', setTopBox.Digit6},\n                    {'7', setTopBox.Digit7},\n                    {'8', setTopBox.Digit8},\n                    {'9', setTopBox.Digit9},\n                    {'0', setTopBox.Digit0},\n                    {'-', setTopBox.Dash}\n                };\n            }\n            catch\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"DevicePresets '{0}', not attached to INumericKeypad device. Ignoring\", key);\n                _dialFunctions = null;\n                return;\n            }\n\n            _enterFunction = setTopBox.KeypadEnter;\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key for the device</param>\n        /// <param name=\"fileName\">file name for presets</param>\n        public DevicePresetsModel(string key, string fileName) : base(key)\n        {\n            PulseTime = 150;\n            DigitSpacingMs = 150;\n\n            UseLocalImageStorage = true;\n\n            ImagesLocalHostPrefix = \"http://\" + CrestronEthernetHelper.GetEthernetParameter(\n                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);\n            ImagesPathPrefix = @\"/presets/images.zip/\";\n            ListPathPrefix = @\"/html/presets/lists/\";\n\n            SetFileName(fileName);\n\n            _initSuccess = true;\n        }\n\n        /// <summary>\n        /// Event fired when a preset is recalled\n        /// </summary>\n        public event PresetRecalledCallback PresetRecalled;\n\n        /// <summary>\n        /// Event fired when presets are saved\n        /// </summary>\n        public event PresetsSavedCallback PresetsSaved;\n\n        /// <summary>\n        /// Gets or sets the PulseTime\n        /// </summary>\n        public int PulseTime { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DigitSpacingMs\n        /// </summary>\n        public int DigitSpacingMs { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the PresetsAreLoaded\n        /// </summary>\n        public bool PresetsAreLoaded { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the PresetsList\n        /// </summary>\n        public List<PresetChannel> PresetsList { get; private set; }\n\n        /// <summary>\n        /// Gets the Count of presets\n        /// </summary>\n        public int Count\n        {\n            get { return PresetsList != null ? PresetsList.Count : 0; }\n        }\n\n        /// <summary>\n        /// Gets or sets the UseLocalImageStorage\n        /// </summary>\n        public bool UseLocalImageStorage { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ImagesLocalHostPrefix\n        /// </summary>\n        public string ImagesLocalHostPrefix { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ImagesPathPrefix\n        /// </summary>\n        public string ImagesPathPrefix { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ListPathPrefix\n        /// </summary>\n        public string ListPathPrefix { get; set; }\n\n        /// <summary>\n        /// Event fired when presets are loaded\n        /// </summary>\n        public event EventHandler PresetsLoaded;\n\n\n        /// <summary>\n        /// SetFileName method\n        /// </summary>\n        public void SetFileName(string path)\n        {\n            _filePath = ListPathPrefix + path;\n\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Setting presets file path to {0}\", _filePath);\n            LoadChannels();\n        }\n\n        /// <summary>\n        /// LoadChannels method\n        /// </summary>\n        public void LoadChannels()\n        {\n            try\n            {\n                _fileOps.Enter();\n\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Loading presets from {0}\", _filePath);\n                PresetsAreLoaded = false;\n                try\n                {\n                    var pl = JsonConvert.DeserializeObject<PresetsList>(File.ReadToEnd(_filePath, Encoding.ASCII));\n                    Name = pl.Name;\n                    PresetsList = pl.Channels;\n\n                    Debug.LogMessage(LogEventLevel.Verbose, this, \"Loaded {0} presets\", PresetsList.Count);\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, this,\n                        \"LoadChannels: Error reading presets file. These presets will be empty:\\r  '{0}'\\r  Error:{1}\",\n                        _filePath, e.Message);\n                    // Just save a default empty list\n                    PresetsList = new List<PresetChannel>();\n                }\n                PresetsAreLoaded = true;\n\n                var handler = PresetsLoaded;\n                if (handler != null)\n                {\n                    handler(this, EventArgs.Empty);\n                }\n            }\n            finally\n            {\n                _fileOps.Leave();\n            }\n        }\n\n        /// <summary>\n        /// Dial method\n        /// </summary>\n        public void Dial(int presetNum)\n        {\n            if (presetNum <= PresetsList.Count)\n            {\n                Dial(PresetsList[presetNum - 1].Channel);\n            }\n        }\n\n        /// <summary>\n        /// Dial method\n        /// </summary>\n        public void Dial(string chanNum)\n        {\n            if (_dialIsRunning || !_initSuccess)\n            {\n                return;\n            }\n            if (_dialFunctions == null)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"DevicePresets '{0}', not attached to keypad device. Ignoring channel\", Key);\n                return;\n            }\n\n            _dialIsRunning = true;\n            CrestronInvoke.BeginInvoke(o =>\n            {\n                foreach (var c in chanNum.ToCharArray())\n                {\n                    if (_dialFunctions.ContainsKey(c))\n                    {\n                        Pulse(_dialFunctions[c]);\n                    }\n                    CrestronEnvironment.Sleep(DigitSpacingMs);\n                }\n\n                if (_enterFunction != null)\n                {\n                    Pulse(_enterFunction);\n                }\n                _dialIsRunning = false;\n            });\n\n            if (_setTopBox == null) return;\n\n            OnPresetRecalled(_setTopBox, chanNum);\n        }\n\n        /// <summary>\n        /// Dial method\n        /// </summary>\n        public void Dial(int presetNum, ISetTopBoxNumericKeypad setTopBox)\n        {\n            if (presetNum <= PresetsList.Count)\n            {\n                Dial(PresetsList[presetNum - 1].Channel, setTopBox);\n            }\n        }\n\n        /// <summary>\n        /// Dial method\n        /// </summary>\n        public void Dial(string chanNum, ISetTopBoxNumericKeypad setTopBox)\n        {\n            _dialFunctions = new Dictionary<char, Action<bool>>(10)\n            {\n                {'1', setTopBox.Digit1},\n                {'2', setTopBox.Digit2},\n                {'3', setTopBox.Digit3},\n                {'4', setTopBox.Digit4},\n                {'5', setTopBox.Digit5},\n                {'6', setTopBox.Digit6},\n                {'7', setTopBox.Digit7},\n                {'8', setTopBox.Digit8},\n                {'9', setTopBox.Digit9},\n                {'0', setTopBox.Digit0},\n                {'-', setTopBox.Dash}\n            };\n\n            _enterFunction = setTopBox.KeypadEnter;\n\n            OnPresetRecalled(setTopBox, chanNum);\n\n            Dial(chanNum);\n        }\n\n        private void OnPresetRecalled(ISetTopBoxNumericKeypad setTopBox, string channel)\n        {\n            var handler = PresetRecalled;\n\n            if (handler == null)\n            {\n                return;\n            }\n\n            handler(setTopBox, channel);\n        }\n\n        /// <summary>\n        /// UpdatePreset method\n        /// </summary>\n        public void UpdatePreset(int index, PresetChannel preset)\n        {\n            if (index >= PresetsList.Count)\n            {\n                return;\n            }\n\n            PresetsList[index] = preset;\n\n            SavePresets();\n\n            OnPresetsSaved();\n        }\n\n        /// <summary>\n        /// UpdatePresets method\n        /// </summary>\n        public void UpdatePresets(List<PresetChannel> presets)\n        {\n            PresetsList = presets;\n\n            SavePresets();\n\n            OnPresetsSaved();\n        }\n\n        private void SavePresets()\n        {\n            try\n            {\n                _fileOps.Enter();\n                var pl = new PresetsList {Channels = PresetsList, Name = Name};\n                var json = JsonConvert.SerializeObject(pl, Formatting.Indented);\n\n                using (var file = File.Open(_filePath, FileMode.Truncate))\n                {\n                    file.Write(json, Encoding.UTF8);\n                }\n            }\n            finally\n            {\n                _fileOps.Leave();\n            }\n            \n        }\n\n        private void OnPresetsSaved()\n        {\n            var handler = PresetsSaved;\n\n            if (handler == null) return;\n\n            handler(PresetsList);\n        }\n\n        private void Pulse(Action<bool> act)\n        {\n            act(true);\n            CrestronEnvironment.Sleep(PulseTime);\n            act(false);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Presets/DevicePresetsView.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core.Presets\n{\n\t/// <summary>\n\t/// Represents a DevicePresetsView\n\t/// </summary>\n\tpublic class DevicePresetsView\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the ShowNumbers\n\t\t/// </summary>\n\t\tpublic bool ShowNumbers { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the ShowName\n\t\t/// </summary>\n\t\tpublic bool ShowName { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the ShowIcon\n\t\t/// </summary>\n\t\tpublic bool ShowIcon { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SRL\n        /// </summary>\n        public SubpageReferenceList SRL { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Model\n\t\t/// </summary>\n\t\tpublic DevicePresetsModel Model { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"tl\">trilst</param>\n\t\t/// <param name=\"model\">device presets model</param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\tpublic DevicePresetsView(BasicTriListWithSmartObject tl, DevicePresetsModel model)\n\t\t{\n\t\t\tif (model == null)\n\t\t\t{\n\t\t\t\tthrow new ArgumentNullException(\"model\", \"DevicePresetsView Cannot be instantiated with null model\");\n\t\t\t}\n\t\t\tShowIcon = true;\n\t\t\tShowName = true;\n\n\t\t\tModel = model;\n\n\t\t\tSRL = new SubpageReferenceList(tl, 10012, 3, 0, 4);\n\t\t\tModel.PresetsLoaded += new EventHandler(Model_PresetsLoaded);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Attach method\n\t\t/// </summary>\n\t\tpublic void Attach()\n\t\t{\n\t\t\tif (Model.PresetsAreLoaded)\n\t\t\t{\n\t\t\t\tuint index = 1;\n\t\t\t\tforeach (var p in Model.PresetsList)\n\t\t\t\t{\n\t\t\t\t\tSRL.AddItem(new PresetsListSubpageReferenceListItem(p, index, SRL, this));\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\t\t\tSRL.Count = (ushort)Model.PresetsList.Count;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Detach method\n\t\t/// </summary>\n\t\tpublic void Detach()\n\t\t{\n\t\t\tSRL.Clear();\n\t\t}\n\n\t\tvoid Model_PresetsLoaded(object sender, EventArgs e)\n\t\t{\n\t\t\tDetach();\n\t\t\tAttach();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Presets/Interfaces.cs",
    "content": "﻿//using System;\n//using System.Collections.Generic;\n//using System.Linq;\n//using System.Text;\n//using Crestron.SimplSharp;\n\n//namespace PepperDash.Essentials.Core\n//{\n//    public interface IPresetsFileChanged : IKeyed\n//    {\n//        public event EventHandler<PresetsFileChangeEventArgs> PresetsFileChanged;\n//    }\n\n//    public class PresetsFileChangeEventArgs : EventArgs\n//    {\n//        public string FilePath { get; private set; }\n//        public PresetsFileChangeEventArgs(string filePath)\n//        {\n//            FilePath = filePath;\n//        }\n//    }\n//}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Presets/PresetBase.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core.Presets\n{\n    /// <summary>\n    /// Represents a PresetBase\n    /// </summary>\n    public class PresetBase\n    {\n        /// <summary>\n        /// Gets or sets the ID\n        /// </summary>\n        [JsonProperty(\"id\")]\n        public int ID { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Description\n        /// </summary>\n        [JsonProperty(\"description\")]\n        public string Description { get; set; }\n\n        /// <summary>\n        /// Gets or sets Defined\n        /// </summary>\n        [JsonProperty(\"defined\")]\n        public bool Defined { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IsDefinable\n        /// </summary>\n        [JsonProperty(\"isDefinable\")]\n        public bool IsDefinable { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"id\">id of the preset</param>\n        /// <param name=\"description\">description of the preset</param>\n        /// <param name=\"def\">whether the preset is defined</param>\n        /// <param name=\"isDef\">whether the preset is definable</param>\n        public PresetBase(int id, string description, bool def, bool isDef)\n        {\n            ID = id;\n            Description = description;\n            Defined = def;\n            IsDefinable = isDef;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Presets/PresetChannel.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core.Presets\n{\n /// <summary>\n /// Represents a PresetChannel\n /// </summary>\n\tpublic class PresetChannel\n\t{\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Name\n\t\t/// </summary>\n\t\t[JsonProperty(Required = Required.Always,PropertyName = \"name\")]\n\t\tpublic string Name { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IconUrl\n\t\t/// </summary>\n\t\t[JsonProperty(Required = Required.Always, PropertyName = \"iconUrl\")]\n\t\tpublic string IconUrl { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Channel\n\t\t/// </summary>\n\t\t[JsonProperty(Required = Required.Always, PropertyName = \"channel\")]\n\t\tpublic string Channel { get; set; }\n\t}\n\n\t/// <summary>\n\t/// Represents a PresetsList\n\t/// </summary>\n\tpublic class PresetsList\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the Name\n\t\t/// </summary>\n\t\t[JsonProperty(Required=Required.Always,PropertyName = \"name\")]\n\t\tpublic string Name { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Channels\n\t\t/// </summary>\n\t\t[JsonProperty(Required = Required.Always, PropertyName = \"channels\")]\n\t\tpublic List<PresetChannel> Channels { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Presets/PresetsListSubpageReferenceListItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core.Presets\n{\n /// <summary>\n /// Represents a PresetsListSubpageReferenceListItem\n /// </summary>\n\tpublic class PresetsListSubpageReferenceListItem : SubpageReferenceListItem\n\t{\n\t\tDevicePresetsView View;\n\t\tPresetChannel Channel;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"chan\">preset channel</param>\n\t\t/// <param name=\"index\">index of the preset</param>\n\t\t/// <param name=\"owner\">owner of the subpage reference list item</param>\n\t\t/// <param name=\"view\">device presets view</param>\n\t\tpublic PresetsListSubpageReferenceListItem(PresetChannel chan, uint index, \n\t\t\tSubpageReferenceList owner, DevicePresetsView view)\n\t\t\t: base(index, owner)\n\t\t{\n\t\t\tView = view;\n\t\t\tChannel = chan;\n\t\t\towner.GetBoolFeedbackSig(index, 1).UserObject = new Action<bool>(b => { if (!b) view.Model.Dial((int)index); });\n\t\t\tRefresh();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Clear method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Clear()\n\t\t{\n\t\t\tOwner.GetBoolFeedbackSig(Index, 1).UserObject = null;\n\t\t\tOwner.StringInputSig(Index, 1).StringValue = \"\";\n\t\t\tOwner.StringInputSig(Index, 2).StringValue = \"\";\n\t\t\tOwner.StringInputSig(Index, 3).StringValue = \"\";\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Refresh method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Refresh()\n\t\t{\n\t\t\tvar name = View.ShowName ? Channel.Name : \"\";\n\t\t\tOwner.StringInputSig(Index, 1).StringValue = name;\n\t\t\tvar chan = View.ShowNumbers ? Channel.Channel : \"\";\n\t\t\tOwner.StringInputSig(Index, 2).StringValue = chan;\n\t\t\tvar url = View.Model.ImagesLocalHostPrefix + View.Model.ImagesPathPrefix + Channel.IconUrl;\n\t\t\tDebug.LogMessage(LogEventLevel.Verbose, \"icon url={0}\", url);\n\t\t\tvar icon = View.ShowIcon ? url : \"\";\n\t\t\tOwner.StringInputSig(Index, 3).StringValue = icon;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Properties/ControlSystem.cfg",
    "content": ""
  },
  {
    "path": "src/PepperDash.Essentials.Core/Queues/ComsMessage.cs",
    "content": "﻿using System;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.Queues\n{\n    /// <summary>\n    /// Represents a ComsMessage\n    /// </summary>\n    public class ComsMessage : IQueueMessage\n    {\n        private readonly byte[] _bytes;\n        private readonly IBasicCommunication _coms;\n        private readonly string _string;\n        private readonly bool _isByteMessage;\n\n        /// <summary>\n        /// Constructor for a string message\n        /// </summary>\n        /// <param name=\"coms\">IBasicCommunication to send the message</param>\n        /// <param name=\"message\">Message to send</param>\n        public ComsMessage(IBasicCommunication coms, string message)\n        {\n            Validate(coms, message);\n            _coms = coms;\n            _string = message;\n        }\n\n        /// <summary>\n        /// Constructor for a byte message\n        /// </summary>\n        /// <param name=\"coms\">IBasicCommunication to send the message</param>\n        /// <param name=\"message\">Message to send</param>\n        public ComsMessage(IBasicCommunication coms, byte[] message)\n        {\n            Validate(coms, message);\n            _coms = coms;\n            _bytes = message;\n            _isByteMessage = true;\n        }\n\n        private void Validate(IBasicCommunication coms, object message)\n        {\n            if (coms == null)\n                throw new ArgumentNullException(\"coms\");\n\n            if (message == null)\n                throw new ArgumentNullException(\"message\");\n        }\n\n        /// <summary>\n        /// Dispatch method\n        /// </summary>\n        public void Dispatch()\n        {\n            if (_isByteMessage)\n            {\n                _coms.SendBytes(_bytes);\n            }\n            else\n            {\n                _coms.SendText(_string);\n            }\n        }\n\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return _bytes != null ? _bytes.ToString() : _string;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Queues/GenericQueue.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Threading;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing Serilog.Events;\nusing Thread = Crestron.SimplSharpPro.CrestronThread.Thread;\n\nnamespace PepperDash.Essentials.Core.Queues\n{\n    /// <summary>\n    /// Threadsafe processing of queued items with pacing if required\n    /// </summary>\n    public class GenericQueue : IQueue<IQueueMessage>\n    {\n        private readonly string _key;\n\n        /// <summary>\n        /// The internal queue\n        /// </summary>\n        protected readonly ConcurrentQueue<IQueueMessage> _queue;\n\n        /// <summary>\n        /// The worker thread\n        /// </summary>\n        protected readonly Thread _worker;\n\n        /// <summary>\n        /// The wait handle for the queue\n        /// </summary>\n        protected readonly CEvent _waitHandle = new CEvent();\n\n        private bool _delayEnabled;\n        private int _delayTime;\n\n        private const Thread.eThreadPriority _defaultPriority = Thread.eThreadPriority.MediumPriority;\n\n        /// <summary>\n        /// Gets or sets the Disposed\n        /// </summary>\n        public bool Disposed { get; private set; }\n\n        /// <summary>\n        /// Returns the capacity of the CrestronQueue (fixed Size property)\n        /// </summary>\n        public int QueueCapacity\n        {\n            get\n            {\n                return int.MaxValue;\n            }\n        }\n\n        /// <summary>\n        /// Returns the number of elements currently in the CrestronQueue\n        /// </summary>    \n        public int QueueCount\n        {\n            get\n            {\n                return _queue.Count;\n            }\n        }\n\n        /// <summary>\n        /// Constructor with no thread priority\n        /// </summary>\n        /// <param name=\"key\"></param>\n        public GenericQueue(string key)\n            : this(key, _defaultPriority, 0, 0)\n        {\n        }\n\n        /// <summary>\n        /// Constructor with queue size\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"capacity\">Fixed size for the queue to hold</param>\n        public GenericQueue(string key, int capacity)\n            : this(key, _defaultPriority, capacity, 0)\n        {\n        }\n\n        /// <summary>\n        /// Constructor for generic queue with no pacing\n        /// </summary>\n        /// <param name=\"key\">Key</param>\n        /// <param name=\"pacing\">Pacing in ms between actions</param>\n        public GenericQueue(int pacing, string key)\n            : this(key, _defaultPriority, 0, pacing)\n        {\n        }\n\n        /// <summary>\n        /// Constructor with pacing and capacity\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"pacing\"></param>\n        /// <param name=\"capacity\"></param>\n        public GenericQueue(string key, int pacing, int capacity)\n            : this(key, _defaultPriority, capacity, pacing)\n        {\n        }\n\n        /// <summary>\n        /// Constructor with pacing and priority\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"pacing\"></param>\n        /// <param name=\"priority\"></param>\n        public GenericQueue(string key, int pacing, Thread.eThreadPriority priority)\n            : this(key, priority, 0, pacing)\n        {\n        }\n\n        /// <summary>\n        /// Constructor with pacing, priority and capacity\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"priority\"></param>\n        /// <param name=\"capacity\"></param>\n        public GenericQueue(string key, Thread.eThreadPriority priority, int capacity)\n            : this(key, priority, capacity, 0)\n        {\n        }\n\n        /// <summary>\n        /// Constructor with pacing, priority and capacity\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"pacing\"></param>\n        /// <param name=\"priority\"></param>\n        /// <param name=\"capacity\"></param>\n        public GenericQueue(string key, int pacing, Thread.eThreadPriority priority, int capacity)\n            : this(key, priority, capacity, pacing)\n        {           \n        }\n\n        /// <summary>\n        /// Constructor for generic queue with no pacing\n        /// </summary>\n        /// <param name=\"key\">Key</param>\n        /// <param name=\"priority\"></param>\n        /// <param name=\"capacity\"></param>\n        /// <param name=\"pacing\"></param>\n        protected GenericQueue(string key, Thread.eThreadPriority priority, int capacity, int pacing)\n        {\n            _key = key;\n            int cap = 25; // sets default\n            if (capacity > 0)\n            {\n                cap = capacity; // overrides default\n            }\n\n            _queue = new ConcurrentQueue<IQueueMessage>();\n            _worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running)\n            {\n                Priority = priority,\n                Name = _key\n            };\n\n            SetDelayValues(pacing);\n        }\n\n        private void SetDelayValues(int pacing)\n        {\n            _delayEnabled = pacing > 0;\n            _delayTime = pacing;\n\n            CrestronEnvironment.ProgramStatusEventHandler += programEvent =>\n            {\n                if (programEvent != eProgramStatusEventType.Stopping)\n                    return;\n\n                Dispose(true);\n            };\n        }\n\n        /// <summary>\n        /// Thread callback\n        /// </summary>\n        /// <param name=\"obj\">The action used to process dequeued items</param>\n        /// <returns>Null when the thread is exited</returns>\n        private object ProcessQueue(object obj)\n        {\n            while (true)\n            {\n                if (_queue.TryDequeue(out var item) && item == null)\n                    break;\n\n                if (item != null)\n                {\n                    try\n                    {\n                        //Debug.LogMessage(LogEventLevel.Verbose, this, \"Processing queue item: '{0}'\", item.ToString());\n                        item.Dispatch();\n\n                        if (_delayEnabled)\n                            Thread.Sleep(_delayTime);\n                    }\n                    catch (ThreadAbortException)\n                    {\n                        //swallowing this exception, as it should only happen on shut down\n                    }\n                    catch (Exception ex)\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, this, \"Caught an exception in the Queue: {1}:{0}\", ex.Message, ex);\n                        Debug.LogMessage(LogEventLevel.Verbose, this, \"Stack Trace: {0}\", ex.StackTrace);\n\n                        if (ex.InnerException != null)\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, this, \"---\\r\\n{0}\", ex.InnerException.Message);\n                            Debug.LogMessage(LogEventLevel.Verbose, this, \"Stack Trace: {0}\", ex.InnerException.StackTrace);\n                        }\n                    }\n                }\n                else _waitHandle.Wait();\n            }\n\n            return null;\n        }\n\n        /// <summary>\n        /// Enqueue method\n        /// </summary>\n        public void Enqueue(IQueueMessage item)\n        {\n            if (Disposed)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Queue has been disposed. Enqueuing messages not allowed while program is stopping.\");\n                return;\n            }\n\n            _queue.Enqueue(item);\n            _waitHandle.Set();\n        }\n\n        /// <summary>\n        /// Dispose method\n        /// </summary>\n        public void Dispose()\n        {\n            Dispose(true);\n            CrestronEnvironment.GC.SuppressFinalize(this);\n        }\n\n        /// <summary>\n        /// Actually does the disposing.  If you override this method, be sure to either call the base implementation \n        /// or clean up all the resources yourself.\n        /// </summary>\n        /// <param name=\"disposing\">set to true unless called from finalizer</param>\n        protected void Dispose(bool disposing)\n        {\n            if (Disposed)\n                return;\n\n            if (disposing)\n            {\n                using (_waitHandle)\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, this, \"Disposing...\");\n                    _queue.Enqueue(null);\n                    _waitHandle.Set();\n                    _worker.Join();\n                }\n            }\n\n            Disposed = true;\n        }\n\n        /// <summary>\n        /// Finalizer\n        /// </summary>\n        ~GenericQueue()\n        {\n            Dispose(true);\n        }\n\n        /// <summary>\n        /// Key\n        /// </summary>\n        public string Key\n        {\n            get { return _key; }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Queues/IQueue.cs",
    "content": "﻿using System;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.Queues\n{\n    /// <summary>\n    /// Defines the contract for IQueue\n    /// </summary>\n    public interface IQueue<T> : IKeyed, IDisposable where T : class \n    {\n        /// <summary>\n        /// Enqueues an item\n        /// </summary>\n        /// <param name=\"item\">item to be queued</param>\n        void Enqueue(T item);\n\n        /// <summary>\n        /// gets the disposed status of the queue\n        /// </summary>\n        bool Disposed { get; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Queues/IQueueMessage.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Queues\n{\n    /// <summary>\n    /// Defines the contract for IQueueMessage\n    /// </summary>\n    public interface IQueueMessage\n    {\n        /// <summary>\n        /// Dispatches the message\n        /// </summary>\n        void Dispatch();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Queues/ProcessStringMessage.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core.Queues\n{\n    /// <summary>\n    /// Represents a ProcessStringMessage\n    /// </summary>\n    public class ProcessStringMessage : IQueueMessage\n    {\n        private readonly Action<string> _action;\n        private readonly string _message;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"message\">Message to be processed</param>\n        /// <param name=\"action\">Action to invoke on the message</param>\n        public ProcessStringMessage(string message, Action<string> action)\n        {\n            _message = message;\n            _action = action;\n        }\n\n        /// <summary>\n        /// Processes the string with the given action\n        /// </summary>\n        public void Dispatch()\n        {\n            if (_action == null || String.IsNullOrEmpty(_message))\n                return;\n\n            _action(_message);\n        }\n\n        /// <summary>\n        /// To string\n        /// </summary>\n        /// <returns>The current message</returns>\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return _message ?? String.Empty;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Queues/StringResponseProcessor.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.Queues\n{\n    /// <summary>\n    /// Processes string responses from a communication port or gather using a queue to ensure thread safety\n    /// </summary>\n    public sealed class StringResponseProcessor : IKeyed, IDisposable\n    {\n        private readonly Action<string> _processStringAction; \n        private readonly IQueue<IQueueMessage> _queue;\n        private readonly IBasicCommunication _coms;\n        private readonly CommunicationGather _gather;\n\n        private StringResponseProcessor(string key, Action<string> processStringAction)\n        {\n            _processStringAction = processStringAction;\n            _queue = new GenericQueue(key);\n\n            CrestronEnvironment.ProgramStatusEventHandler += programEvent =>\n            {\n                if (programEvent != eProgramStatusEventType.Stopping)\n                    return;\n\n                Dispose();\n            };\n        }\n\n        /// <summary>\n        /// Constructor that builds an instance and subscribes to coms TextReceived for processing\n        /// </summary>\n        /// <param name=\"coms\">Com port to process strings from</param>\n        /// <param name=\"processStringAction\">Action to process the incoming strings</param>\n        public StringResponseProcessor(IBasicCommunication coms, Action<string> processStringAction)\n            : this(coms.Key, processStringAction)\n        {\n            _coms = coms;\n            coms.TextReceived += OnResponseReceived;\n        }\n\n        /// <summary>\n        /// Constructor that builds an instance and subscribes to gather Line Received for processing\n        /// </summary>\n        /// <param name=\"gather\">Gather to process strings from</param>\n        /// <param name=\"processStringAction\">Action to process the incoming strings</param>\n        public StringResponseProcessor(CommunicationGather gather, Action<string> processStringAction)\n            : this(gather.Port.Key, processStringAction)\n        {\n            _gather = gather;\n            gather.LineReceived += OnResponseReceived;\n        }\n\n        private void OnResponseReceived(object sender, GenericCommMethodReceiveTextArgs args)\n        {\n            _queue.Enqueue(new ProcessStringMessage(args.Text, _processStringAction));\n        }\n\n        /// <summary>\n        /// Key\n        /// </summary>\n        public string Key\n        {\n            get { return _queue.Key; }\n        }\n\n        /// <summary>\n        /// Disposes the instance and cleans up resources.\n        /// </summary>\n        public void Dispose()\n        {\n            Dispose(true);\n            CrestronEnvironment.GC.SuppressFinalize(this);\n        }\n\n        private void Dispose(bool disposing)\n        {\n            if (Disposed)\n                return;\n\n            if (disposing)\n            {\n                if (_coms != null)\n                    _coms.TextReceived -= OnResponseReceived;\n\n                if (_gather != null)\n                {\n                    _gather.LineReceived -= OnResponseReceived;\n                    _gather.Stop();\n                }\n\n                _queue.Dispose();\n            }\n\n            Disposed = true;\n        }\n\n        /// <summary>\n        /// Gets or sets the Disposed\n        /// </summary>\n        public bool Disposed { get; private set; }\n\n        /// <summary>\n        /// Finalizer\n        /// </summary>\n        ~StringResponseProcessor()\n        {\n            Dispose(false);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Ramps and Increments/ActionIncrementer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// An incrementer that can use the values of some other object/primitive value to do its thing.\n    /// It uses an Action to set the value and a Func to get the value from whatever this is\n    /// attached to.\n    /// </summary>\n    public class ActionIncrementer\n    {\n        /// <summary>\n        /// The amount to change the value by each increment\n        /// </summary>\n        public int ChangeAmount { get; set; }\n\n        /// <summary>\n        /// The maximum value for the incrementer\n        /// </summary>\n        public int MaxValue { get; set; }\n\n        /// <summary>\n        /// The minimum value for the incrementer\n        /// </summary>\n        public int MinValue { get; set; }\n\n        /// <summary>\n        /// The delay before repeating starts\n        /// </summary>\n        public uint RepeatDelay { get; set; }\n\n        /// <summary>\n        /// The time between repeats\n        /// </summary>\n        public uint RepeatTime { get; set; }\n\n        Action<int> SetAction;\n        Func<int> GetFunc;\n        CTimer Timer;\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"changeAmount\"></param>\n        /// <param name=\"minValue\"></param>\n        /// <param name=\"maxValue\"></param>\n        /// <param name=\"repeatDelay\"></param>\n        /// <param name=\"repeatTime\"></param>\n        /// <param name=\"setAction\">Action that will be called when this needs to set the destination value</param>\n        /// <param name=\"getFunc\">Func that is called to get the current value</param>\n        public ActionIncrementer(int changeAmount, int minValue, int maxValue, uint repeatDelay, uint repeatTime, Action<int> setAction, Func<int> getFunc)\n        {\n            SetAction = setAction;\n            GetFunc = getFunc;\n            ChangeAmount = changeAmount;\n            MaxValue = maxValue;\n            MinValue = minValue;\n            RepeatDelay = repeatDelay;\n            RepeatTime = repeatTime;\n        }\n\n        /// <summary>\n        /// StartUp method\n        /// </summary>\n        public void StartUp()\n        {\n            if (Timer != null) return;\n            Go(ChangeAmount);\n        }\n\n        /// <summary>\n        /// Starts decrementing cycle\n        /// </summary>\n        public void StartDown()\n        {\n            if (Timer != null) return;\n            Go(-ChangeAmount);\n        }\n\n        /// <summary>\n        /// Stops the repeat\n        /// </summary>\n        public void Stop()\n        {\n            if (Timer != null)\n                Timer.Stop();\n            Timer = null;\n        }\n\n        /// <summary>\n        /// Helper that does the work of setting new level, and starting repeat loop, checking against bounds first.\n        /// </summary>\n        /// <param name=\"change\"></param>\n        void Go(int change)\n        {\n            int currentLevel = GetFunc();\n            // Fire once then pause\n            int newLevel = currentLevel + change;\n            bool atLimit = CheckLevel(newLevel, out newLevel);\n            SetAction(newLevel);\n\n            if (atLimit) // Don't go past end\n                Stop();\n            else if (Timer == null) // Only enter the timer if it's not already running\n                Timer = new CTimer(o => { Go(change); }, null, RepeatDelay, RepeatTime);\n        }\n\n        /// <summary>\n        /// Helper to check a new level against min/max. Returns revised level if new level\n        /// will go out of bounds\n        /// </summary>\n        /// <param name=\"levelIn\">The level to check against bounds</param>\n        /// <param name=\"levelOut\">Revised level if bounds are exceeded. Min or max</param>\n        /// <returns>true if new level is at or past bounds</returns>\n        bool CheckLevel(int levelIn, out int levelOut)\n        {\n            bool isAtLimit = false;\n            if (levelIn > MaxValue)\n            {\n                levelOut = MaxValue;\n                isAtLimit = true;\n            }\n            else if (levelIn < MinValue)\n            {\n                levelOut = MinValue;\n                isAtLimit = true;\n            }\n            else\n                levelOut = levelIn;\n            return isAtLimit;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Ramps and Increments/NumericalHelpers.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a NumericalHelpers\n    /// </summary>\n    public class NumericalHelpers \n    {  \n        /// <summary>\n        /// Scales a value\n        /// </summary>\n        /// <param name=\"input\"></param>\n        /// <param name=\"inMin\"></param>\n        /// <param name=\"inMax\"></param>\n        /// <param name=\"outMin\"></param>\n        /// <param name=\"outMax\"></param>\n        /// <returns></returns>\n        public static double Scale(double input, double inMin, double inMax, double outMin, double outMax)\n        {\n            //Debug.LogMessage(LogEventLevel.Verbose, this, \"Scaling (double) input '{0}' with min '{1}'/max '{2}' to output range min '{3}'/max '{4}'\", input, inMin, inMax, outMin, outMax);\n\n            double inputRange = inMax - inMin;\n\n            if (inputRange <= 0)\n            {\n                throw new ArithmeticException(string.Format(\"Invalid Input Range '{0}' for Scaling.  Min '{1}' Max '{2}'.\", inputRange, inMin, inMax));\n            }\n\n            double outputRange = outMax - outMin;\n\n            var output = (((input - inMin) * outputRange) / inputRange) + outMin;\n\n           // Debug.LogMessage(LogEventLevel.Verbose, this, \"Scaled output '{0}'\", output);\n\n            return output;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Ramps and Increments/UshortSigIncrementer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Attaches to UShortInputSig and does incremental ramping of the signal \n    /// </summary>\n    public class UshortSigIncrementer\n    {\n        UShortInputSig TheSig;\n\n        /// <summary>\n        /// The amount to change the value by each increment\n        /// </summary>\n        public ushort ChangeAmount { get; set; }\n\n        /// <summary>\n        /// The maximum value for the incrementer\n        /// </summary>\n        public int MaxValue { get; set; }\n\n        /// <summary>\n        /// The minimum value for the incrementer\n        /// </summary>\n        public int MinValue { get; set; }\n\n        /// <summary>\n        /// The delay before repeating starts\n        /// </summary>\n        public uint RepeatDelay { get; set; }\n\n        /// <summary>\n        /// The time between repeats\n        /// </summary>\n        public uint RepeatTime { get; set; }\n\n        bool SignedMode;\n        CTimer Timer;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"sig\">the signal toi be incremented</param>\n        /// <param name=\"changeAmount\">the amount to increment by</param>\n        /// <param name=\"minValue\">the minimum value of the signal</param>\n        /// <param name=\"maxValue\">the maximum value of the signal</param>\n        /// <param name=\"repeatDelay\">the delay before repeating starts</param>\n        /// <param name=\"repeatTime\">the time between repeats</param>\n        public UshortSigIncrementer(UShortInputSig sig, ushort changeAmount, int minValue, int maxValue, uint repeatDelay, uint repeatTime)\n        {\n            TheSig = sig;\n            ChangeAmount = changeAmount;\n            MaxValue = maxValue;\n            MinValue = minValue;\n            if (MinValue < 0 || MaxValue < 0) SignedMode = true;\n            RepeatDelay = repeatDelay;\n            RepeatTime = repeatTime;\n            if (SignedMode && (MinValue < -32768 || MaxValue > 32767))\n                Debug.LogMessage(LogEventLevel.Debug, \"UshortSigIncrementer has signed values that exceed range of -32768, 32767\");\n        }\n\n        /// <summary>\n        /// StartUp method\n        /// </summary>\n        public void StartUp()\n        {\n            if (Timer != null) return;\n            Go(ChangeAmount);\n        }\n\n        /// <summary>\n        /// StartDown method\n        /// </summary>\n        public void StartDown()\n        {\n            if (Timer != null) return;\n            Go(-ChangeAmount);\n        }\n\n        void Go(int change)\n        {\n            int level;\n            if (SignedMode) level = TheSig.ShortValue;\n            else level = TheSig.UShortValue;\n\n            // Fire once then pause\n            int newLevel = level + change;\n            bool atLimit = CheckLevel(newLevel, out newLevel);\n            SetValue((ushort)newLevel);\n\n\n            if (atLimit) // Don't go past end\n                Stop();\n            else if (Timer == null) // Only enter the timer if it's not already running\n                Timer = new CTimer(o => { Go(change); }, null, RepeatDelay, RepeatTime);\n        }\n\n        bool CheckLevel(int levelIn, out int levelOut)\n        {\n            bool IsAtLimit = false;\n            if (levelIn > MaxValue)\n            {\n                levelOut = MaxValue;\n                IsAtLimit = true;\n            }\n            else if (levelIn < MinValue)\n            {\n                levelOut = MinValue;\n                IsAtLimit = true;\n            }\n            else\n                levelOut = levelIn;\n            return IsAtLimit;\n        }\n\n        /// <summary>\n        /// Stop method\n        /// </summary>\n        public void Stop()\n        {\n            if (Timer != null)\n                Timer.Stop();\n            Timer = null;\n        }\n\n        void SetValue(ushort value)\n        {\n            //CrestronConsole.PrintLine(\"Increment level:{0} / {1}\", value, (short)value);\n            TheSig.UShortValue = value;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.Scheduler;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Devices;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// A device that when linked to a room can power the room on when enabled during scheduled hours.\n    /// </summary>\n    public class RoomOnToDefaultSourceWhenOccupied : ReconfigurableDevice\n    {\n        RoomOnToDefaultSourceWhenOccupiedConfig PropertiesConfig;\n\n        /// <summary>\n        /// Gets or sets the FeatureEnabled\n        /// </summary>\n        public bool FeatureEnabled { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the FeatureEnabledTime\n        /// </summary>\n        public DateTime FeatureEnabledTime { get; private set; }\n\n        ScheduledEvent FeatureEnableEvent;\n\n        const string FeatureEnableEventName = \"EnableRoomOnToDefaultSourceWhenOccupied\";\n\n        /// <summary>\n        /// Gets or sets the FeatureDisabledTime\n        /// </summary>\n        public DateTime FeatureDisabledTime { get; private set; }\n\n        ScheduledEvent FeatureDisableEvent;\n\n        const string FeatureDisableEventName = \"DisableRoomOnToDefaultSourceWhenOccupied\";\n\n        ScheduledEventGroup FeatureEventGroup;\n\n        /// <summary>\n        /// Gets or sets the Room\n        /// </summary>\n        public IRoomOccupancy Room { get; private set; }\n\n        private Fusion.IEssentialsRoomFusionController FusionRoom;\n\n        /// <summary>\n        /// Constructor for RoomOnToDefaultSourceWhenOccupied\n        /// </summary>\n        /// <param name=\"config\">config of the device</param>\n        public RoomOnToDefaultSourceWhenOccupied(DeviceConfig config) :\n            base (config)\n        {\n            PropertiesConfig = JsonConvert.DeserializeObject<RoomOnToDefaultSourceWhenOccupiedConfig>(config.Properties.ToString());\n\n            FeatureEventGroup = new ScheduledEventGroup(this.Key);\n\n            FeatureEventGroup.RetrieveAllEvents();\n\n            // Add to the global class for tracking\n            Scheduler.AddEventGroup(FeatureEventGroup);\n\n            AddPostActivationAction(() =>\n            {\n                // Subscribe to room event to know when RoomOccupancy is set and ready to be subscribed to\n                if (Room != null)\n                    Room.RoomOccupancyIsSet += new EventHandler<EventArgs>(RoomOccupancyIsSet);\n\n                else\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Room has no RoomOccupancy object set\");\n\n                var fusionRoomKey = PropertiesConfig.RoomKey + \"-fusion\";\n\n                FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Core.Fusion.IEssentialsRoomFusionController;\n\n                if (FusionRoom == null)\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to get Fusion Room from Device Manager with key: {0}\", fusionRoomKey);\n            });\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            SetUpDevice();\n\n            return base.CustomActivate();\n        }\n\n        /// <summary>\n        /// Sets up device based on config values\n        /// </summary>\n        void SetUpDevice()\n        {\n            Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as IRoomOccupancy;\n\n            if (Room != null)\n            {\n                try\n                {\n                    FeatureEnabledTime = DateTime.Parse(PropertiesConfig.OccupancyStartTime);\n\n                    if (FeatureEnabledTime != null)\n                    {\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Enabled Time: {0}\", FeatureEnabledTime.ToString());\n                    }\n                    else\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to parse {0} to DateTime\", PropertiesConfig.OccupancyStartTime);\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to parse OccupancyStartTime property: {0} \\n Error: {1}\", PropertiesConfig.OccupancyStartTime, e);\n                }\n\n                try\n                {\n                    FeatureDisabledTime = DateTime.Parse(PropertiesConfig.OccupancyEndTime);\n\n                    if (FeatureDisabledTime != null)\n                    {\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Disabled Time: {0}\", FeatureDisabledTime.ToString());\n                    }\n                    else\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to parse {0} to DateTime\", PropertiesConfig.OccupancyEndTime);\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to parse a DateTime config value \\n Error: {1}\", e);\n                }\n\n                if (!PropertiesConfig.EnableRoomOnWhenOccupied)\n                    FeatureEventGroup.ClearAllEvents();\n                else\n                {\n                    AddEnableEventToGroup();\n\n                    AddDisableEventToGroup();\n\n                    FeatureEventGroup.UserGroupCallBack += new ScheduledEventGroup.UserEventGroupCallBack(FeatureEventGroup_UserGroupCallBack);\n\n                    FeatureEventGroup.EnableAllEvents();\n                }\n\n                FeatureEnabled = CheckIfFeatureShouldBeEnabled();\n            }\n            else\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to get room from Device Manager with key: {0}\", PropertiesConfig.RoomKey);\n        }\n\n        /// <summary>\n        /// CustomSetConfig method\n        /// </summary>\n        /// <param name=\"config\">config of the device</param>\n        protected override void CustomSetConfig(DeviceConfig config)\n        {\n            var newPropertiesConfig = JsonConvert.DeserializeObject<RoomOnToDefaultSourceWhenOccupiedConfig>(config.Properties.ToString());\n\n            if(newPropertiesConfig != null)\n                PropertiesConfig = newPropertiesConfig;\n\n            ConfigWriter.UpdateDeviceConfig(config);\n\n            SetUpDevice();\n        }\n\n        /// <summary>\n        /// Subscribe to feedback from RoomIsOccupiedFeedback on Room\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        void RoomOccupancyIsSet(object sender, EventArgs e)\n        {\n            if (Room.RoomOccupancy != null)\n            {\n                Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;\n                Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += new EventHandler<FeedbackEventArgs>(RoomIsOccupiedFeedback_OutputChange);\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Subscribed to RoomOccupancy status from: '{0}'\", Room.Key);\n            }\n        }\n\n        void FeatureEventGroup_UserGroupCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"{0}:{1} @ {2}\", SchEvent.Name, type, DateTime.Now);\n\n            if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)\n            {\n                SchEvent.Acknowledge();\n\n                if (SchEvent.Name == FeatureEnableEventName)\n                {\n                    \n                    if (PropertiesConfig.EnableRoomOnWhenOccupied)\n                        FeatureEnabled = true;\n\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"*****Feature Enabled by event.*****\");\n                }\n                else if (SchEvent.Name == FeatureDisableEventName)\n                {\n                    FeatureEnabled = false;\n\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"*****Feature Disabled by event.*****\");\n                }\n            }\n        }\n\n        /// <summary>\n        /// Checks if the feature should be currently enabled.  Used on startup if processor starts after start time but before end time\n        /// </summary>\n        /// <returns></returns>\n        bool CheckIfFeatureShouldBeEnabled()\n        {\n            bool enabled = false;\n\n            if(PropertiesConfig.EnableRoomOnWhenOccupied)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Current Time: {0} \\n FeatureEnabledTime: {1} \\n FeatureDisabledTime: {2}\", DateTime.Now, FeatureEnabledTime, FeatureDisabledTime);\n\n                if (DateTime.Now.TimeOfDay.CompareTo(FeatureEnabledTime.TimeOfDay) >= 0 && FeatureDisabledTime.TimeOfDay.CompareTo(DateTime.Now.TimeOfDay) > 0)\n                {\n                    if (SchedulerUtilities.CheckIfDayOfWeekMatchesRecurrenceDays(DateTime.Now, CalculateDaysOfWeekRecurrence()))\n                    {\n                        enabled = true;\n                    }\n                }\n            }\n\n            if(enabled)\n                Debug.LogMessage(LogEventLevel.Debug, this, \"*****Feature Enabled*****\");\n            else\n                Debug.LogMessage(LogEventLevel.Debug, this, \"*****Feature Disabled*****\");\n\n            return enabled;\n        }\n\n        /// <summary>\n        /// Respond to Occupancy status event\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"RoomIsOccupiedFeeback.OutputChange event fired. e.BoolValue: {0}\", e.BoolValue);\n            if(e.BoolValue)\n            {\n                // Occupancy detected\n\n                if (FeatureEnabled)\n                {\n                    var essentialsRoom = Room as IEssentialsRoom;\n\n                    if (essentialsRoom != null) {\n                        if (!essentialsRoom.OnFeedback.BoolValue)\n                        {\n                            Debug.LogMessage(LogEventLevel.Debug, this, \"Powering Room on to default source\");\n\n                            var defaultRouteRoom = Room as IRunDefaultPresentRoute;\n\n                            if (defaultRouteRoom != null)\n                            {\n                                defaultRouteRoom.RunDefaultPresentRoute();\n                            }\n                        }\n                    }\n                    // Check room power state first\n                    \n                }\n            }\n        }\n\n        void CreateEvent(ScheduledEvent schEvent, string name)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Adding Event: '{0}'\", name);\n            // Create the event\n            if (schEvent == null)\n                schEvent = new ScheduledEvent(name, FeatureEventGroup);\n\n            // Set up its initial properties\n            \n            schEvent.Acknowledgeable = false;\n\n            if(!schEvent.Persistent)\n                schEvent.Persistent = true;\n\n            schEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday);\n\n            // Set config driven properties\n\n            if (schEvent.Name == FeatureEnableEventName)\n            {\n                schEvent.Description = \"Enables the RoomOnToDefaultSourceWhenOccupiedFeature\";\n\n                var eventRecurrennce = CalculateDaysOfWeekRecurrence();\n\n                var eventTime = new DateTime();\n\n                // Check to make sure the date for this event is in the future\n                if (DateTime.Now.CompareTo(FeatureEnabledTime) > 0)\n                    eventTime = FeatureEnabledTime.AddDays(1);\n                else\n                    eventTime = FeatureEnabledTime;\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"eventTime (before recurrence check): {0}\", eventTime);\n\n                // Check day of week against recurrence days and move date ahead as necessary to avoid throwing an exception by trying to set the event\n                // start date on a day of the week that doesn't match teh recurrence values\n                while(!SchedulerUtilities.CheckIfDayOfWeekMatchesRecurrenceDays(eventTime, eventRecurrennce))\n                {\n                    eventTime = eventTime.AddDays(1);\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"eventTime does not fall on a recurrence weekday.  eventTime: {0}\", eventTime);\n                }\n\n                schEvent.DateAndTime.SetAbsoluteEventTime(eventTime);\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Event '{0}' Absolute time set to {1}\", schEvent.Name, schEvent.DateAndTime.ToString());\n\n                //CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureEnabledTime, FeatureDisabledTime);\n\n                schEvent.Recurrence.Weekly(eventRecurrennce);\n\n            }\n            else if (schEvent.Name == FeatureDisableEventName)\n            {\n                schEvent.Description = \"Disables the RoomOnToDefaultSourceWhenOccupiedFeature\";\n\n                // Check to make sure the date for this event is in the future\n                if (DateTime.Now.CompareTo(FeatureDisabledTime) > 0)\n                    schEvent.DateAndTime.SetAbsoluteEventTime(FeatureDisabledTime.AddDays(1));\n                else\n                    schEvent.DateAndTime.SetAbsoluteEventTime(FeatureDisabledTime);\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Event '{0}' Absolute time set to {1}\", schEvent.Name, schEvent.DateAndTime.ToString());\n\n                CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureDisabledTime, FeatureEnabledTime);\n\n                schEvent.Recurrence.Daily();\n            }\n        }\n\n        void CalculateAndSetAcknowledgeExpirationTimeout(ScheduledEvent schEvent, DateTime time1, DateTime time2)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"time1.Hour = {0}\", time1.Hour);\n            Debug.LogMessage(LogEventLevel.Debug, this, \"time2.Hour = {0}\", time2.Hour);\n            Debug.LogMessage(LogEventLevel.Debug, this, \"time1.Minute = {0}\", time1.Minute);\n            Debug.LogMessage(LogEventLevel.Debug, this, \"time2.Minute = {0}\", time2.Minute);\n\n            // Calculate the Acknowledge Expiration timer to be the time between the enable and dispable events, less one minute\n            var ackHours = time2.Hour - time1.Hour;\n            if(ackHours < 0)\n                ackHours = ackHours + 24;\n            var ackMinutes = time2.Minute - time1.Minute;\n\n            Debug.LogMessage(LogEventLevel.Debug, this, \"ackHours = {0}, ackMinutes = {1}\", ackHours, ackMinutes);\n\n            var ackTotalMinutes = ((ackHours * 60) + ackMinutes) - 1;\n\n            var ackExpHour = ackTotalMinutes / 60;\n            var ackExpMinutes = ackTotalMinutes % 60;\n\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Acknowledge Expiration Timeout: {0} hours, {1} minutes\", ackExpHour, ackExpMinutes);\n\n            schEvent.AcknowledgeExpirationTimeout.Hour = (ushort)(ackHours);\n            schEvent.AcknowledgeExpirationTimeout.Minute = (ushort)(ackExpMinutes);\n        }\n\n        /// <summary>\n        /// Checks existing event to see if it matches the execution time\n        /// </summary>\n        /// <param name=\"existingEvent\">event we are checking</param>\n        /// <param name=\"newTime\">time we are checking against</param>\n        /// <returns></returns>\n        bool CheckExistingEventTimeForMatch(ScheduledEvent existingEvent, DateTime newTime)\n        {\n            bool isMatch = true;\n\n            // Check to see if hour and minute match\n            if (existingEvent.DateAndTime.Hour != newTime.Hour || existingEvent.DateAndTime.Minute != newTime.Minute)\n                return false;\n\n\n            return isMatch;\n        }\n\n        /// <summary>\n        /// Checks existing event to see if it matches the recurrence days\n        /// </summary>\n        /// <param name=\"existingEvent\"></param>\n        /// <param name=\"eWeekdays\"></param>\n        /// <returns></returns>\n        bool CheckExistingEventRecurrenceForMatch(ScheduledEvent existingEvent, ScheduledEventCommon.eWeekDays eWeekdays)\n        {\n            bool isMatch = true;\n\n            // Check to see if recurrence matches\n            if (eWeekdays != existingEvent.Recurrence.RecurrenceDays)\n                return false;\n\n            return isMatch;\n        }\n\n        /// <summary>\n        /// Adds the Enable event to the local event group and sets its properties based on config\n        /// </summary>\n        void AddEnableEventToGroup()\n        {\n            if (!FeatureEventGroup.ScheduledEvents.ContainsKey(FeatureEnableEventName))\n            {\n                CreateEvent(FeatureEnableEvent, FeatureEnableEventName);\n            }\n            else\n            {\n                // Check if existing event has same time and recurrence as config values\n\n                FeatureEnableEvent = FeatureEventGroup.ScheduledEvents[FeatureEnableEventName];\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Enable event already found in group\");\n\n                // Check config times and days against DateAndTime of existing event.  If different, delete existing event and create new event\n                if(!CheckExistingEventTimeForMatch(FeatureEnableEvent, FeatureEnabledTime) || !CheckExistingEventRecurrenceForMatch(FeatureEnableEvent, CalculateDaysOfWeekRecurrence()))\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Existing event does not match new config properties. Deleting exisiting event: '{0}'\", FeatureEnableEvent.Name);\n                    FeatureEventGroup.DeleteEvent(FeatureEnableEvent);\n\n                    FeatureEnableEvent = null;\n\n                    CreateEvent(FeatureEnableEvent, FeatureEnableEventName);\n                }\n            }\n\n        }\n\n        /// <summary>\n        /// Adds the Enable event to the local event group and sets its properties based on config\n        /// </summary>\n        void AddDisableEventToGroup()\n        {\n            if (!FeatureEventGroup.ScheduledEvents.ContainsKey(FeatureDisableEventName))\n            {\n                CreateEvent(FeatureDisableEvent, FeatureDisableEventName);\n            }\n            else\n            {\n                FeatureDisableEvent = FeatureEventGroup.ScheduledEvents[FeatureDisableEventName];\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Disable event already found in group\");\n\n                // Check config times against DateAndTime of existing event.  If different, delete existing event and create new event\n                if(!CheckExistingEventTimeForMatch(FeatureDisableEvent, FeatureDisabledTime))\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Existing event does not match new config properties. Deleting exisiting event: '{0}'\", FeatureDisableEvent.Name);\n\n                    FeatureEventGroup.DeleteEvent(FeatureDisableEvent);\n\n                    FeatureDisableEvent = null;\n\n                    CreateEvent(FeatureDisableEvent, FeatureDisableEventName);\n                }\n            }\n        }\n\n\n        /// <summary>\n        /// Calculates the correct bitfield enum value for the event recurrence based on the config values\n        /// </summary>\n        /// <returns></returns>\n        ScheduledEventCommon.eWeekDays CalculateDaysOfWeekRecurrence()\n        {\n            ScheduledEventCommon.eWeekDays value = new ScheduledEventCommon.eWeekDays();\n\n            if (PropertiesConfig.EnableSunday)\n                value = value | ScheduledEventCommon.eWeekDays.Sunday;\n            if (PropertiesConfig.EnableMonday)\n                value = value | ScheduledEventCommon.eWeekDays.Monday;\n            if (PropertiesConfig.EnableTuesday)\n                value = value | ScheduledEventCommon.eWeekDays.Tuesday;\n            if (PropertiesConfig.EnableWednesday)\n                value = value | ScheduledEventCommon.eWeekDays.Wednesday;\n            if (PropertiesConfig.EnableThursday)\n                value = value | ScheduledEventCommon.eWeekDays.Thursday;\n            if (PropertiesConfig.EnableFriday)\n                value = value | ScheduledEventCommon.eWeekDays.Friday;\n            if (PropertiesConfig.EnableSaturday)\n                value = value | ScheduledEventCommon.eWeekDays.Saturday;\n\n            return value;\n        }\n\n        /// <summary>\n        /// Callback for event that enables feature.  Enables feature if config property is true\n        /// </summary>\n        /// <param name=\"SchEvent\"></param>\n        /// <param name=\"type\"></param>\n        void FeatureEnableEvent_UserCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)\n        {\n            if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)\n            {\n                if(PropertiesConfig.EnableRoomOnWhenOccupied)\n                    FeatureEnabled = true;\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"RoomOnToDefaultSourceWhenOccupied Feature Enabled.\");\n            }\n        }\n\n        /// <summary>\n        /// Callback for event that enables feature.  Disables feature\n        /// </summary>\n        /// <param name=\"SchEvent\"></param>\n        /// <param name=\"type\"></param>\n        void FeatureDisableEvent_UserCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)\n        {\n            if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)\n            {\n                FeatureEnabled = false;\n\n                Debug.LogMessage(LogEventLevel.Debug, this, \"RoomOnToDefaultSourceWhenOccupied Feature Disabled.\");\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a RoomOnToDefaultSourceWhenOccupiedConfig\n    /// </summary>\n    public class RoomOnToDefaultSourceWhenOccupiedConfig\n    {\n        /// <summary>\n        /// Gets or sets the RoomKey\n        /// </summary>\n        [JsonProperty(\"roomKey\")]\n        public string RoomKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableRoomOnWhenOccupied\n        /// </summary>\n        [JsonProperty(\"enableRoomOnWhenOccupied\")]\n        public bool EnableRoomOnWhenOccupied { get; set; }\n\n        /// <summary>\n        /// Gets or sets the OccupancyStartTime\n        /// </summary>\n        [JsonProperty(\"occupancyStartTime\")]\n        public string OccupancyStartTime { get; set; }\n\n        /// <summary>\n        /// Gets or sets the OccupancyEndTime\n        /// </summary>\n        [JsonProperty(\"occupancyEndTime\")]\n        public string OccupancyEndTime { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableSunday\n        /// </summary>\n        [JsonProperty(\"enableSunday\")]\n        public bool EnableSunday { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableMonday\n        /// </summary>\n        [JsonProperty(\"enableMonday\")]\n        public bool EnableMonday { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableTuesday\n        /// </summary>\n        [JsonProperty(\"enableWednesday\")]\n        public bool EnableTuesday { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableWednesday\n        /// </summary>\n        [JsonProperty(\"enableWednesday\")]\n        public bool EnableWednesday { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableThursday\n        /// </summary>\n        [JsonProperty(\"enableThursday\")]\n        public bool EnableThursday { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableFriday\n        /// </summary>\n        [JsonProperty(\"enableFriday\")]\n        public bool EnableFriday { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableSaturday\n        /// </summary>\n        [JsonProperty(\"enableSaturday\")]\n        public bool EnableSaturday { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a RoomOnToDefaultSourceWhenOccupiedFactory\n    /// </summary>\n    public class RoomOnToDefaultSourceWhenOccupiedFactory : EssentialsDeviceFactory<RoomOnToDefaultSourceWhenOccupied>\n    {\n        /// <summary>\n        /// Constructor for RoomOnToDefaultSourceWhenOccupiedFactory\n        /// </summary>\n        public RoomOnToDefaultSourceWhenOccupiedFactory()\n        {\n            TypeNames = new List<string>() { \"roomonwhenoccupancydetectedfeature\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new RoomOnToDefaultSourceWhenOccupied Device\");\n            return new RoomOnToDefaultSourceWhenOccupied(dc);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing Serilog.Events;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a device that manages room combinations by controlling partitions and scenarios.\n    /// </summary>\n    /// <remarks>The <see cref=\"EssentialsRoomCombiner\"/> allows for dynamic configuration of room\n    /// combinations  based on partition states and predefined scenarios. It supports both automatic and manual modes \n    /// for managing room combinations. In automatic mode, the device determines the current room  combination scenario\n    /// based on partition sensor states. In manual mode, scenarios can be set  explicitly by the user.</remarks>\n    public class EssentialsRoomCombiner : EssentialsDevice, IEssentialsRoomCombiner\n    {\n        private EssentialsRoomCombinerPropertiesConfig _propertiesConfig;\n\n        private IRoomCombinationScenario _currentScenario;\n\n        private List<IEssentialsRoom> _rooms;\n\n        /// <summary>\n        /// Gets a list of rooms represented as key-name pairs.\n        /// </summary>\n        public List<IKeyName> Rooms\n        {\n            get\n            {\n                return _rooms.Cast<IKeyName>().ToList();\n            }\n        }\n\n        private bool _isInAutoMode;\n\n        /// <summary>\n        /// Gets or sets a value indicating whether the system is operating in automatic mode.\n        /// </summary>\n        /// <remarks>Changing this property triggers an update event via\n        /// <c>IsInAutoModeFeedback.FireUpdate()</c>. Ensure that any event listeners are properly configured to handle\n        /// this update.</remarks>\n        public bool IsInAutoMode\n        {\n            get\n            {\n                return _isInAutoMode;\n            }\n            set\n            {\n                if (value == _isInAutoMode)\n                {\n                    return;\n                }\n\n                _isInAutoMode = value;\n                IsInAutoModeFeedback.FireUpdate();\n            }\n        }\n\n        /// <summary>\n        /// Gets a value indicating whether automatic mode is disabled.\n        /// </summary>\n        public bool DisableAutoMode\n        {\n            get\n            {\n                return _propertiesConfig.DisableAutoMode;\n            }\n        }\n\n        private CTimer _scenarioChangeDebounceTimer;\n\n        private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s\n\n        private Mutex _scenarioChange = new Mutex();\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"EssentialsRoomCombiner\"/> class, which manages room combination\n        /// scenarios and partition states.\n        /// </summary>\n        /// <remarks>The <see cref=\"EssentialsRoomCombiner\"/> class is designed to handle dynamic room\n        /// combination scenarios based on partition states. It supports both automatic and manual modes for managing\n        /// room combinations. By default, the instance starts in automatic mode unless the <paramref name=\"props\"/>\n        /// specifies otherwise.  After activation, the room combiner initializes partition state providers and sets up\n        /// the initial room configuration. Additionally, it subscribes to the <see\n        /// cref=\"DeviceManager.AllDevicesInitialized\"/> event to ensure proper initialization of dependent devices\n        /// before determining or setting the room combination scenario.</remarks>\n        /// <param name=\"key\">The unique identifier for the room combiner instance.</param>\n        /// <param name=\"props\">The configuration properties for the room combiner, including default settings and debounce times.</param>\n        public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props)\n            : base(key)\n        {\n            _propertiesConfig = props;\n\n            Partitions = new List<IPartitionController>();\n            RoomCombinationScenarios = new List<IRoomCombinationScenario>();\n\n            if (_propertiesConfig.ScenarioChangeDebounceTimeSeconds > 0)\n            {\n                _scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;\n            }\n\n            IsInAutoModeFeedback = new BoolFeedback(() => _isInAutoMode);\n\n            // default to auto mode\n            IsInAutoMode = true;\n\n            if (_propertiesConfig.defaultToManualMode)\n            {\n                IsInAutoMode = false;\n            }\n\n            IsInAutoModeFeedback.FireUpdate();\n\n            CreateScenarios();\n\n            AddPostActivationAction(() =>\n            {\n                SetupPartitionStateProviders();\n\n                SetRooms();\n            });\n\n\n            // Subscribe to the AllDevicesInitialized event\n            // We need to wait until all devices are initialized in case\n            // any actions are dependent on 3rd party devices already being\n            // connected and initialized\n            DeviceManager.AllDevicesInitialized += (o, a) =>\n            {\n                if (IsInAutoMode)\n                {\n                    DetermineRoomCombinationScenario();\n                }\n                else\n                {\n                    SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey);\n                }\n            };\n        }\n\n        private void CreateScenarios()\n        {\n            foreach (var scenarioConfig in _propertiesConfig.Scenarios)\n            {\n                var scenario = new RoomCombinationScenario(scenarioConfig);\n                RoomCombinationScenarios.Add(scenario);\n            }\n        }\n\n        private void SetRooms()\n        {\n            _rooms = new List<IEssentialsRoom>();\n\n            foreach (var roomKey in _propertiesConfig.RoomKeys)\n            {\n                var room = DeviceManager.GetDeviceForKey(roomKey);\n\n                if (DeviceManager.GetDeviceForKey(roomKey) is IEssentialsRoom essentialsRoom)\n                {\n                    _rooms.Add(essentialsRoom);\n                }\n            }\n\n            var rooms = DeviceManager.AllDevices.OfType<IEssentialsRoom>().Cast<Device>();\n\n            foreach (var room in rooms)\n            {\n                room.Deactivate();\n            }\n        }\n\n        private void SetupPartitionStateProviders()\n        {\n            foreach (var pConfig in _propertiesConfig.Partitions)\n            {\n                var sensor = DeviceManager.GetDeviceForKey(pConfig.DeviceKey) as IPartitionStateProvider;\n\n                var partition = new EssentialsPartitionController(pConfig.Key, pConfig.Name, sensor, _propertiesConfig.defaultToManualMode, pConfig.AdjacentRoomKeys);\n\n                partition.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;\n\n                Partitions.Add(partition);\n            }\n        }\n\n        private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            StartDebounceTimer();\n        }\n\n        private void StartDebounceTimer()\n        {\n            // default to 500ms for manual mode\n            var time = 500;\n\n            // if in auto mode, debounce the scenario change\n            if (IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000;\n\n            if (_scenarioChangeDebounceTimer == null)\n            {\n                _scenarioChangeDebounceTimer = new CTimer((o) => DetermineRoomCombinationScenario(), time);\n            }\n            else\n            {\n                _scenarioChangeDebounceTimer.Reset(time);\n            }\n        }\n\n        /// <summary>\n        /// Determines the current room combination scenario based on the state of the partition sensors\n        /// </summary>\n        private void DetermineRoomCombinationScenario()\n        {\n            if (_scenarioChangeDebounceTimer != null)\n            {\n                _scenarioChangeDebounceTimer.Dispose();\n                _scenarioChangeDebounceTimer = null;\n            }\n\n            this.LogInformation(\"Determining Combination Scenario\");\n\n            var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) =>\n            {\n                this.LogDebug(\"Checking scenario {scenarioKey}\", s.Key);\n                // iterate the partition states\n                foreach (var partitionState in s.PartitionStates)\n                {\n                    this.LogDebug(\"checking PartitionState {partitionStateKey}\", partitionState.PartitionKey);\n                    // get the partition by key\n                    var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));\n\n                    this.LogDebug(\"Expected State: {partitionPresent} Actual State: {partitionState}\", partitionState.PartitionPresent, partition.PartitionPresentFeedback.BoolValue);\n\n                    if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue)\n                    {\n                        // the partition can't be found or the state doesn't match\n                        return false;\n                    }\n                }\n                // if it hasn't returned false by now we have the matching scenario\n                return true;\n            });\n\n            if (currentScenario != null)\n            {\n                this.LogInformation(\"Found combination Scenario {scenarioKey}\", currentScenario.Key);\n                ChangeScenario(currentScenario);\n            }\n        }\n\n        private async Task ChangeScenario(IRoomCombinationScenario newScenario)\n        {\n            \n\n                if (newScenario == _currentScenario)\n                {\n                    return;\n                }\n\n                // Deactivate the old scenario first\n                if (_currentScenario != null)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Deactivating scenario {currentScenario}\", this, _currentScenario.Name);\n                    await _currentScenario.Deactivate();\n                }\n\n                _currentScenario = newScenario;\n\n                // Activate the new scenario\n                if (_currentScenario != null)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, $\"Current Scenario: {_currentScenario.Name}\", this);\n                    await _currentScenario.Activate();\n                }\n\n                RoomCombinationScenarioChanged?.Invoke(this, new EventArgs());\n\n            \n        }\n\n        #region IEssentialsRoomCombiner Members\n\n        /// <summary>\n        /// Occurs when the room combination scenario changes.\n        /// </summary>\n        /// <remarks>This event is triggered whenever the configuration or state of the room combination\n        /// changes. Subscribers can use this event to update their logic or UI based on the new scenario.</remarks>\n        public event EventHandler<EventArgs> RoomCombinationScenarioChanged;\n\n        /// <summary>\n        /// Gets the current room combination scenario.\n        /// </summary>\n        public IRoomCombinationScenario CurrentScenario\n        {\n            get\n            {\n                return _currentScenario;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the IsInAutoModeFeedback\n        /// </summary>\n        public BoolFeedback IsInAutoModeFeedback { get; private set; }\n\n        /// <summary>\n        /// Enables auto mode for the room combiner and its partitions, allowing automatic room combination scenarios to\n        /// be determined.\n        /// </summary>\n        /// <remarks>Auto mode allows the room combiner to automatically adjust its configuration based on\n        /// the state of its partitions.  If auto mode is disabled in the configuration, this method logs a warning and\n        /// does not enable auto mode.</remarks>\n        public void SetAutoMode()\n        {\n            if(_propertiesConfig.DisableAutoMode)\n            {\n                this.LogWarning(\"Auto mode is disabled for this room combiner. Cannot set to auto mode.\");\n                return;\n            }\n            IsInAutoMode = true;\n\n            foreach (var partition in Partitions)\n            {\n                partition.SetAutoMode();\n            }\n\n            DetermineRoomCombinationScenario();\n        }\n\n        /// <summary>\n        /// Switches the system to manual mode, disabling automatic operations.\n        /// </summary>\n        /// <remarks>This method sets the system to manual mode by updating the mode state and propagates \n        /// the change to all partitions. Once in manual mode, automatic operations are disabled  for the system and its\n        /// partitions.</remarks>\n        /// <summary>\n        /// SetManualMode method\n        /// </summary>\n        public void SetManualMode()\n        {\n            IsInAutoMode = false;\n\n            foreach (var partition in Partitions)\n            {\n                partition.SetManualMode();\n            }\n        }\n\n        /// <summary>\n        /// Toggles the current mode between automatic and manual.\n        /// </summary>\n        /// <remarks>If the current mode is automatic, this method switches to manual mode.  If the\n        /// current mode is manual, it switches to automatic mode.</remarks>\n        /// <summary>\n        /// ToggleMode method\n        /// </summary>\n        public void ToggleMode()\n        {\n            if (IsInAutoMode)\n            {\n                SetManualMode();\n            }\n            else\n            {\n                SetAutoMode();\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the RoomCombinationScenarios\n        /// </summary>\n        public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }\n\n        /// <summary>\n        /// Gets the collection of partition controllers managed by this instance.\n        /// </summary>\n        public List<IPartitionController> Partitions { get; private set; }\n\n        /// <summary>\n        /// Toggles the state of the partition identified by the specified partition key.\n        /// </summary>\n        /// <remarks>If no partition with the specified key exists, the method performs no\n        /// action.</remarks>\n        /// <param name=\"partitionKey\">The key of the partition whose state is to be toggled. This value cannot be null or empty.</param>\n        public void TogglePartitionState(string partitionKey)\n        {\n            var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey));\n\n            if (partition != null)\n            {\n                partition.ToggglePartitionState();\n            }\n        }\n\n        /// <summary>\n        /// Sets the room combination scenario based on the specified scenario key.\n        /// </summary>\n        /// <remarks>This method manually adjusts the partition states according to the specified\n        /// scenario. If the application is in auto mode,  the operation will not proceed, and a log message will be\n        /// generated indicating that the mode must be set to manual first.  If the specified scenario key does not\n        /// match any existing scenario, a debug log message will be generated. For each partition state in the\n        /// scenario, the corresponding partition will be updated to either \"Present\" or \"Not Present\"  based on the\n        /// scenario's configuration. If a partition key in the scenario cannot be found, a debug log message will be\n        /// generated.</remarks>\n        /// <param name=\"scenarioKey\">The key identifying the room combination scenario to apply. This must match the key of an existing scenario.</param>\n        public void SetRoomCombinationScenario(string scenarioKey)\n        {\n            if (IsInAutoMode)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Cannot set room combination scenario when in auto mode.  Set to auto mode first.\");\n                return;\n            }\n\n            // Get the scenario\n            var scenario = RoomCombinationScenarios.FirstOrDefault((s) => s.Key.Equals(scenarioKey));\n\n\n            // Set the parition states from the scenario manually\n            if (scenario != null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Manually setting scenario to '{0}'\", scenario.Key);\n                foreach (var partitionState in scenario.PartitionStates)\n                {\n                    var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));\n\n                    if (partition != null)\n                    {\n                        if (partitionState.PartitionPresent)\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, this, \"Manually setting state to Present for: '{0}'\", partition.Key);\n                            partition.SetPartitionStatePresent();\n                        }\n                        else\n                        {\n                            Debug.LogMessage(LogEventLevel.Information, this, \"Manually setting state to Not Present for: '{0}'\", partition.Key);\n                            partition.SetPartitionStateNotPresent();\n                        }\n                    }\n                    else\n                    {\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to find partition with key: '{0}'\", partitionState.PartitionKey);\n                    }\n                }\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Unable to find scenario with key: '{0}'\", scenarioKey);\n            }\n        }\n\n        #endregion\n    }\n\n    /// <summary>\n    /// Provides a factory for creating instances of <see cref=\"EssentialsRoomCombiner\"/> devices.\n    /// </summary>\n    /// <remarks>This factory is responsible for constructing <see cref=\"EssentialsRoomCombiner\"/> devices\n    /// based on the provided configuration. It supports the type name \"essentialsroomcombiner\" for device\n    /// creation.</remarks>\n    /// <summary>\n    /// Represents a EssentialsRoomCombinerFactory\n    /// </summary>\n    public class EssentialsRoomCombinerFactory : EssentialsDeviceFactory<EssentialsRoomCombiner>\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"EssentialsRoomCombinerFactory\"/> class.\n        /// </summary>\n        /// <remarks>This factory is used to create instances of room combiners with the specified type\n        /// names. By default, the factory includes the type name \"essentialsroomcombiner\".</remarks>\n        public EssentialsRoomCombinerFactory()\n        {\n            TypeNames = new List<string> { \"essentialsroomcombiner\" };\n        }\n\n        /// <summary>\n        /// Creates and initializes a new instance of the <see cref=\"EssentialsRoomCombiner\"/> device.\n        /// </summary>\n        /// <remarks>This method uses the provided device configuration to extract the properties and\n        /// create an  <see cref=\"EssentialsRoomCombiner\"/> device. Ensure that the configuration contains valid \n        /// properties for the device to be created successfully.</remarks>\n        /// <param name=\"dc\">The device configuration containing the key and properties required to build the device.</param>\n        /// <returns>A new instance of <see cref=\"EssentialsRoomCombiner\"/> initialized with the specified configuration.</returns>\n        public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new EssentialsRoomCombiner Device\");\n\n            var props = dc.Properties.ToObject<EssentialsRoomCombinerPropertiesConfig>();\n\n            return new EssentialsRoomCombiner(dc.Key, props);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs",
    "content": "﻿using System.Collections.Generic;\n\nusing PepperDash.Core;\n\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Config properties for an EssentialsRoomCombiner device\n    /// </summary>\n    public class EssentialsRoomCombinerPropertiesConfig\n    {\n        /// <summary>\r\n        /// Gets or sets a value indicating whether the system operates in automatic mode.\r\n        /// <remarks>Some systems don't have partitions sensors, and show shouldn't allow auto mode to be turned on. When this is true in the configuration, \r\n        /// auto mode won't be allowed to be turned on.</remarks>\r\n        /// </summary>\n        [JsonProperty(\"disableAutoMode\")]\n        public bool DisableAutoMode { get; set; }\n\n        /// <summary>\n        /// The list of partitions that device the rooms\n        /// </summary>\n        [JsonProperty(\"partitions\")]\n        public List<PartitionConfig> Partitions {get; set;}\n\n        /// <summary>\n        /// The list of combinations scenarios for the rooms\n        /// </summary>\n        [JsonProperty(\"scenarios\")]\n        public List<RoomCombinationScenarioConfig> Scenarios { get; set; }\n\n        /// <summary>\n        /// The list of rooms keys that can be combined\n        /// </summary>\n        [JsonProperty(\"roomKeys\")]\n        public List<string> RoomKeys {get; set;}\n\n        /// <summary>\n        /// Set to true to default to manual mode\n        /// </summary>\n        [JsonProperty(\"defaultToManualMode\")]\n        public bool defaultToManualMode { get; set; }\n\n        /// <summary>\n        /// The key of the scenario to default to at system startup if in manual mode\n        /// </summary>\n        [JsonProperty(\"defaultScenarioKey\")]\n        public string defaultScenarioKey { get; set; }\n\n        /// <summary>\r\n        /// Gets or sets the debounce time, in seconds, for scenario changes.\r\n        /// </summary>\n        [JsonProperty(\"scenarioChangeDebounceTimeSeconds\")]\n        public int ScenarioChangeDebounceTimeSeconds { get; set; }\n    }\n\n    /// <summary>\n    /// Config properties for a partition that separates rooms\n    /// </summary>\n    public class PartitionConfig : IKeyName\n    {\n        /// <summary>\r\n        /// Gets or sets the unique key associated with the object.\r\n        /// </summary>\n        [JsonProperty(\"key\")]\n        public string Key { get; set; }\n\n        /// <summary>\r\n        /// Gets or sets the name associated with the object.\r\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Key of the device that implements IPartitionStateProvider to provide the state of the partition\n        /// </summary>\n        [JsonProperty(\"deviceKey\")]\n        public string DeviceKey { get; set; }\n\n        /// <summary>\n        /// Keys of the rooms that this partion would be located between\n        /// </summary>\n        [JsonProperty(\"adjacentRoomKeys\")]\n        public List<string> AdjacentRoomKeys { get; set; }\n    }\n\n    /// <summary>\n    /// Config propeties for a room combination scenario\n    /// </summary>\n    public class RoomCombinationScenarioConfig : IKeyName\n    {\n        /// <summary>\r\n        /// Gets or sets the key associated with the object.\r\n        /// </summary>\n        [JsonProperty(\"key\")]\n        public string Key { get; set; }\n\n        /// <summary>\r\n        /// Gets or sets the name associated with the object.\r\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        /// <summary>\r\n        /// Gets or sets a value indicating whether to hide this scenario in the UI.\r\n        /// </summary>\n        [JsonProperty(\"hideInUi\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool HideInUi { get; set; }\r\n\r\n        /// <summary>\r\n        /// Gets or sets the collection of partition states.\r\n        /// </summary>\n        [JsonProperty(\"partitionStates\")]\n        public List<PartitionState> PartitionStates { get; set; }\n\n        /// <summary>\n        /// Determines which UI devices get mapped to which room in this scenario.  The Key should be the key of the UI device and the Value should be the key of the room to map to\n        /// </summary>\n        [JsonProperty(\"uiMap\")]\n        public Dictionary<string, string> UiMap { get; set; }\n\n        /// <summary>\r\n        /// Gets or sets the list of actions to be performed during device activation.\r\n        /// </summary>\n        [JsonProperty(\"activationActions\")]\n        public List<DeviceActionWrapper> ActivationActions { get; set; }\n\n        /// <summary>\r\n        /// Gets or sets the list of actions to be performed when a device is deactivated.\r\n        /// </summary>\n        [JsonProperty(\"deactivationActions\")]\n        public List<DeviceActionWrapper> DeactivationActions { get; set; }    \n    }\n\n    /// <summary>\n    /// Config properties to represent the state of a partition sensor in a RoomCombinationScenario\n    /// </summary>\n    public class PartitionState\n    {\n        /// <summary>\r\n        /// Gets or sets the partition key used to group and organize data within a storage system.\r\n        /// </summary>\n        [JsonProperty(\"partitionKey\")]\n        public string PartitionKey { get; set; }\n\n        /// <summary>\r\n        /// Gets or sets a value indicating whether a partition is currently present.\r\n        /// </summary>\n        [JsonProperty(\"partitionSensedState\")]\n        public bool PartitionPresent { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Describes the functionality for an EssentailsRoomCombiner device\n    /// </summary>\n    public interface IEssentialsRoomCombiner : IKeyed\n    {\n        /// <summary>\n        /// Indicates that the room combination scenario has changed\n        /// </summary>\n        event EventHandler<EventArgs> RoomCombinationScenarioChanged;\n\n        /// <summary>\n        /// The current room combination scenario\n        /// </summary>\n        [JsonProperty(\"currentScenario\")]\n        IRoomCombinationScenario CurrentScenario { get; }\n\n        /// <summary>\n        /// When true, indicates the current mode is auto mode\n        /// </summary>\n        [JsonIgnore]\n        BoolFeedback IsInAutoModeFeedback {get;}\n\n        /// <summary>\n        /// Gets a value indicating whether the automatic mode is disabled.\n        /// </summary>\n        [JsonProperty(\"disableAutoMode\")]\n        bool DisableAutoMode { get; }\n        /// <summary>\n        /// Gets a value indicating whether the system is operating in automatic mode.\n        /// </summary>\n        [JsonProperty(\"isInAutoMode\")]\n        bool IsInAutoMode { get; }\n\n        /// <summary>\n        /// Gets the collection of rooms associated with the current object.\n        /// </summary>\n        [JsonProperty(\"rooms\")]\n        List<IKeyName> Rooms { get; }\n\n        /// <summary>\n        /// Sets auto mode\n        /// </summary>\n        void SetAutoMode();\n\n        /// <summary>\n        /// Sets manual mode\n        /// </summary>\n        void SetManualMode();\n\n        /// <summary>\n        /// Toggles the current mode between auto and manual\n        /// </summary>\n        void ToggleMode();\n\n        /// <summary>\n        /// The available room combinatino scenarios\n        /// </summary>\n        [JsonProperty(\"roomCombinationScenarios\")]\n        List<IRoomCombinationScenario> RoomCombinationScenarios { get; }\n\n        /// <summary>\n        /// The partition\n        /// </summary>\n        [JsonProperty(\"partitions\")]\n        List<IPartitionController> Partitions { get; }\n\n        /// <summary>\n        /// Toggles the state of a manual partition sensor\n        /// </summary>\n        /// <param name=\"partitionKey\"></param>\n        void TogglePartitionState(string partitionKey);\n\n        /// <summary>\n        /// Sets the room combination scenario (if in manual mode)\n        /// </summary>\n        /// <param name=\"scenarioKey\"></param>\n        void SetRoomCombinationScenario(string scenarioKey);\n    }\n\n    /// <summary>\n    /// Represents a scenario for combining rooms, including activation, deactivation, and associated state.\n    /// </summary>\n    /// <remarks>This interface defines the behavior for managing room combination scenarios, including\n    /// activation and deactivation, tracking the active state, and managing related partition states and UI mappings.\n    /// Implementations of this interface are expected to handle the logic for room combinations based on the provided\n    /// partition states and UI mappings.</remarks>\n    /// <summary>\n    /// Defines the contract for IRoomCombinationScenario\n    /// </summary>\n    public interface IRoomCombinationScenario : IKeyName\n    {\n        /// <summary>\n        /// When true, indicates that this room combination scenario is active\n        /// </summary>\n        [JsonIgnore]\n        BoolFeedback IsActiveFeedback { get; }\n\n        /// <summary>\n        /// Gets a value indicating whether the entity is active.\n        /// </summary>\n        [JsonProperty(\"isActive\")]\n        bool IsActive { get; }\n\n        /// <summary>\n        /// Gets a value indicating whether this scenario should be hidden in the UI.\n        /// </summary>\n        [JsonProperty(\"hideInUi\")]\n        bool HideInUi { get; }\n\n        /// <summary>\n        /// Activates this room combination scenario\n        /// </summary>\n        Task Activate();\n\n        /// <summary>\n        /// Deactivates this room combination scenario\n        /// </summary>\n        Task Deactivate();\n\n        /// <summary>\n        /// The state of the partitions that would activate this scenario\n        /// </summary>\n        [JsonProperty(\"partitionStates\")]\n        List<PartitionState> PartitionStates { get; }\n\n        /// <summary>\n        /// The mapping of UIs by key to rooms by key\n        /// </summary>\n        [JsonProperty(\"uiMap\")]\n        Dictionary<string, string> UiMap { get; set; }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Combining/RoomCombinationScenario.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing Serilog.Events;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a room combination scenario\n    /// </summary>\n    public class RoomCombinationScenario : IRoomCombinationScenario, IKeyName\n    {\n        private RoomCombinationScenarioConfig _config;\n\n        /// <summary>\n        /// Gets or sets the key associated with the object.\n        /// </summary>\n        [JsonProperty(\"key\")]\n        public string Key { get; set; }\n\n        /// <summary>\n        /// Gets or sets the name associated with the object.\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Gets a value indicating whether to hide this scenario in the UI.\n        /// </summary>\n        ///        \n        [JsonProperty(\"hideInUi\")]\n\n        public bool HideInUi\n        {\n            get { return _config.HideInUi; }\n        }\n\n        /// <summary>\n        /// Gets or sets the PartitionStates\n        /// </summary>\n        ///\n        [JsonProperty(\"partitionStates\")]\n\n        public List<PartitionState> PartitionStates { get; private set; }\n\n        /// <summary>\n        /// Determines which UI devices get mapped to which room in this scenario.  The Key should be the key of the UI device and the Value should be the key of the room to map to\n        /// </summary>\n        [JsonProperty(\"uiMap\")]\n        public Dictionary<string, string> UiMap { get; set; }\n\n        private bool _isActive;\n\n        /// <summary>\n        /// Gets or sets IsActive\n        /// </summary>\n        [JsonProperty(\"isActive\")]\n        public bool IsActive\n        {\n            get { return _isActive; }\n            set\n            {\n                if (value == _isActive)\n                {\n                    return;\n                }\n\n                _isActive = value;\n                IsActiveFeedback.FireUpdate();\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the IsActiveFeedback\n        /// </summary>\n        [JsonIgnore]\n        public BoolFeedback IsActiveFeedback { get; private set; }\n\n        private List<DeviceActionWrapper> activationActions;\n\n        private List<DeviceActionWrapper> deactivationActions;\n\n        /// <summary>\n        /// Constructor for RoomCombinationScenario\n        /// </summary>\n        /// <param name=\"config\">config of the room combine scenario</param>\n        public RoomCombinationScenario(RoomCombinationScenarioConfig config)\n        {\n            Key = config.Key;\n\n            Name = config.Name;\n\n            PartitionStates = config.PartitionStates;\n\n            UiMap = config.UiMap;\n\n            activationActions = config.ActivationActions;\n\n            deactivationActions = config.DeactivationActions;\n\n            _config = config;\n\n            IsActiveFeedback = new BoolFeedback(() => _isActive);\n        }\n\n        /// <summary>\n        /// Activates the scenario\n        /// </summary>\n        /// <returns></returns>\n        public async Task Activate()\n        {\n            this.LogInformation(\"Activating Scenario {name} with {activationActionCount} action(s) defined\", Name, activationActions.Count);\n\n            List<Task> tasks = new List<Task>();\n\n            if (activationActions != null)\n            {\n                foreach (var action in activationActions)\n                {\n                    this.LogInformation(\"Running Activation action {@action}\", action);\n                    await DeviceJsonApi.DoDeviceActionAsync(action);\n                }\n            }\n\n            IsActive = true;\n        }\n\n        /// <summary>\n        /// Deactivates the scenario\n        /// </summary>\n        /// <returns></returns>\n        public async Task Deactivate()\n        {\n            this.LogInformation(\"Deactivating Scenario {name} with {deactivationActionCount} action(s) defined\", Name, deactivationActions.Count);\n\n            List<Task> tasks = new List<Task>();\n\n            if (deactivationActions != null)\n            {\n                foreach (var action in deactivationActions)\n                {\n                    this.LogInformation(\"Running deactivation action {actionDeviceKey}:{actionMethod}\", action.DeviceKey, action.MethodName);\n                    await DeviceJsonApi.DoDeviceActionAsync(action);\n                }\n            }\n\n            IsActive = false;\n        }\n\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsDualDisplayRoomPropertiesConfig.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// Represents a EssentialsDualDisplayRoomPropertiesConfig\n    /// </summary>\n    public class EssentialsDualDisplayRoomPropertiesConfig : EssentialsNDisplayRoomPropertiesConfig\n    {\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsHuddleRoomPropertiesConfig.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// Represents a EssentialsHuddleRoomPropertiesConfig\n    /// </summary>\n    public class EssentialsHuddleRoomPropertiesConfig : EssentialsRoomPropertiesConfig\n    {\n        /// <summary>\n        /// The key of the default display device\n        /// </summary>\n        [JsonProperty(\"defaultDisplayKey\")]\n        public string DefaultDisplayKey { get; set; }\n\n        /// <summary>\n        /// The key of the default audio device\n        /// </summary>\n        [JsonProperty(\"defaultAudioKey\")]\n        public string DefaultAudioKey { get; set; }\n\n        /// <summary>\n        /// The key of the source list for the room\n        /// </summary>\n        [JsonProperty(\"sourceListKey\")]\n        public string SourceListKey { get; set; }\n\n        /// <summary>\n        /// The key of the default source item from the source list\n        /// </summary>\n        [JsonProperty(\"defaultSourceItem\")]\n        public string DefaultSourceItem { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs",
    "content": "﻿\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n\n  /// <summary>\n  /// Represents a EssentialsHuddleVtc1PropertiesConfig\n  /// </summary>\n  public class EssentialsHuddleVtc1PropertiesConfig : EssentialsConferenceRoomPropertiesConfig\n  {\n    /// <summary>\n    /// Gets or sets the DefaultDisplayKey\n    /// </summary>\n    [JsonProperty(\"defaultDisplayKey\")]\n    public string DefaultDisplayKey { get; set; }\n\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsNDisplayRoomPropertiesConfig.cs",
    "content": "﻿\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// \n    /// </summary>\n    public class EssentialsNDisplayRoomPropertiesConfig : EssentialsConferenceRoomPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the DefaultAudioBehavior\n        /// </summary>\n        [JsonProperty(\"defaultAudioBehavior\")]\n        public string DefaultAudioBehavior { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DefaultVideoBehavior\n        /// </summary>\n        [JsonProperty(\"defaultVideoBehavior\")]\n        public string DefaultVideoBehavior { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Displays\n        /// </summary>\n        [JsonProperty(\"displays\")]\n        public Dictionary<eSourceListItemDestinationTypes, DisplayItem> Displays { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public EssentialsNDisplayRoomPropertiesConfig()\n        {\n            Displays = new Dictionary<eSourceListItemDestinationTypes, DisplayItem>();\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a DisplayItem\n    /// </summary>\n    public class DisplayItem : IKeyName\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; set; }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsPresentationPropertiesConfig.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// Represents a EssentialsPresentationRoomPropertiesConfig\n    /// </summary>\n    public class EssentialsPresentationRoomPropertiesConfig : EssentialsRoomPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the DefaultAudioBehavior\n        /// </summary>\n        public string DefaultAudioBehavior { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DefaultAudioKey\n        /// </summary>\n        public string DefaultAudioKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DefaultVideoBehavior\n        /// </summary>\n        public string DefaultVideoBehavior { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DisplayKeys\n        /// </summary>\n        public List<string> DisplayKeys { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SourceListKey\n        /// </summary>\n        public string SourceListKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether the room has a DSP\n        /// </summary>\n        public bool HasDsp { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public EssentialsPresentationRoomPropertiesConfig()\n        {\n            DisplayKeys = new List<string>();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Privacy;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// Represents a EssentialsRoomConfigHelper\n    /// </summary>\n    public class EssentialsRoomConfigHelper\n    {\n        /// <summary>\n        /// GetEmergency method\n        /// </summary>\n        public static EssentialsRoomEmergencyBase GetEmergency(EssentialsRoomPropertiesConfig props, IEssentialsRoom room)\n        {\n            // This emergency \n            var emergency = props.Emergency;\n            if (emergency != null)\n            {\n                //switch on emergency type here.  Right now only contact and shutdown\n                var e = new EssentialsRoomEmergencyContactClosure(room.Key + \"-emergency\", props.Emergency, room);\n                DeviceManager.AddDevice(e);\n                return e;\n            }\n            return null;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"props\"></param>\n        /// <param name=\"room\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// GetMicrophonePrivacy method\n        /// </summary>\n        public static MicrophonePrivacyController GetMicrophonePrivacy(\n            EssentialsRoomPropertiesConfig props, IPrivacy room)\n        {\n            var microphonePrivacy = props.MicrophonePrivacy;\n            if (microphonePrivacy == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Cannot create microphone privacy with null properties\");\n                return null;\n            }\n            // Get the MicrophonePrivacy device from the device manager\n            var mP = (DeviceManager.GetDeviceForKey(props.MicrophonePrivacy.DeviceKey) as MicrophonePrivacyController);\n            // Set this room as the IPrivacy device\n            if (mP == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"ERROR: Selected device {0} is not MicrophonePrivacyController\", props.MicrophonePrivacy.DeviceKey);\n                return null;\n            }\n            mP.SetPrivacyDevice(room);\n\n            var behaviour = props.MicrophonePrivacy.Behaviour.ToLower();\n\n            if (behaviour == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"WARNING: No behaviour defined for MicrophonePrivacyController\");\n                return null;\n            }\n            if (behaviour == \"trackroomstate\")\n            {\n                // Tie LED enable to room power state\n                var essRoom = room as IEssentialsRoom;\n                essRoom.OnFeedback.OutputChange += (o, a) =>\n                {\n                    if (essRoom.OnFeedback.BoolValue)\n                        mP.EnableLeds = true;\n                    else\n                        mP.EnableLeds = false;\n                };\n\n                mP.EnableLeds = essRoom.OnFeedback.BoolValue;\n            }\n            else if (behaviour == \"trackcallstate\")\n            {\n                // Tie LED enable to room power state\n                var inCallRoom = room as IHasInCallFeedback;\n                inCallRoom.InCallFeedback.OutputChange += (o, a) =>\n                {\n                    if (inCallRoom.InCallFeedback.BoolValue)\n                        mP.EnableLeds = true;\n                    else\n                        mP.EnableLeds = false;\n                };\n\n                mP.EnableLeds = inCallRoom.InCallFeedback.BoolValue;\n            }\n\n            return mP;\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a EssentialsRoomPropertiesConfig\n    /// </summary>\n    public class EssentialsRoomPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Addresses\n        /// </summary>\n        [JsonProperty(\"addresses\")]\n        public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Description\n        /// </summary>\n        [JsonProperty(\"description\")]\n        public string Description { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Emergency\n        /// </summary>\n        [JsonProperty(\"emergency\")]\n        public EssentialsRoomEmergencyConfig Emergency { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Help\n        /// </summary>\n        [JsonProperty(\"help\")]\n        public EssentialsHelpPropertiesConfig Help { get; set; }\n\n        /// <summary>\n        /// Gets or sets the HelpMessage\n        /// </summary>\n        [JsonProperty(\"helpMessage\")]\n        public string HelpMessage { get; set; }\n\n        /// <summary>\n        /// Read this value to get the help message.  It checks for the old and new config format.\n        /// </summary>\n        public string HelpMessageForDisplay\n        {\n            get\n            {\n                if (Help != null && !string.IsNullOrEmpty(Help.Message))\n                {\n                    return Help.Message;\n                }\n                else\n                {\n                    return HelpMessage;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the Environment\n        /// </summary>\n        [JsonProperty(\"environment\")]\n        public EssentialsEnvironmentPropertiesConfig Environment { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LogoLight\n        /// </summary>\n        [JsonProperty(\"logo\")]\n        public EssentialsLogoPropertiesConfig LogoLight { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LogoDark\n        /// </summary>\n        [JsonProperty(\"logoDark\")]\n        public EssentialsLogoPropertiesConfig LogoDark { get; set; }\n\n        /// <summary>\n        /// Gets or sets the MicrophonePrivacy\n        /// </summary>\n        [JsonProperty(\"microphonePrivacy\")]\n        public EssentialsRoomMicrophonePrivacyConfig MicrophonePrivacy { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Occupancy\n        /// </summary>\n        [JsonProperty(\"occupancy\")]\n        public EssentialsRoomOccSensorConfig Occupancy { get; set; }\n\n        /// <summary>\n        /// Gets or sets the OneButtonMeeting\n        /// </summary>\n        [JsonProperty(\"oneButtonMeeting\")]\n        public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShutdownVacancySeconds\n        /// </summary>\n        [JsonProperty(\"shutdownVacancySeconds\")]\n        public int ShutdownVacancySeconds { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShutdownPromptSeconds\n        /// </summary>\n        [JsonProperty(\"shutdownPromptSeconds\")]\n        public int ShutdownPromptSeconds { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Tech\n        /// </summary>\n        [JsonProperty(\"tech\")]\n        public EssentialsRoomTechConfig Tech { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Volumes\n        /// </summary>\n        [JsonProperty(\"volumes\")]\n        public EssentialsRoomVolumesConfig Volumes { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Fusion\n        /// </summary>\n        [JsonProperty(\"fusion\")]\n        public EssentialsRoomFusionConfig Fusion { get; set; }\n\n        /// <summary>\n        /// Gets or sets the UiBehavior\n        /// </summary>\n        [JsonProperty(\"essentialsRoomUiBehaviorConfig\", NullValueHandling = NullValueHandling.Ignore)]\n        public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ZeroVolumeWhenSwtichingVolumeDevices\n        /// </summary>\n        [JsonProperty(\"zeroVolumeWhenSwtichingVolumeDevices\")]\n        public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; }\n\n        /// <summary>\n        /// Indicates if this room represents a combination of other rooms\n        /// </summary>\n        [JsonProperty(\"isRoomCombinationScenario\")]\n        public bool IsRoomCombinationScenario { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public EssentialsRoomPropertiesConfig()\n        {\n            LogoLight = new EssentialsLogoPropertiesConfig();\n            LogoDark = new EssentialsLogoPropertiesConfig();\n        }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsRoomUiBehaviorConfig\n    /// </summary>\n    public class EssentialsRoomUiBehaviorConfig\n    {\n        /// <summary>\n        /// Gets or sets the DisableActivityButtonsWhileWarmingCooling\n        /// </summary>\n        [JsonProperty(\"disableActivityButtonsWhileWarmingCooling\")]\n        public bool DisableActivityButtonsWhileWarmingCooling { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsAvRoomPropertiesConfig\n    /// </summary>\n    public class EssentialsAvRoomPropertiesConfig : EssentialsRoomPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the DefaultAudioKey\n        /// </summary>\n        [JsonProperty(\"defaultAudioKey\")]\n        public string DefaultAudioKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DefaultOnDspPresetKey\n        /// </summary>\n        [JsonProperty(\"defaultOnDspPresetKey\")]\n        public string DefaultOnDspPresetKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DefaultOffDspPresetKey\n        /// </summary>\n        [JsonProperty(\"defaultOffDspPresetKey\")]\n        public string DefaultOffDspPresetKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SourceListKey\n        /// </summary>\n        [JsonProperty(\"sourceListKey\")]\n        public string SourceListKey { get; set; }\n        /// <summary>\n        /// Gets or sets the DestinationListKey\n        /// </summary>\n        [JsonProperty(\"destinationListKey\")]\n        public string DestinationListKey { get; set; }\n        /// <summary>\n        /// Gets or sets the AudioControlPointListKey\n        /// </summary>\n        [JsonProperty(\"audioControlPointListKey\")]\n        public string AudioControlPointListKey { get; set; }\n        /// <summary>\n        /// Gets or sets the CameraListKey\n        /// </summary>\n        [JsonProperty(\"cameraListKey\")]\n        public string CameraListKey { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the DefaultSourceItem\n        /// </summary>\n        [JsonProperty(\"defaultSourceItem\")]\n        public string DefaultSourceItem { get; set; }\n        /// <summary>\n        /// Indicates if the room supports advanced sharing\n        /// </summary>\n        [JsonProperty(\"supportsAdvancedSharing\")]\n        public bool SupportsAdvancedSharing { get; set; }\n\n        /// <summary>\n        /// Indicates if non-tech users can change the share mode\n        /// </summary>\n        [JsonProperty(\"userCanChangeShareMode\")]\n        public bool UserCanChangeShareMode { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the MatrixRoutingKey\n        /// </summary>\n        [JsonProperty(\"matrixRoutingKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string MatrixRoutingKey { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsConferenceRoomPropertiesConfig\n    /// </summary>\n    public class EssentialsConferenceRoomPropertiesConfig : EssentialsAvRoomPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the VideoCodecKey\n        /// </summary>\n        [JsonProperty(\"videoCodecKey\")]\n        public string VideoCodecKey { get; set; }\n        /// <summary>\n        /// Gets or sets the AudioCodecKey\n        /// </summary>\n        [JsonProperty(\"audioCodecKey\")]\n        public string AudioCodecKey { get; set; }\n\n    }\n\n    /// <summary>\n    /// Represents a EssentialsEnvironmentPropertiesConfig\n    /// </summary>\n    public class EssentialsEnvironmentPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Enabled\n        /// </summary>\n        public bool Enabled { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DeviceKeys\n        /// </summary>\n        [JsonProperty(\"deviceKeys\")]\n        public List<string> DeviceKeys { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public EssentialsEnvironmentPropertiesConfig()\n        {\n            DeviceKeys = new List<string>();\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a EssentialsRoomFusionConfig\n    /// </summary>\n    public class EssentialsRoomFusionConfig\n    {\n        /// <summary>\n        /// Gets the the IpId as a UInt16\n        /// </summary>\n        public uint IpIdInt\n        {\n            get\n            {\n                try\n                {\n                    return Convert.ToUInt32(IpId, 16);\n                }\n                catch (Exception)\n                {\n                    throw new FormatException(string.Format(\"ERROR:Unable to convert IP ID: {0} to hex.  Error:\\n{1}\", IpId));\n                }\n\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the IpId\n        /// </summary>\n        [JsonProperty(\"ipId\")]\n        public string IpId { get; set; }\n\n        /// <summary>\n        /// Gets or sets the JoinMapKey\n        /// </summary>\n        [JsonProperty(\"joinMapKey\")]\n        public string JoinMapKey { get; set; }\n\n    }\n\n    /// <summary>\n    /// Represents a EssentialsRoomMicrophonePrivacyConfig\n    /// </summary>\n    public class EssentialsRoomMicrophonePrivacyConfig\n    {\n        /// <summary>\n        /// Gets or sets the DeviceKey\n        /// </summary>\n        [JsonProperty(\"deviceKey\")]\n        public string DeviceKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Behaviour\n        /// </summary>\n        [JsonProperty(\"behaviour\")]\n        public string Behaviour { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsHelpPropertiesConfig\n    /// </summary>\n    public class EssentialsHelpPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Message\n        /// </summary>\n        [JsonProperty(\"message\")]\n        public string Message { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShowCallButton\n        /// </summary>\n        [JsonProperty(\"showCallButton\")]\n        public bool ShowCallButton { get; set; }\n\n        /// <summary>\n        /// Defaults to \"Call Help Desk\"\n        /// </summary>\n        [JsonProperty(\"callButtonText\")]\n        public string CallButtonText { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public EssentialsHelpPropertiesConfig()\n        {\n            CallButtonText = \"Call Help Desk\";\n        }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsOneButtonMeetingPropertiesConfig\n    /// </summary>\n    public class EssentialsOneButtonMeetingPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Enable\n        /// </summary>\n        [JsonProperty(\"enable\")]\n        public bool Enable { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsRoomAddressPropertiesConfig\n    /// </summary>\n    public class EssentialsRoomAddressPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the PhoneNumber\n        /// </summary>\n        [JsonProperty(\"phoneNumber\")]\n        public string PhoneNumber { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SipAddress\n        /// </summary>\n        [JsonProperty(\"sipAddress\")]\n        public string SipAddress { get; set; }\n    }\n\n\n    /// <summary>\n    /// Represents a EssentialsLogoPropertiesConfig\n    /// </summary>\n    public class EssentialsLogoPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        [JsonProperty(\"type\")]\n        public string Type { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Url\n        /// </summary>\n        [JsonProperty(\"url\")]\n        public string Url { get; set; }\n\n        /// <summary>\n        /// GetLogoUrlLight method\n        /// </summary>\n        public string GetLogoUrlLight()\n        {\n            if (Type == \"url\")\n                return Url;\n            if (Type == \"system\")\n                return string.Format(\"http://{0}:8080/logo.png\",\n                    CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0));\n            return null;\n        }\n\n        /// <summary>\n        /// GetLogoUrlDark method\n        /// </summary>\n        public string GetLogoUrlDark()\n        {\n            if (Type == \"url\")\n                return Url;\n            if (Type == \"system\")\n                return string.Format(\"http://{0}:8080/logo-dark.png\",\n                    CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0));\n            return null;\n        }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsRoomOccSensorConfig\n    /// </summary>\n    public class EssentialsRoomOccSensorConfig\n    {\n        /// <summary>\n        /// Gets or sets the DeviceKey\n        /// </summary>\n        [JsonProperty(\"deviceKey\")]\n        public string DeviceKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the TimeoutMinutes\n        /// </summary>\n        [JsonProperty(\"timeoutMinutes\")]\n        public int TimeoutMinutes { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsRoomTechConfig\n    /// </summary>\n    public class EssentialsRoomTechConfig\n    {\n        /// <summary>\n        /// Gets or sets the Password\n        /// </summary>\n        [JsonProperty(\"password\")]\n        public string Password { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomEmergencyConfig.cs",
    "content": "﻿namespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// Represents a EssentialsRoomEmergencyConfig\n    /// </summary>\n    public class EssentialsRoomEmergencyConfig\n    {\n        /// <summary>\n        /// Gets or sets the Trigger\n        /// </summary>\n        public EssentialsRoomEmergencyTriggerConfig Trigger { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Behavior\n        /// </summary>\n        public string Behavior { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsRoomEmergencyTriggerConfig\n    /// </summary>\n    public class EssentialsRoomEmergencyTriggerConfig\n    {\n        /// <summary>\n        /// contact,versiport\n        /// </summary>\n        public string Type { get; set; }\n\n        /// <summary>\n        /// Input number if contact\n        /// </summary>\n        public int Number { get; set; }\n\n        /// <summary>\n        /// TriggerOnClose indicates if the trigger is on close\n        /// </summary>\n        public bool TriggerOnClose { get; set; }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomScheduledEventsConfig.cs",
    "content": "﻿\n\nusing System.Collections.Generic;\nusing Crestron.SimplSharp.Scheduler;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// Represents a EssentialsRoomScheduledEventsConfig\n    /// </summary>\n    public class EssentialsRoomScheduledEventsConfig\n    {\n        /// <summary>\n        /// Gets or sets the ScheduledEvents\n        /// </summary>\n        [JsonProperty(\"scheduledEvents\")]\n        public List<ScheduledEventConfig> ScheduledEvents;\n    }\n\n    /// <summary>\n    /// Represents a ScheduledEventConfig\n    /// </summary>\n    public class ScheduledEventConfig\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        [JsonProperty(\"key\")]\n        public string Key;\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        [JsonProperty(\"name\")]\n        public string Name;\n\n        /// <summary>\n        /// Gets or sets the Days\n        /// </summary>\n        [JsonProperty(\"days\")]\n        public ScheduledEventCommon.eWeekDays Days;\n\n        /// <summary>\n        /// Gets or sets the Time\n        /// </summary>\n        [JsonProperty(\"time\")]\n        public string Time;\n\n        /// <summary>\n        /// Gets or sets the Actions\n        /// </summary>\n        [JsonProperty(\"actions\")]\n        public List<DeviceActionWrapper> Actions;\n\n        /// <summary>\n        /// Gets or sets the Persistent\n        /// </summary>\n        [JsonProperty(\"persistent\")]\n        public bool Persistent;\n\n        /// <summary>\n        /// Gets or sets the Acknowledgeable\n        /// </summary>\n        [JsonProperty(\"acknowledgeable\")]\n        public bool Acknowledgeable;\n\n        /// <summary>\n        /// Gets or sets the Enable\n        /// </summary>\n        [JsonProperty(\"enable\")]\n        public bool Enable;\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsTechRoomConfig.cs",
    "content": "﻿\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Essentials.Room.Config;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// Represents a EssentialsTechRoomConfig\n    /// </summary>\n    public class EssentialsTechRoomConfig\n    {\n        /// <summary>\n        /// The key of the dummy device used to enable routing\n        /// </summary>\n        [JsonProperty(\"dummySourceKey\")]\n        public string DummySourceKey { get; set; }\n\n        /// <summary>\n        /// The keys of the displays assigned to this room\n        /// </summary>\n        [JsonProperty(\"displays\")]\n        public List<string> Displays { get; set; }\n        \n        /// <summary>\n        /// The keys of the tuners assigned to this room\n        /// </summary>\n        [JsonProperty(\"tuners\")]\n        public List<string> Tuners { get; set; }\n\n        /// <summary>\n        /// PIN to access the room as a normal user\n        /// </summary>\n        [JsonProperty(\"userPin\")]\n        public string UserPin { get; set; }\n\n        /// <summary>\n        /// PIN to access the room as a tech user\n        /// </summary>\n        [JsonProperty(\"techPin\")]\n        public string TechPin { get; set; }\n\n        /// <summary>\n        /// Name of the presets file.  Path prefix is assumed to be /html/presets/lists/\n        /// </summary>\n        [JsonProperty(\"presetsFileName\")]\n        public string PresetsFileName { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ScheduledEvents\n        /// </summary>\n        [JsonProperty(\"scheduledEvents\")]\n        public List<ScheduledEventConfig> ScheduledEvents { get; set; }\n\n        /// <summary>\n        /// Indicates that the room is the primary when true\n        /// </summary>\n        [JsonProperty(\"isPrimary\")]\n        public bool IsPrimary { get; set; }\n\n        /// <summary>\n        /// Indicates which tuners should mirror preset recall when two rooms are configured in a primary->secondary scenario\n        /// </summary>\n        [JsonProperty(\"mirroredTuners\")]\n        public Dictionary<uint, string> MirroredTuners { get; set; }\n\n        /// <summary>\n        /// Gets or sets the HelpMessage\n        /// </summary>\n        [JsonProperty(\"helpMessage\")]\n        public string HelpMessage { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IsTvPresetsProvider\n        /// </summary>\n        [JsonProperty(\"isTvPresetsProvider\")] \n        public bool IsTvPresetsProvider;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public EssentialsTechRoomConfig()\n        {\n            Displays = new List<string>();\n            Tuners = new List<string>();\n            ScheduledEvents = new List<ScheduledEventConfig>();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/EssentialsVolumeLevelConfig.cs",
    "content": "﻿using System;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n    /// <summary>\n    /// Represents a EssentialsRoomVolumesConfig\n    /// </summary>\n    public class EssentialsRoomVolumesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Master\n        /// </summary>\n        public EssentialsVolumeLevelConfig Master { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Program\n        /// </summary>\n        public EssentialsVolumeLevelConfig Program { get; set; }\n\n        /// <summary>\n        /// Gets or sets the AudioCallRx\n        /// </summary>\n        public EssentialsVolumeLevelConfig AudioCallRx { get; set; }\n\n        /// <summary>\n        /// Gets or sets the AudioCallTx\n        /// </summary>\n        public EssentialsVolumeLevelConfig AudioCallTx { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a EssentialsVolumeLevelConfig\n    /// </summary>\n    public class EssentialsVolumeLevelConfig\n    {\n        /// <summary>\n        /// Gets or sets the DeviceKey\n        /// </summary>\n        public string DeviceKey { get; set; }\n        /// <summary>\n        /// Gets or sets the Label\n        /// </summary>\n        public string Label { get; set; }\n        /// <summary>\n        /// Gets or sets the Level\n        /// </summary>\n        public int Level { get; set; }\n\n        /// <summary>\n        /// Helper to get the device associated with key - one timer.\n        /// </summary>\n        public IBasicVolumeWithFeedback GetDevice()\n        {\n            throw new NotImplementedException(\"This method references DM CHASSIS Directly\");\n            /*\n            // DM output card format: deviceKey--output~number, dm8x8-1--output~4\n            var match = Regex.Match(DeviceKey, @\"([-_\\w]+)--(\\w+)~(\\d+)\");\n            if (match.Success)\n            {\n                var devKey = match.Groups[1].Value;\n                var chassis = DeviceManager.GetDeviceForKey(devKey) as DmChassisController;\n                if (chassis != null)\n                {\n                    var outputNum = Convert.ToUInt32(match.Groups[3].Value);\n                    if (chassis.VolumeControls.ContainsKey(outputNum)) // should always...\n                        return chassis.VolumeControls[outputNum];\n                }\n                // No volume for some reason. We have failed as developers\n                return null;\n            }\n\n            // DSP/DMPS format: deviceKey--levelName, biampTesira-1--master\n            match = Regex.Match(DeviceKey, @\"([-_\\w]+)--(.+)\");\n            if (match.Success)\n            {\n                var devKey = match.Groups[1].Value;\n                var dsp = DeviceManager.GetDeviceForKey(devKey) as BiampTesiraForteDsp;\n                if (dsp != null)\n                {\n                    var levelTag = match.Groups[2].Value;\n                    if (dsp.LevelControlPoints.ContainsKey(levelTag)) // should always...\n                        return dsp.LevelControlPoints[levelTag];\n                }\n\n                var dmps = DeviceManager.GetDeviceForKey(devKey) as DmpsAudioOutputController;\n                if (dmps != null)\n                {\n                    var levelTag = match.Groups[2].Value;\n                    switch (levelTag)\n                    {\n                        case \"master\":\n                            return dmps.MasterVolumeLevel;\n                        case \"source\":\n                            return dmps.SourceVolumeLevel;\n                        case \"micsmaster\":\n                            return dmps.MicsMasterVolumeLevel;\n                        case \"codec1\":\n                            return dmps.Codec1VolumeLevel;\n                        case \"codec2\":\n                            return dmps.Codec2VolumeLevel;\n                        default:\n                            return dmps.MasterVolumeLevel;\n                    }\n                }\n                // No volume for some reason. We have failed as developers\n                return null;\n            }\n\n            return null;\n        }\n             * */\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Config/SimplRoomPropertiesConfig.cs",
    "content": "﻿using System.Collections.Generic;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Room.Config\n{\n /// <summary>\n /// Represents a SimplRoomPropertiesConfig\n /// </summary>\n\tpublic class SimplRoomPropertiesConfig : EssentialsHuddleVtc1PropertiesConfig\n\t{\n        /// <summary>\n        /// Gets or sets the RoomPhoneNumber\n        /// </summary>\n\t\t[JsonProperty(\"roomPhoneNumber\")]\n\t\tpublic string RoomPhoneNumber { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the RoomURI\n        /// </summary>\n\t\t[JsonProperty(\"roomURI\")]\n\t\tpublic string RoomURI { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the SpeedDials\n        /// </summary>\n\t\t[JsonProperty(\"speedDials\")]\n\t\tpublic List<SimplSpeedDial> SpeedDials { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the VolumeSliderNames\n        /// </summary>\n\t\t[JsonProperty(\"volumeSliderNames\")]\n\t\tpublic List<string> VolumeSliderNames { get; set; }\n\t}\n\n    /// <summary>\n    /// Represents a SimplSpeedDial\n    /// </summary>\n\tpublic class SimplSpeedDial\n\t{\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n\t\t[JsonProperty(\"name\")]\n\t\tpublic string Name { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the Number\n        /// </summary>\n\t\t[JsonProperty(\"number\")]\n\t\tpublic string Number { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/EsentialsRoomEmergencyContactClosure.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharpPro;\nusing PepperDash.Essentials.Room.Config;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a EssentialsRoomEmergencyContactClosure\n    /// </summary>\n    public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase, IEssentialsRoomEmergency\n    {\n        /// <summary>\n        /// Event fired when emergency state changes\n        /// </summary>\n        public event EventHandler<EventArgs> EmergencyStateChange;\n\n        IEssentialsRoom Room;\n        string Behavior;\n        bool TriggerOnClose;\n\n        /// <summary>\n        /// Gets or sets the InEmergency\n        /// </summary>\n        public bool InEmergency { get; private set; }\n\n        /// <summary>\n        /// Constructor for EssentialsRoomEmergencyContactClosure\n        /// </summary>\n        /// <param name=\"key\">device key</param>\n        /// <param name=\"config\">emergency device config</param>\n        /// <param name=\"room\">the room associated with this emergency contact closure</param>\n        public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, IEssentialsRoom room) :\n            base(key)\n        {\n            Room = room;\n            var cs = Global.ControlSystem;\n\n            if (config.Trigger.Type.Equals(\"contact\", StringComparison.OrdinalIgnoreCase))\n            {\n                var portNum = (uint)config.Trigger.Number;\n                if (portNum <= cs.NumberOfDigitalInputPorts)\n                {\n                    cs.DigitalInputPorts[portNum].Register();\n                    cs.DigitalInputPorts[portNum].StateChange += EsentialsRoomEmergencyContactClosure_StateChange;\n                }\n            }\n            else if (config.Trigger.Type.Equals(\"versiport\", StringComparison.OrdinalIgnoreCase))\n            {\n                var portNum = (uint)config.Trigger.Number;\n                if (portNum <= cs.NumberOfVersiPorts)\n                {\n                    cs.VersiPorts[portNum].Register();\n                    cs.VersiPorts[portNum].SetVersiportConfiguration(eVersiportConfiguration.DigitalInput);\n                    cs.VersiPorts[portNum].DisablePullUpResistor = true;\n                    cs.VersiPorts[portNum].VersiportChange += EssentialsRoomEmergencyContactClosure_VersiportChange;\n                }\n            }\n            Behavior = config.Behavior;\n            TriggerOnClose = config.Trigger.TriggerOnClose;\n        }\n\n        private void EssentialsRoomEmergencyContactClosure_VersiportChange(Versiport port, VersiportEventArgs args)\n        {\n            if (args.Event == eVersiportEvent.DigitalInChange)\n            {\n                ContactClosure_StateChange(port.DigitalIn);\n            }\n        }\n\n        void EsentialsRoomEmergencyContactClosure_StateChange(DigitalInput digitalInput, DigitalInputEventArgs args)\n        {\n            ContactClosure_StateChange(args.State);\n        }\n\n        void ContactClosure_StateChange(bool portState)\n        {\n            if (portState && TriggerOnClose || !portState && !TriggerOnClose)\n            {\n                InEmergency = true;\n                if (EmergencyStateChange != null)\n                    EmergencyStateChange(this, new EventArgs());\n                RunEmergencyBehavior();\n            }\n            else\n            {\n                InEmergency = false;\n                if (EmergencyStateChange != null)\n                    EmergencyStateChange(this, new EventArgs());\n            }\n        }\n\n        /// <summary>\n        /// RunEmergencyBehavior method\n        /// </summary>\n        public void RunEmergencyBehavior()\n        {\n            if (Behavior.Equals(\"shutdown\"))\n                Room.Shutdown();\n        }\n    }\n\n    /// <summary>\n    /// Defines the contract for IEssentialsRoomEmergency\n    /// </summary>\n    public interface IEssentialsRoomEmergency\n    {\n        /// <summary>\n        /// Event fired when emergency state changes\n        /// </summary>\n        event EventHandler<EventArgs> EmergencyStateChange;\n\n        /// <summary>\n        /// Gets or sets the InEmergency\n        /// </summary>\n        bool InEmergency { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.Scheduler;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Devices;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// \n    /// </summary>\n    public abstract class EssentialsRoomBase : ReconfigurableDevice, IEssentialsRoom\n    {\n        /// <summary>\n        ///\n        /// </summary>\n        public BoolFeedback OnFeedback { get; private set; }\n\n        /// <summary>\n        /// Fires when the RoomOccupancy object is set\n        /// </summary>\n        public event EventHandler<EventArgs> RoomOccupancyIsSet;\n\n        /// <summary>\n        /// Gets or sets the IsWarmingUpFeedback\n        /// </summary>\n        public BoolFeedback IsWarmingUpFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IsCoolingDownFeedback\n        /// </summary>\n        public BoolFeedback IsCoolingDownFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the RoomOccupancy\n        /// </summary>\n        public IOccupancyStatusProvider RoomOccupancy { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the OccupancyStatusProviderIsRemote\n        /// </summary>\n        public bool OccupancyStatusProviderIsRemote { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the EnvironmentalControlDevices\n        /// </summary>\n        public List<EssentialsDevice> EnvironmentalControlDevices { get; protected set; }\n\n        /// <summary>\n        /// Indicates if the room has any environmental control devices\n        /// </summary>\n        public bool HasEnvironmentalControlDevices\n        {\n            get\n            {\n                return EnvironmentalControlDevices != null && EnvironmentalControlDevices.Count > 0;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the IsWarmingFeedbackFunc\n        /// </summary>\n        protected abstract Func<bool> IsWarmingFeedbackFunc { get; }\n\n        /// <summary>\n        /// Gets or sets the IsCoolingFeedbackFunc\n        /// </summary>\n        protected abstract Func<bool> IsCoolingFeedbackFunc { get; }\n\n        /// <summary>\n        /// Gets or sets the IsMobileControlEnabled\n        /// </summary>\n        public bool IsMobileControlEnabled { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the MobileControlRoomBridge\n        /// </summary>\n        public IMobileControlRoomMessenger MobileControlRoomBridge { get; private set; }\n\n        /// <summary>\n        /// The config name of the default source list\n        /// </summary>\n        protected const string _defaultListKey = \"default\";\n\n        /// <summary>\n        /// The config name of the source list\n        /// </summary>\n\t\t/// \n\t\tprivate string _sourceListKey;\n\n        /// <summary>\n        /// Gets or sets the SourceListKey\n        /// </summary>\n        public string SourceListKey {\n\t\t\tget\n\t\t\t{\n                if(string.IsNullOrEmpty(_sourceListKey))\n                {\n                    return _defaultListKey;\n                }\n                else\n                {\n                    return _sourceListKey;\n                }\n\t\t\t}\n\t\t\tprotected set\n\t\t\t{\n                if (value != _sourceListKey)\n                {\n                    _sourceListKey = value;\n                }\n\t\t\t}\n\t\t}\n\n        private string _destinationListKey;\n\n        /// <summary>\n        /// Gets or sets the DestinationListKey\n        /// </summary>\n        public string DestinationListKey\n        {\n            get \n            {\n                if (string.IsNullOrEmpty(_destinationListKey))\n                {\n                    return _defaultListKey;\n                }\n                else\n                {\n                    return _destinationListKey;\n                }\n            }\n            protected set\n            {\n                if (value != _destinationListKey)\n                {\n                    _destinationListKey = value;\n                }\n            }\n        }\n\n        private string _audioControlPointListKey;\n\n        /// <summary>\n        /// Gets or sets the AudioControlPointListKey\n        /// </summary>\n        public string AudioControlPointListKey\n        {\n            get\n            {\n                if (string.IsNullOrEmpty(_audioControlPointListKey))\n                {\n                    return _defaultListKey;\n                }\n                else\n                {\n                    return _destinationListKey;\n                }\n            }\n            protected set\n            {\n                if (value != _audioControlPointListKey)\n                {\n                    _audioControlPointListKey = value;\n                }\n            }\n        }\n\n        private string _cameraListKey;\n\n        /// <summary>\n        /// Gets or sets the CameraListKey\n        /// </summary>\n        public string CameraListKey\n        {\n            get\n            {\n                if (string.IsNullOrEmpty(_cameraListKey))\n                {\n                    return _defaultListKey;\n                }\n                else\n                {\n                    return _cameraListKey;\n                }\n            }\n            protected set\n            {\n                if (value != _cameraListKey)\n                {\n                    _cameraListKey = value;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the ShutdownPromptTimer\n        /// </summary>\n        public SecondsCountdownTimer ShutdownPromptTimer { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the ShutdownPromptSeconds\n        /// </summary>\n        public int ShutdownPromptSeconds { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShutdownVacancySeconds\n        /// </summary>\n        public int ShutdownVacancySeconds { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShutdownType\n        /// </summary>\n        public eShutdownType ShutdownType { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Emergency\n        /// </summary>\n        public EssentialsRoomEmergencyBase Emergency { get; set; }\n\n        /// <summary>\n        /// Gets or sets the MicrophonePrivacy\n        /// </summary>\n        public Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LogoUrlLightBkgnd\n        /// </summary>\n        public string LogoUrlLightBkgnd { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LogoUrlDarkBkgnd\n        /// </summary>\n        public string LogoUrlDarkBkgnd { get; set; }\n\n        /// <summary>\n        /// Gets or sets the RoomVacancyShutdownTimer\n        /// </summary>\n        protected SecondsCountdownTimer RoomVacancyShutdownTimer { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the VacancyMode\n        /// </summary>\n        public eVacancyMode VacancyMode { get; private set; }\n\n        /// <summary>\n        /// Seconds after vacancy prompt is displayed until shutdown\n        /// </summary>\n        protected int RoomVacancyShutdownSeconds;\n\n        /// <summary>\n        /// Seconds after vacancy detected until prompt is displayed\n        /// </summary>\n        protected int RoomVacancyShutdownPromptSeconds;\n\n        /// <summary>\n        /// \n        /// </summary>\n        protected abstract Func<bool> OnFeedbackFunc { get; }\n\n        /// <summary>\n        /// Gets or sets the SavedVolumeLevels\n        /// </summary>\n\t\tprotected Dictionary<IBasicVolumeWithFeedback, uint> SavedVolumeLevels = new Dictionary<IBasicVolumeWithFeedback, uint>();\n\n\t\t/// <summary>\n\t\t/// When volume control devices change, should we zero the one that we are leaving?\n\t\t/// </summary>\n\t\tpublic bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; }\n\n        /// <summary>\n        /// Constructor for EssentialsRoomBase\n        /// </summary>\n        /// <param name=\"config\">config of the device</param>\n        public EssentialsRoomBase(DeviceConfig config)\n            : base(config)\n        {\n            EnvironmentalControlDevices = new List<EssentialsDevice>();\n\n            // Setup the ShutdownPromptTimer\n            ShutdownPromptTimer = new SecondsCountdownTimer(Key + \"-offTimer\");\n            ShutdownPromptTimer.IsRunningFeedback.OutputChange += (o, a) =>\n            {\n                if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue)\n                    ShutdownType = eShutdownType.None;\n            };\n\n            ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered \n\n            ShutdownPromptSeconds = 60;\n            ShutdownVacancySeconds = 120; \n            \n            ShutdownType = eShutdownType.None;\n\n            RoomVacancyShutdownTimer = new SecondsCountdownTimer(Key + \"-vacancyOffTimer\");\n            //RoomVacancyShutdownTimer.IsRunningFeedback.OutputChange += (o, a) =>\n            //{\n            //    if (!RoomVacancyShutdownTimer.IsRunningFeedback.BoolValue)\n            //        ShutdownType = ShutdownType.Vacancy;\n            //};\n            RoomVacancyShutdownTimer.HasFinished += new EventHandler<EventArgs>(RoomVacancyShutdownPromptTimer_HasFinished); // Shutdown is triggered\n\n            RoomVacancyShutdownPromptSeconds = 1500;    //  25 min to prompt warning\n            RoomVacancyShutdownSeconds = 240;           //  4 min after prompt will trigger shutdown prompt\n            VacancyMode = eVacancyMode.None;\n\n            OnFeedback = new BoolFeedback(OnFeedbackFunc);\n\n            IsWarmingUpFeedback = new BoolFeedback(IsWarmingFeedbackFunc);\n            IsCoolingDownFeedback = new BoolFeedback(IsCoolingFeedbackFunc);\n\n            AddPostActivationAction(() =>\n            {\n                if (RoomOccupancy != null)\n                    OnRoomOccupancyIsSet();\n            });\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            SetUpMobileControl();\n\n            return base.CustomActivate();\n        }\n\n        /// <summary>\n        /// Sets the SourceListKey property to the passed in value or the default if no value passed in\n        /// </summary>\n        /// <param name=\"sourceListKey\"></param>\n        protected void SetSourceListKey(string sourceListKey)\n        {\n            if (!string.IsNullOrEmpty(sourceListKey))\n            {\n                SourceListKey = sourceListKey;\n            }\n            else\n            {\n                sourceListKey = _defaultListKey;\n            }\n        }\n\n        /// <summary>\n        /// Sets the DestinationListKey property to the passed in value or the default if no value passed in\n        /// </summary>\n        /// <param name=\"destinationListKey\">key of the destination list object</param>\n        protected void SetDestinationListKey(string destinationListKey)\n        {\n            if (!string.IsNullOrEmpty(destinationListKey))\n            {\n                DestinationListKey = destinationListKey;\n            }\n        }\n\n        /// <summary>\n        /// If mobile control is enabled, sets the appropriate properties\n        /// </summary>\n        void SetUpMobileControl()\n        {\n            var mcBridgeKey = string.Format(\"mobileControlBridge-{0}\", Key);\n            var mcBridge = DeviceManager.GetDeviceForKey(mcBridgeKey);\n            if (mcBridge == null)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"*********************Mobile Control Bridge Not found for this room.\");\n                IsMobileControlEnabled = false;\n                return;\n            }\n            else\n            {\n                MobileControlRoomBridge = mcBridge as IMobileControlRoomMessenger;\n                Debug.LogMessage(LogEventLevel.Debug, this, \"*********************Mobile Control Bridge found and enabled for this room\");\n                IsMobileControlEnabled = true;\n            }\n        }\n\n        void RoomVacancyShutdownPromptTimer_HasFinished(object sender, EventArgs e)\n        {\n            switch (VacancyMode)\n            {\n                case eVacancyMode.None:\n                    StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);\n                    break;\n                case eVacancyMode.InInitialVacancy:\n                    StartRoomVacancyTimer(eVacancyMode.InShutdownWarning);\n                    break;\n                case eVacancyMode.InShutdownWarning:\n                    {\n                        StartShutdown(eShutdownType.Vacancy);\n                        Debug.LogMessage(LogEventLevel.Information, this, \"Shutting Down due to vacancy.\");\n                        break;\n                    }\n                default:\n                    break;\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"type\"></param>\n        /// <summary>\n        /// StartShutdown method\n        /// </summary>\n        public void StartShutdown(eShutdownType type)\n        {\n            // Check for shutdowns running. Manual should override other shutdowns\n\n            if (type == eShutdownType.Manual)\n                ShutdownPromptTimer.SecondsToCount = ShutdownPromptSeconds;\n            else if (type == eShutdownType.Vacancy)\n                ShutdownPromptTimer.SecondsToCount = ShutdownVacancySeconds;\n            ShutdownType = type;\n            ShutdownPromptTimer.Start();\n\n            Debug.LogMessage(LogEventLevel.Information, this, \"ShutdownPromptTimer Started. Type: {0}.  Seconds: {1}\", ShutdownType, ShutdownPromptTimer.SecondsToCount);\n        }\n\n        /// <summary>\n        /// StartRoomVacancyTimer method\n        /// </summary>\n        public void StartRoomVacancyTimer(eVacancyMode mode)\n        {\n            if (mode == eVacancyMode.None)\n                RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownPromptSeconds;\n            else if (mode == eVacancyMode.InInitialVacancy)\n                RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownSeconds;\n            else if (mode == eVacancyMode.InShutdownWarning)\n                RoomVacancyShutdownTimer.SecondsToCount = 60;\n            VacancyMode = mode;\n            RoomVacancyShutdownTimer.Start();\n\n            Debug.LogMessage(LogEventLevel.Information, this, \"Vacancy Timer Started. Mode: {0}.  Seconds: {1}\", VacancyMode, RoomVacancyShutdownTimer.SecondsToCount);\n        }\n\n        /// <summary>\n        /// Shutdown method\n        /// </summary>\n        public void Shutdown()\n        {\n            VacancyMode = eVacancyMode.None;\n            EndShutdown();\n        }\n\n        /// <summary>\n        /// This method is for the derived class to define it's specific shutdown\n        /// requirements but should not be called directly.  It is called by Shutdown()\n        /// </summary>\n        protected abstract void EndShutdown();\n\n\n        /// <summary>\n        /// Override this to implement a default volume level(s) method\n        /// </summary>\n        public abstract void SetDefaultLevels();\n\n        /// <summary>\n        /// Sets the object to be used as the IOccupancyStatusProvider for the room. Can be an Occupancy Aggregator or a specific device\n        /// </summary>\n        /// <param name=\"statusProvider\"></param>\n        /// <param name=\"timeoutMinutes\"></param>\n        public void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes)\n        { \n\t\t\tif (statusProvider == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"ERROR: Occupancy sensor device is null\");\n\t\t\t\treturn;\n\t\t\t}\n\n            Debug.LogMessage(LogEventLevel.Information, this, \"Room Occupancy set to device: '{0}'\", (statusProvider as Device).Key);\n            Debug.LogMessage(LogEventLevel.Information, this, \"Timeout Minutes from Config is: {0}\", timeoutMinutes);\n\n            // If status provider is fusion, set flag to remote\n            if (statusProvider is Core.Fusion.IEssentialsRoomFusionController)\n                OccupancyStatusProviderIsRemote = true;\n\n            if(timeoutMinutes > 0)\n                RoomVacancyShutdownSeconds = timeoutMinutes * 60;\n\n            Debug.LogMessage(LogEventLevel.Information, this, \"RoomVacancyShutdownSeconds set to {0}\", RoomVacancyShutdownSeconds);\n\n            RoomOccupancy = statusProvider;\n\n            RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;\n            RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;\n\n            OnRoomOccupancyIsSet();\n        }\n\n        void OnRoomOccupancyIsSet()\n        {\n            var handler = RoomOccupancyIsSet;\n            if (handler != null)\n                handler(this, new EventArgs());\n        }\n\n        /// <summary>\n        /// To allow base class to power room on to last source\n        /// </summary>\n        public abstract void PowerOnToDefaultOrLastSource();\n\n        /// <summary>\n        /// To allow base class to power room on to default source\n        /// </summary>\n        /// <returns></returns>\n        public abstract bool RunDefaultPresentRoute();\n\n        void RoomIsOccupiedFeedback_OutputChange(object sender, EventArgs e)\n        {\n            if (RoomOccupancy.RoomIsOccupiedFeedback.BoolValue == false  && AllowVacancyTimerToStart())\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Notice: Vacancy Detected\");\n                // Trigger the timer when the room is vacant\n                StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Notice: Occupancy Detected\");\n                // Reset the timer when the room is occupied\n                RoomVacancyShutdownTimer.Cancel();\n            }\n        }\n\n        /// <summary>\n        /// Executes when RoomVacancyShutdownTimer expires.  Used to trigger specific room actions as needed.  Must nullify the timer object when executed\n        /// </summary>\n        /// <param name=\"o\"></param>\n        public abstract void RoomVacatedForTimeoutPeriod(object o);\n\n        /// <summary>\n        /// Allow the vacancy event from an occupancy sensor to turn the room off.\n        /// </summary>\n        /// <returns>If the timer should be allowed. Defaults to true</returns>\n        protected virtual bool AllowVacancyTimerToStart()\n        {\n            return true;\n        }\n    }\n        \n    /// <summary>\n    /// To describe the various ways a room may be shutting down\n    /// </summary>\n    public enum eShutdownType\n    {\n        /// <summary>\n        /// No shutdown in progress\n        /// </summary>\n        None = 0,\n\n        /// <summary>\n        /// Manual shutdown initiated\n        /// </summary>\n        External,\n\n        /// <summary>\n        /// Vacancy based shutdown\n        /// </summary>\n        Manual,\n\n        /// <summary>\n        /// Shutdown due to room vacancy\n        /// </summary>\n        Vacancy\n    }\n\n    /// <summary>\n    /// Enumeration of eVacancyMode values\n    /// </summary>\n    public enum eVacancyMode\n    {\n        /// <summary>\n        /// No vacancy detected\n        /// </summary>\n        None = 0,\n\n        /// <summary>\n        /// InInitialVacancy - countdown to warning\n        /// </summary>\n        InInitialVacancy,\n\n        /// <summary>\n        /// InShutdownWarning - countdown to shutdown\n        /// </summary>\n        InShutdownWarning\n    }\n\n    /// <summary>\n    /// Enumeration of eWarmingCoolingMode values\n    /// </summary>\n    public enum eWarmingCoolingMode\n    {\n        /// <summary>\n        /// None\n        /// </summary>\n        None,\n\n        /// <summary>\n        /// Warming\n        /// </summary>\n        Warming,\n\n        /// <summary>\n        /// Cooling\n        /// </summary>\n        Cooling\n    }\n\n    /// <summary>\n    /// Base class for room emergency implementations\n    /// </summary>\n    public abstract class EssentialsRoomEmergencyBase : IKeyed\n    {\n        /// <summary>\n        /// Key of the room\n        /// </summary>\n        public string Key { get; private set; }\n\n        /// <summary>\n        /// Constructor for EssentialsRoomEmergencyBase\n        /// </summary>\n        /// <param name=\"key\">key of the room</param>\n        public EssentialsRoomEmergencyBase(string key)\n        {\n            Key = key;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Room.Config;\nusing PepperDash.Essentials.Core.Devices;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Describes the basic functionality of an EssentialsRoom\n    /// </summary>\n    public interface IEssentialsRoom : IKeyName, IReconfigurableDevice, IRunDefaultPresentRoute, IEnvironmentalControls\n    {\n        /// <summary>\n        /// Gets the PowerFeedback\n        /// </summary>\n        BoolFeedback OnFeedback { get; }        \n\n        /// <summary>\n        /// Gets the IsOccupiedFeedback\n        /// </summary>\n        BoolFeedback IsWarmingUpFeedback { get; }\n\n        /// <summary>\n        /// Gets the IsCoolingDownFeedback\n        /// </summary>\n        BoolFeedback IsCoolingDownFeedback { get; }        \n\n        /// <summary>\n        /// Gets a value indicating whether mobile control is enabled for this room\n        /// </summary>\n        bool IsMobileControlEnabled { get; }\n\n        /// <summary>\n        /// Gets the MobileControlRoomBridge\n        /// </summary>\n        IMobileControlRoomMessenger MobileControlRoomBridge { get; }\n\n        /// <summary>\n        /// Gets the SourceListKey\n        /// </summary>\n        string SourceListKey { get; }\n\n        /// <summary>\n        /// Gets the DestinationListKey\n        /// </summary>\n        string DestinationListKey { get; }\n\n        /// <summary>\n        /// Gets the AudioControlPointListKey\n        /// </summary>\n        string AudioControlPointListKey { get; }\n\n        /// <summary>\n        /// Gets the CameraListKey\n        /// </summary>\n        string CameraListKey { get; }\n\n        /// <summary>\n        /// Gets the ShutdownPromptTimer\n        /// </summary>\n        SecondsCountdownTimer ShutdownPromptTimer { get; }\n\n        /// <summary>\n        /// Gets the ShutdownVacancyTimer\n        /// </summary>\n        int ShutdownPromptSeconds { get; }\n\n        /// <summary>\n        /// Gets the ShutdownVacancySeconds\n        /// </summary>\n        int ShutdownVacancySeconds { get; }\n\n        /// <summary>\n        /// Gets the ShutdownType\n        /// </summary>\n        eShutdownType ShutdownType { get; }      \n\n        /// <summary>\n        /// Gets the LogoUrlLightBkgnd\n        /// </summary>\n        string LogoUrlLightBkgnd { get; }\n\n        /// <summary>\n        /// Gets the LogoUrlDarkBkgnd\n        /// </summary>\n        string LogoUrlDarkBkgnd { get; }\n\n        /// <summary>\n        /// Starts the shutdown process\n        /// </summary>\n        /// <param name=\"type\">type of shutdown event</param>\n        void StartShutdown(eShutdownType type);        \n\n        /// <summary>\n        /// Shuts down the room\n        /// </summary>\n        void Shutdown();        \n\n        /// <summary>\n        /// Powers on the room to either the default source or the last source used\n        /// </summary>\n        void PowerOnToDefaultOrLastSource();               \n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/IRoomEventSchedule.cs",
    "content": "﻿using PepperDash.Essentials.Room.Config;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoomEventSchedule\n    /// </summary>\n    public interface IRoomEventSchedule\n    {\n        /// <summary>\n        /// Adds or updates a scheduled event\n        /// </summary>\n        /// <param name=\"eventConfig\"></param>\n        void AddOrUpdateScheduledEvent(ScheduledEventConfig eventConfig);\n\n        /// <summary>\n        /// Removes a scheduled event by its key\n        /// </summary>\n        /// <returns></returns>\n        List<ScheduledEventConfig> GetScheduledEvents();\n\n        /// <summary>\n        /// Removes a scheduled event by its key\n        /// </summary>\n        event EventHandler<ScheduledEventEventArgs> ScheduledEventsChanged;\n    }\n\n    /// <summary>\n    /// Represents a ScheduledEventEventArgs\n    /// </summary>\n    public class ScheduledEventEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the ScheduledEvents\n        /// </summary>\n        public List<ScheduledEventConfig> ScheduledEvents;\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Interfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// For rooms with in call feedback\n    /// </summary>\n    public interface IHasInCallFeedback\n    {\n        /// <summary>\n        /// Gets the InCallFeedback\n        /// </summary>\n        BoolFeedback InCallFeedback { get; }\n    }\n\n    /// <summary>\n    /// For rooms with a single display\n    /// </summary>\n    public interface IHasDefaultDisplay\n    {\n        /// <summary>\n        /// Gets the DefaultDisplay\n        /// </summary>\n        IRoutingSink DefaultDisplay { get; }\n    }\n\n    /// <summary>\n    /// For rooms with multiple displays\n    /// </summary>\n    [Obsolete(\"Will be removed in a future version\")]\n    public interface IHasMultipleDisplays\n    {\n        /// <summary>\n        /// Gets the Displays dictionary\n        /// </summary>\n        Dictionary<eSourceListItemDestinationTypes, IRoutingSink> Displays { get; }\n    }\n\n    /// <summary>\n    /// For rooms with routing\n    /// </summary>\n    public interface IRunRouteAction\n    {\n        /// <summary>\n        /// Runs a route action\n        /// </summary>\n        /// <param name=\"routeKey\"></param>\n        /// <param name=\"sourceListKey\"></param>\n        void RunRouteAction(string routeKey, string sourceListKey);\n\n        /// <summary>\n        /// Runs a route action with a success callback\n        /// </summary>\n        /// <param name=\"routeKey\"></param>\n        /// <param name=\"sourceListKey\"></param>\n        /// <param name=\"successCallback\"></param>\n        void RunRouteAction(string routeKey, string sourceListKey, Action successCallback);\n    }\n\n    /// <summary>\n    /// Defines the contract for IRunDirectRouteAction\n    /// </summary>\n    public interface IRunDirectRouteAction\n    {\n        /// <summary>\n        /// Runs a direct route\n        /// </summary>\n        /// <param name=\"sourceKey\"></param>\n        /// <param name=\"destinationKey\"></param>\n        /// <param name=\"type\"></param>\n        void RunDirectRoute(string sourceKey, string destinationKey, eRoutingSignalType type = eRoutingSignalType.AudioVideo);\n    }\n\n    /// <summary>\n    /// Describes a room with matrix routing\n    /// </summary>\n    public interface IHasMatrixRouting\n    {\n        /// <summary>\n        /// Gets the MatrixRoutingDeviceKey\n        /// </summary>\n        string MatrixRoutingDeviceKey { get; }\n\n        /// <summary>\n        /// Gets the EndpointKeys\n        /// </summary>\n        List<string> EndpointKeys { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IHasRoutingEndpoints\n    /// </summary>\n    public interface IHasRoutingEndpoints\n    {\n        /// <summary>\n        /// Gets the EndpointKeys\n        /// </summary>\n        List<string> EndpointKeys { get; }\n    }\n\n    /// <summary>\n    /// Describes a room with a shutdown prompt timer\n    /// </summary>\n    public interface IShutdownPromptTimer\n    {\n        /// <summary>\n        /// Gets the ShutdownPromptTimer\n        /// </summary>\n        SecondsCountdownTimer ShutdownPromptTimer { get; }\n\n        /// <summary>\n        /// Gets the ShutdownPromptSeconds\n        /// </summary>\n        /// <param name=\"seconds\">number of seconds to set</param>\n        void SetShutdownPromptSeconds(int seconds);\n\n        /// <summary>\n        /// Starts the shutdown process\n        /// </summary>\n        /// <param name=\"type\">type of shutdown event</param>\n        void StartShutdown(eShutdownType type);\n    }\n\n    /// <summary>\n    /// Defines the contract for ITechPassword\n    /// </summary>\n    public interface ITechPassword\n    {\n        /// <summary>\n        /// Event fired when tech password validation result is available\n        /// </summary>\n        event EventHandler<TechPasswordEventArgs> TechPasswordValidateResult;\n\n        /// <summary>\n        /// Event fired when tech password is changed\n        /// </summary>\n        event EventHandler<EventArgs> TechPasswordChanged;\n\n        /// <summary>\n        /// Gets the TechPasswordLength\n        /// </summary>\n        int TechPasswordLength { get; }\n\n        /// <summary>\n        /// Validates the tech password\n        /// </summary>\n        /// <param name=\"password\">The tech password to validate</param>\n        void ValidateTechPassword(string password);\n\n        /// <summary>\n        /// Sets the tech password\n        /// </summary>\n        /// <param name=\"oldPassword\">The current tech password</param>\n        /// <param name=\"newPassword\">The new tech password to set</param>\n        void SetTechPassword(string oldPassword, string newPassword);\n    }\n\n    /// <summary>\n    /// Represents a TechPasswordEventArgs\n    /// </summary>\n    public class TechPasswordEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the IsValid\n        /// </summary>\n        public bool IsValid { get; private set; }\n\n        /// <summary>\n        /// Constructor for TechPasswordEventArgs\n        /// </summary>\n        /// <param name=\"isValid\"></param>\n        public TechPasswordEventArgs(bool isValid)\n        {\n            IsValid = isValid;\n        }\n    }\n\n    /// <summary>\n    /// Defines the contract for IRunDefaultPresentRoute\n    /// </summary>\n    public interface IRunDefaultPresentRoute\n    {\n        /// <summary>\n        /// Runs the default present route\n        /// </summary>\n        /// <returns></returns>\n        bool RunDefaultPresentRoute();\n    }\n\n    /// <summary>\n    /// For rooms that have default presentation and calling routes\n    /// </summary>\n    public interface IRunDefaultCallRoute : IRunDefaultPresentRoute\n    {\n        /// <summary>\n        /// Runs the default call route\n        /// </summary>\n        /// <returns></returns>\n        bool RunDefaultCallRoute();\n    }\n\n    /// <summary>\n    /// Describes environmental controls available on a room such as lighting, shades, temperature, etc.\n    /// </summary>\n    public interface IEnvironmentalControls\n    {\n        /// <summary>\n        /// Gets the EnvironmentalControlDevices\n        /// </summary>\n        List<EssentialsDevice> EnvironmentalControlDevices { get; }\n\n        /// <summary>\n        /// Gets a value indicating whether the room has environmental control devices\n        /// </summary>\n        bool HasEnvironmentalControlDevices { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IRoomOccupancy\n    /// </summary>\n    public interface IRoomOccupancy : IKeyed\n    {\n        /// <summary>\n        /// Gets the RoomOccupancy\n        /// </summary>\n        IOccupancyStatusProvider RoomOccupancy { get; }\n\n        /// <summary>\n        /// Gets a value indicating whether the OccupancyStatusProviderIsRemote\n        /// </summary>\n        bool OccupancyStatusProviderIsRemote { get; }\n\n        /// <summary>\n        /// Sets the room occupancy\n        /// </summary>\n        /// <param name=\"statusProvider\"></param>\n        /// <param name=\"timeoutMinutes\"></param>\n        void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes);\n\n        /// <summary>\n        /// Called when the room has been vacated for the timeout period\n        /// </summary>\n        /// <param name=\"o\"></param>\n        void RoomVacatedForTimeoutPeriod(object o);\n\n        /// <summary>\n        /// Starts the room vacancy timer\n        /// </summary>\n        /// <param name=\"mode\">vacancy mode</param>\n        void StartRoomVacancyTimer(eVacancyMode mode);\n\n        /// <summary>\n        /// Gets the VacancyMode\n        /// </summary>\n        eVacancyMode VacancyMode { get; }\n\n        /// <summary>\n        /// Event fired when room occupancy is set\n        /// </summary>\n        event EventHandler<EventArgs> RoomOccupancyIsSet;\n    }\n\n    /// <summary>\n    /// Defines the contract for IEmergency\n    /// </summary>\n    public interface IEmergency\n    {\n        /// <summary>\n        /// Gets the Emergency\n        /// </summary>\n        EssentialsRoomEmergencyBase Emergency { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IMicrophonePrivacy\n    /// </summary>\n    public interface IMicrophonePrivacy\n    {\n        /// <summary>\n        /// Gets the MicrophonePrivacy\n        /// </summary>\n        Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IHasAccessoryDevices\n    /// </summary>\n    public interface IHasAccessoryDevices : IKeyName\n    {\n        /// <summary>\n        /// Gets the AccessoryDeviceKeys\n        /// </summary>\n        List<string> AccessoryDeviceKeys { get; }\n    }\n\n    /// <summary>\n    /// Defines the contract for IHasCiscoNavigatorTouchpanel\n    /// </summary>\n    public interface IHasCiscoNavigatorTouchpanel\n    {\n        /// <summary>\n        /// Gets the CiscoNavigatorTouchpanelKey\n        /// </summary>\n        string CiscoNavigatorTouchpanelKey { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/Room.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\t//***************************************************************************************************\n\n\t/// <summary>\n\t/// Abstract base class for Room\n\t/// </summary>\n\tpublic abstract class Room : Device, IHasFeedback\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the RoomIsOnFeedback\n\t\t/// </summary>\n\t\tpublic abstract BoolFeedback RoomIsOnFeedback { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IsCoolingDownFeedback\n\t\t/// </summary>\n\t\tpublic abstract BoolFeedback IsCoolingDownFeedback { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IsWarmingUpFeedback\n\t\t/// </summary>\n\t\tpublic abstract BoolFeedback IsWarmingUpFeedback { get; protected set; }\n\n\t\t// In concrete classes, these should be computed from the relevant devices\n\t\t/// <summary>\n\t\t/// Gets or sets the CooldownTime\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic virtual uint CooldownTime { get { return 10000; } }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the WarmupTime\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic virtual uint WarmupTime { get { return 5000; } }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Description\n\t\t/// </summary>\n\t\tpublic string Description { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the HelpMessage\n\t\t/// </summary>\n\t\tpublic string HelpMessage { get; set; }\n\n\t\t/// <summary>\n\t\t/// Room Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\">room key</param>\n\t\t/// <param name=\"name\">room name</param>\n\t\tpublic Room(string key, string name)\n\t\t\t: base(key, name)\n\t\t{\n\t\t\tDescription = \"\";\n\t\t\tHelpMessage = \"\";\n\t\t}\n\n  /// <summary>\n  /// RoomOn method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic virtual void RoomOn() { }\n\n  /// <summary>\n  /// RoomOff method\n  /// </summary>\n\t\tpublic virtual void RoomOff() { }\n\n\t\t#region IDeviceWithOutputs Members\n\n\t\t/// <summary>\n\t\t/// Gets the Feedbacks\n\t\t/// </summary>\n\t\tpublic virtual FeedbackCollection<Feedback> Feedbacks\n\t\t{\n\t\t\tget\n\t\t\t{\n                return new FeedbackCollection<Feedback>\n                {\n                    RoomIsOnFeedback,\n                    IsCoolingDownFeedback,\n                    IsWarmingUpFeedback\n                };\n\t\t\t}\n\t\t}\n\n\t\t#endregion\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Room/iOccupancyStatusProvider.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IOccupancyStatusProvider\n    /// </summary>\n    public interface IOccupancyStatusProvider\n    {\n        /// <summary>\n        /// Gets the RoomIsOccupiedFeedback\n        /// </summary>\n        BoolFeedback RoomIsOccupiedFeedback { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/DummyRoutingInputsDevice.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.Routing\n{\n /// <summary>\n /// Represents a DummyRoutingInputsDevice\n /// </summary>\n\tpublic class DummyRoutingInputsDevice : Device, IRoutingSource, IRoutingOutputs\n\t{\n  /// <summary>\n  /// Gets or sets the AudioVideoOutputPort\n  /// </summary>\n\t\tpublic RoutingOutputPort AudioVideoOutputPort { get; private set; }\n\n\t\t/// <summary>\n\t\t/// contains the output port\n\t\t/// </summary>\n\t\tpublic RoutingPortCollection<RoutingOutputPort> OutputPorts\n\t\t{\n\t\t\tget { return new RoutingPortCollection<RoutingOutputPort>() { AudioVideoOutputPort }; }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\">key for special device</param>\n\t\tpublic DummyRoutingInputsDevice(string key) : base(key)\n\t\t{\n\t\t\tAudioVideoOutputPort = new RoutingOutputPort(\"internal\", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.BackplaneOnly,\n\t\t\t\tnull, this, true);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/Extensions.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing PepperDash.Essentials.Core.Queues;\nusing PepperDash.Essentials.Core.Routing;\nusing Debug = PepperDash.Core.Debug;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\n    /// <summary>\n    /// Extensions added to any IRoutingInputs classes to provide discovery-based routing\n    /// on those destinations.\n    /// </summary>\n    public static class Extensions\n    {\n\n        /// <summary>\n        /// A collection of RouteDescriptors for each signal type.\n        /// </summary>\n        public static readonly Dictionary<eRoutingSignalType, RouteDescriptorCollection> RouteDescriptors = new Dictionary<eRoutingSignalType, RouteDescriptorCollection>()\n        {\n            { eRoutingSignalType.Audio, new RouteDescriptorCollection() },\n            { eRoutingSignalType.Video, new RouteDescriptorCollection() },\n            { eRoutingSignalType.SecondaryAudio, new RouteDescriptorCollection() },\n            { eRoutingSignalType.AudioVideo, new RouteDescriptorCollection() },\n            { eRoutingSignalType.UsbInput, new RouteDescriptorCollection() },\n            { eRoutingSignalType.UsbOutput, new RouteDescriptorCollection() }\n        };\n\n        /// <summary>\n        /// Stores pending route requests, keyed by the destination device key.\n        /// Used primarily to handle routing requests while a device is cooling down.\n        /// </summary>\n        private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();\n\n        /// <summary>\n        /// A queue to process route requests and releases sequentially.\n        /// </summary>\n        private static readonly GenericQueue routeRequestQueue = new GenericQueue(\"routingQueue\");\n\n        /// <summary>\n        /// Indexed lookup of TieLines by destination device key for faster queries.\n        /// </summary>\n        private static Dictionary<string, List<TieLine>> _tieLinesByDestination;\n\n        /// <summary>\n        /// Indexed lookup of TieLines by source device key for faster queries.\n        /// </summary>\n        private static Dictionary<string, List<TieLine>> _tieLinesBySource;\n\n        /// <summary>\n        /// Cache of failed route attempts to avoid re-checking impossible paths.\n        /// Format: \"sourceKey|destKey|signalType\"\n        /// Uses ConcurrentDictionary as a thread-safe set (byte value is unused).\n        /// </summary>\n        private static readonly ConcurrentDictionary<string, byte> _impossibleRoutes = new ConcurrentDictionary<string, byte>();\n\n        /// <summary>\n        /// Indexes all TieLines by source and destination device keys for faster lookups.\n        /// Should be called once at system startup after all TieLines are created.\n        /// </summary>\n        public static void IndexTieLines()\n        {\n            try\n            {\n                Debug.LogInformation(\"Indexing TieLines for faster route discovery\");\n\n                _tieLinesByDestination = TieLineCollection.Default\n                    .GroupBy(t => t.DestinationPort.ParentDevice.Key)\n                    .ToDictionary(g => g.Key, g => g.ToList());\n\n                _tieLinesBySource = TieLineCollection.Default\n                    .GroupBy(t => t.SourcePort.ParentDevice.Key)\n                    .ToDictionary(g => g.Key, g => g.ToList());\n\n                Debug.LogInformation(\"TieLine indexing complete. {0} destination keys, {1} source keys\",\n                    _tieLinesByDestination.Count, _tieLinesBySource.Count);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(\"Exception indexing TieLines: {exception}\", ex.Message);\n                Debug.LogDebug(ex, \"Stack Trace: \");\n            }\n        }\n\n        /// <summary>\n        /// Gets TieLines connected to a destination device.\n        /// Uses indexed lookup if available, otherwise falls back to LINQ query.\n        /// </summary>\n        /// <param name=\"destinationKey\">The destination device key</param>\n        /// <returns>List of TieLines connected to the destination</returns>\n        private static IEnumerable<TieLine> GetTieLinesForDestination(string destinationKey)\n        {\n            if (_tieLinesByDestination != null && _tieLinesByDestination.TryGetValue(destinationKey, out List<TieLine> tieLines))\n            {\n                return tieLines;\n            }\n\n            // Fallback to LINQ if index not available\n            return TieLineCollection.Default.Where(t => t.DestinationPort.ParentDevice.Key == destinationKey);\n        }\n\n        /// <summary>\n        /// Gets TieLines connected to a source device.\n        /// Uses indexed lookup if available, otherwise falls back to LINQ query.\n        /// </summary>\n        /// <param name=\"sourceKey\">The source device key</param>\n        /// <returns>List of TieLines connected to the source</returns>\n        private static IEnumerable<TieLine> GetTieLinesForSource(string sourceKey)\n        {\n            if (_tieLinesBySource != null && _tieLinesBySource.TryGetValue(sourceKey, out List<TieLine> tieLines))\n            {\n                return tieLines;\n            }\n\n            // Fallback to LINQ if index not available\n            return TieLineCollection.Default.Where(t => t.SourcePort.ParentDevice.Key == sourceKey);\n        }\n\n        /// <summary>\n        /// Creates a cache key for route impossibility tracking.\n        /// </summary>\n        /// <param name=\"sourceKey\">Source device key</param>\n        /// <param name=\"destKey\">Destination device key</param>\n        /// <param name=\"sourcePortKey\">Source port key</param>\n        /// <param name=\"destinationPortKey\">Destination port key</param>\n        /// <param name=\"type\">Signal type</param>\n        /// <returns>Cache key string</returns>\n        private static string GetRouteKey(string sourceKey, string destKey, string sourcePortKey, string destinationPortKey, eRoutingSignalType type)\n        {\n            return $\"{sourceKey}|{destKey}|{sourcePortKey}|{destinationPortKey}|{type}\";\n        }\n\n        /// <summary>\n        /// Clears the impossible routes cache. Should be called if TieLines are added/removed at runtime.\n        /// </summary>\n        public static void ClearImpossibleRoutesCache()\n        {\n            _impossibleRoutes.Clear();\n            Debug.LogInformation(\"Impossible routes cache cleared\");\n        }\n\n        /// <summary>\n        /// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute\n        /// and then attempts a new Route and if sucessful, stores that RouteDescriptor\n        /// in RouteDescriptorCollection.DefaultCollection\n        /// </summary>        \n        public static void ReleaseAndMakeRoute(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, string destinationPortKey = \"\", string sourcePortKey = \"\")\n        {\n            // Remove this line before committing!!!!!\n            var frame = new StackFrame(1, true);\n            Debug.LogInformation(\"ReleaseAndMakeRoute Called from {method} with params {destinationKey}:{sourceKey}:{signalType}:{destinationPortKey}:{sourcePortKey}\", frame.GetMethod().Name, destination.Key, source.Key, signalType.ToString(), destinationPortKey, sourcePortKey);\n\n            var inputPort = string.IsNullOrEmpty(destinationPortKey) ? null : destination.InputPorts.FirstOrDefault(p => p.Key == destinationPortKey);\n            var outputPort = string.IsNullOrEmpty(sourcePortKey) ? null : source.OutputPorts.FirstOrDefault(p => p.Key == sourcePortKey);\n\n            ReleaseAndMakeRoute(destination, source, signalType, inputPort, outputPort);\n        }\n\n        /// <summary>\n        /// Will release the existing route to the destination, if a route is found. This does not CLEAR the route, only stop counting usage time on any output ports that have a usage tracker set.\n        /// </summary>\n        /// <param name=\"destination\">destination to clear</param>\n        public static void ReleaseRoute(this IRoutingInputs destination)\n        {\n            routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty, false));\n        }\n\n        /// <summary>\n        /// Will release the existing route to the destination, if a route is found. This does not CLEAR the route, only stop counting usage time on any output ports that have a usage tracker set\n        /// </summary>\n        /// <param name=\"destination\">destination to clear</param>\n        /// <param name=\"inputPortKey\">Input to use to find existing route</param>\n        /// <summary>\n        /// ReleaseRoute method\n        /// </summary>\n        public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey)\n        {\n            routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey, false));\n        }\n\n        /// <summary>\n        /// Clears the route on the destination.  This will remove any routes that are currently in use\n        /// </summary>\n        /// <param name=\"destination\">Destination</param>\n        public static void ClearRoute(this IRoutingInputs destination)\n        {\n            routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty, true));\n        }\n\n        /// <summary>\n        /// Clears the route on the destination.  This will remove any routes that are currently in use\n        /// </summary>\n        /// <param name=\"destination\">destination</param>\n        /// <param name=\"inputPortKey\">input to use to find existing route</param>\n        /// <summary>\n        /// ClearRoute method\n        /// </summary>\n        public static void ClearRoute(this IRoutingInputs destination, string inputPortKey)\n        {\n            routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey, true));\n        }\n\n        /// <summary>\n        /// Removes the route request for the destination.  This will remove any routes that are currently in use\n        /// </summary>\n        /// <param name=\"destinationKey\">destination device key</param>\n        public static void RemoveRouteRequestForDestination(string destinationKey)\n        {\n            Debug.LogInformation(\"Removing route request for {destination}\", destinationKey);\n\n            var result = RouteRequests.Remove(destinationKey);\n\n            var messageTemplate = result ? \"Route Request for {destination} removed\" : \"Route Request for {destination} not found\";\n\n            Debug.LogInformation(messageTemplate, destinationKey);\n        }\n\n        /// <summary>\n        /// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.  \n        /// Routes of type AudioVideo will be built as two separate routes, audio and video. If\n        /// a route is discovered, a new RouteDescriptor is returned.  If one or both parts\n        /// of an audio/video route are discovered a route descriptor is returned.  If no route is \n        /// discovered, then null is returned\n        /// </summary>\n        public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)\n        {\n            // if it's a single signal type, find the route\n            if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) &&\n                !(signalType.HasFlag(eRoutingSignalType.Video) && signalType.HasFlag(eRoutingSignalType.SecondaryAudio)))\n            {\n                var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, sourcePort, signalType);\n                Debug.LogDebug(destination, \"Attempting to build source route from {sourceKey} of type {type}\", source.Key, signalType);\n\n                if (!destination.GetRouteToSource(source, null, null, signalType, 0, singleTypeRouteDescriptor, destinationPort, sourcePort))\n                    singleTypeRouteDescriptor = null;\n\n                var routes = singleTypeRouteDescriptor?.Routes ?? new List<RouteSwitchDescriptor>();\n                foreach (var route in routes)\n                {\n                    Debug.LogVerbose(destination, \"Route for device: {route}\", route.ToString());\n                }\n\n                return (singleTypeRouteDescriptor, null);\n            }\n            // otherwise, audioVideo needs to be handled as two steps.\n\n            Debug.LogDebug(destination, \"Attempting to build source route from {destinationKey} to {sourceKey} of type {type}\", source.Key, signalType);\n\n            RouteDescriptor audioRouteDescriptor;\n\n            if (signalType.HasFlag(eRoutingSignalType.SecondaryAudio))\n            {\n                audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, sourcePort, eRoutingSignalType.SecondaryAudio);\n            }\n            else\n            {\n                audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, sourcePort, eRoutingSignalType.Audio);\n            }\n\n            var audioSuccess = destination.GetRouteToSource(source, null, null, signalType.HasFlag(eRoutingSignalType.SecondaryAudio) ? eRoutingSignalType.SecondaryAudio : eRoutingSignalType.Audio, 0, audioRouteDescriptor, destinationPort, sourcePort);\n\n            if (!audioSuccess)\n                Debug.LogDebug(destination, \"Cannot find audio route to {0}\", source.Key);\n\n            var videoRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, sourcePort, eRoutingSignalType.Video);\n\n            var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, videoRouteDescriptor, destinationPort, sourcePort);\n\n            if (!videoSuccess)\n                Debug.LogDebug(destination, \"Cannot find video route to {0}\", source.Key);\n\n            foreach (var route in audioRouteDescriptor.Routes)\n            {\n                Debug.LogVerbose(destination, \"Audio route for device: {route}\", route.ToString());\n            }\n\n            foreach (var route in videoRouteDescriptor.Routes)\n            {\n                Debug.LogVerbose(destination, \"Video route for device: {route}\", route.ToString());\n            }\n\n\n            if (!audioSuccess && !videoSuccess)\n                return (null, null);\n\n            // Return null for descriptors that have no routes\n            return (audioSuccess && audioRouteDescriptor.Routes.Count > 0 ? audioRouteDescriptor : null,\n                    videoSuccess && videoRouteDescriptor.Routes.Count > 0 ? videoRouteDescriptor : null);\n        }\n\n        /// <summary>\n        /// Internal method to handle the logic for releasing an existing route and making a new one.\n        /// Handles devices with cooling states by queueing the request.\n        /// </summary>\n        /// <param name=\"destination\">The destination device.</param>\n        /// <param name=\"source\">The source device.</param>\n        /// <param name=\"signalType\">The type of signal to route.</param>\n        /// <param name=\"destinationPort\">The specific destination input port (optional).</param>\n        /// <param name=\"sourcePort\">The specific source output port (optional).</param>\n        private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null)\n        {\n            if (destination == null) throw new ArgumentNullException(nameof(destination));\n            if (source == null) throw new ArgumentNullException(nameof(source));\n            if (destinationPort == null) Debug.LogDebug(\"Destination port is null\");\n            if (sourcePort == null) Debug.LogDebug(\"Source port is null\");\n\n            var routeRequest = new RouteRequest\n            {\n                Destination = destination,\n                DestinationPort = destinationPort,\n                Source = source,\n                SourcePort = sourcePort,\n                SignalType = signalType\n            };\n\n            var coolingDevice = destination as IWarmingCooling;\n\n            //We already have a route request for this device, and it's a cooling device and is cooling\n            if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)\n            {\n                coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;\n\n                coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;\n\n                RouteRequests[destination.Key] = routeRequest;\n\n                Debug.LogInformation(\"Device: {destination} is cooling down and already has a routing request stored.  Storing new route request to route to source key: {sourceKey}\", destination.Key, routeRequest.Source.Key);\n\n                return;\n            }\n\n            //New Request\n            if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)\n            {\n                coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;\n\n                RouteRequests.Add(destination.Key, routeRequest);\n\n                Debug.LogInformation(\"Device: {destination} is cooling down. Storing route request to route to source key: {sourceKey}\", destination.Key, routeRequest.Source.Key);\n                return;\n            }\n\n            if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)\n            {\n                var handledRequest = RouteRequests[destination.Key];\n\n                coolingDevice.IsCoolingDownFeedback.OutputChange -= handledRequest.HandleCooldown;\n\n                RouteRequests.Remove(destination.Key);\n\n                Debug.LogInformation(\"Device: {destination} is NOT cooling down.  Removing stored route request and routing to source key: {sourceKey}\", destination.Key, routeRequest.Source.Key);\n            }\n\n            routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, destinationPort?.Key ?? string.Empty, false));\n\n            routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));\n        }\n\n        /// <summary>\n        /// Maps destination input ports to source output ports for all routing devices.\n        /// </summary>\n        public static void MapDestinationsToSources()\n        {\n            try\n            {\n                // Index TieLines before mapping if not already done\n                if (_tieLinesByDestination == null || _tieLinesBySource == null)\n                {\n                    IndexTieLines();\n                }\n\n                var sinks = DeviceManager.AllDevices.OfType<IRoutingInputs>().Where(d => !(d is IRoutingInputsOutputs));\n                var sources = DeviceManager.AllDevices.OfType<IRoutingOutputs>().Where(d => !(d is IRoutingInputsOutputs));\n\n                foreach (var sink in sinks)\n                {\n                    foreach (var source in sources)\n                    {\n                        foreach (var inputPort in sink.InputPorts)\n                        {\n                            foreach (var outputPort in source.OutputPorts)\n                            {\n                                var (audioOrSingleRoute, videoRoute) = sink.GetRouteToSource(source, inputPort.Type, inputPort, outputPort);\n\n                                if (audioOrSingleRoute == null && videoRoute == null)\n                                {\n                                    continue;\n                                }\n\n                                if (audioOrSingleRoute != null)\n                                {\n                                    // Only add routes that have actual switching steps\n                                    if (audioOrSingleRoute.Routes == null || audioOrSingleRoute.Routes.Count == 0)\n                                    {\n                                        continue;\n                                    }\n\n                                    // Add to the appropriate collection(s) based on signal type\n                                    // Note: A single route descriptor with combined flags (e.g., AudioVideo) will be added once per matching signal type\n                                    if (audioOrSingleRoute.SignalType.HasFlag(eRoutingSignalType.Audio))\n                                    {\n                                        RouteDescriptors[eRoutingSignalType.Audio].AddRouteDescriptor(audioOrSingleRoute);\n                                    }\n                                    if (audioOrSingleRoute.SignalType.HasFlag(eRoutingSignalType.Video))\n                                    {\n                                        RouteDescriptors[eRoutingSignalType.Video].AddRouteDescriptor(audioOrSingleRoute);\n                                    }\n                                    if (audioOrSingleRoute.SignalType.HasFlag(eRoutingSignalType.SecondaryAudio))\n                                    {\n                                        RouteDescriptors[eRoutingSignalType.SecondaryAudio].AddRouteDescriptor(audioOrSingleRoute);\n                                    }\n                                    if (audioOrSingleRoute.SignalType.HasFlag(eRoutingSignalType.UsbInput))\n                                    {\n                                        RouteDescriptors[eRoutingSignalType.UsbInput].AddRouteDescriptor(audioOrSingleRoute);\n                                    }\n                                    if (audioOrSingleRoute.SignalType.HasFlag(eRoutingSignalType.UsbOutput))\n                                    {\n                                        RouteDescriptors[eRoutingSignalType.UsbOutput].AddRouteDescriptor(audioOrSingleRoute);\n                                    }\n                                }\n                                if (videoRoute != null)\n                                {\n                                    // Only add routes that have actual switching steps\n                                    if (videoRoute.Routes == null || videoRoute.Routes.Count == 0)\n                                    {\n                                        continue;\n                                    }\n\n                                    RouteDescriptors[eRoutingSignalType.Video].AddRouteDescriptor(videoRoute);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(\"Exception mapping routes: {exception}\", ex.Message);\n                Debug.LogDebug(ex, \"Stack Trace: \");\n            }\n        }\n\n        /// <summary>\n        /// Executes the actual routing based on a <see cref=\"RouteRequest\"/>.\n        /// Finds the route path, adds it to the collection, and executes the switches.\n        /// </summary>\n        /// <param name=\"request\">The route request details.</param>\n        private static void RunRouteRequest(RouteRequest request)\n        {\n            try\n            {\n                if (request.Source == null)\n                    return;\n\n                RouteDescriptor audioOrSingleRoute = null;\n                RouteDescriptor videoRoute = null;\n\n                // Try to use pre-loaded route descriptors first\n                if (request.SignalType.HasFlag(eRoutingSignalType.AudioVideo))\n                {\n                    // For AudioVideo routes, check both Audio and Video collections\n                    if (RouteDescriptors.TryGetValue(eRoutingSignalType.Audio, out RouteDescriptorCollection audioCollection))\n                    {\n                        audioOrSingleRoute = audioCollection.Descriptors.FirstOrDefault(d =>\n                            d.Source.Key == request.Source.Key &&\n                            d.Destination.Key == request.Destination.Key &&\n                            (request.DestinationPort == null || d.InputPort?.Key == request.DestinationPort.Key) &&\n                            (request.SourcePort == null || d.OutputPort?.Key == request.SourcePort.Key));\n                    }\n\n                    if (RouteDescriptors.TryGetValue(eRoutingSignalType.Video, out RouteDescriptorCollection videoCollection))\n                    {\n                        videoRoute = videoCollection.Descriptors.FirstOrDefault(d =>\n                            d.Source.Key == request.Source.Key &&\n                            d.Destination.Key == request.Destination.Key &&\n                            (request.DestinationPort == null || d.InputPort?.Key == request.DestinationPort.Key) &&\n                            (request.SourcePort == null || d.OutputPort?.Key == request.SourcePort.Key));\n                    }\n                }\n                else\n                {\n                    // For single signal type routes\n                    var signalTypeToCheck = request.SignalType.HasFlag(eRoutingSignalType.SecondaryAudio)\n                        ? eRoutingSignalType.SecondaryAudio\n                        : request.SignalType;\n\n                    if (RouteDescriptors.TryGetValue(signalTypeToCheck, out RouteDescriptorCollection collection))\n                    {\n                        audioOrSingleRoute = collection.Descriptors.FirstOrDefault(d =>\n                            d.Source.Key == request.Source.Key &&\n                            d.Destination.Key == request.Destination.Key &&\n                            (request.DestinationPort == null || d.InputPort?.Key == request.DestinationPort.Key) &&\n                            (request.SourcePort == null || d.OutputPort?.Key == request.SourcePort.Key));\n                    }\n                }\n\n                // If no pre-loaded route found, build it dynamically\n                if (audioOrSingleRoute == null && videoRoute == null)\n                {\n                    Debug.LogDebug(request.Destination, \"No pre-loaded route found, building dynamically\");\n                    (audioOrSingleRoute, videoRoute) = request.Destination.GetRouteToSource(request.Source, request.SignalType, request.DestinationPort, request.SourcePort);\n                }\n\n                if (audioOrSingleRoute == null && videoRoute == null)\n                    return;\n\n                RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(audioOrSingleRoute);\n\n                if (videoRoute != null)\n                {\n                    RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(videoRoute);\n                }\n\n                Debug.LogVerbose(request.Destination, \"Executing full route\");\n\n                audioOrSingleRoute.ExecuteRoutes();\n                videoRoute?.ExecuteRoutes();\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(\"Exception Running Route Request {request}: {exception}\", request, ex.Message);\n                Debug.LogDebug(ex, \"Stack Trace: \");\n            }\n        }\n\n        /// <summary>\n        /// Will release the existing route on the destination, if it is found in RouteDescriptorCollection.DefaultCollection\n        /// </summary>\n        /// <param name=\"destination\"></param>     \n        /// <param name=\"inputPortKey\"> The input port key to use to find the route.  If empty, will use the first available input port</param>\n        /// <param name=\"clearRoute\"> If true, will clear the route on the destination.  This will remove any routes that are currently in use</param>\n        private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey, bool clearRoute)\n        {\n            try\n            {\n                Debug.LogInformation(destination, \"Release route for '{destination}':'{inputPortKey}'\", destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? \"auto\" : inputPortKey);\n\n                if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)\n                {\n                    var coolingDevice = destination as IWarmingCooling;\n\n                    coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;\n                }\n\n                RouteRequests.Remove(destination.Key);\n\n                var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination, inputPortKey);\n                if (current != null)\n                {\n                    Debug.LogInformation(destination, \"Releasing current route: {0}\", current.Source.Key);\n                    current.ReleaseRoutes(clearRoute);\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(\"Exception releasing route for '{destination}':'{inputPortKey}': {exception}\", destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? \"auto\" : inputPortKey, ex.Message);\n                Debug.LogDebug(ex, \"Stack Trace: \");\n            }\n        }\n\n        /// <summary>\n        /// The recursive part of this.  Will stop on each device, search its inputs for the \n        /// desired source and if not found, invoke this function for the each input port\n        /// hoping to find the source.\n        /// </summary>\n        /// <param name=\"destination\"></param>\n        /// <param name=\"source\"></param>\n        /// <param name=\"outputPortToUse\">The RoutingOutputPort whose link is being checked for a route</param>\n        /// <param name=\"alreadyCheckedDevices\">Prevents Devices from being twice-checked</param>\n        /// <param name=\"signalType\">This recursive function should not be called with AudioVideo</param>\n        /// <param name=\"cycle\">Just an informational counter</param>\n        /// <param name=\"routeTable\">The RouteDescriptor being populated as the route is discovered</param>\n        /// <param name=\"destinationPort\">The RoutingOutputPort whose link is being checked for a route</param>\n        /// <param name=\"sourcePort\">The source output port (optional)</param>\n        /// <returns>true if source is hit</returns>\n        private static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,\n            RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,\n                eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)\n        {\n            cycle++;\n\n            // Check if this route has already been determined to be impossible\n            var routeKey = GetRouteKey(source.Key, destination.Key, sourcePort?.Key ?? \"auto\", destinationPort?.Key ?? \"auto\", signalType);\n            if (_impossibleRoutes.ContainsKey(routeKey))\n            {\n                Debug.LogVerbose(\"Route {0} is cached as impossible, skipping\", routeKey);\n                return false;\n            }\n\n            Debug.LogVerbose(\"GetRouteToSource: {cycle} {sourceKey}:{sourcePortKey}--> {destinationKey}:{destinationPortKey} {type}\", null, cycle, source.Key, sourcePort?.Key ?? \"auto\", destination.Key, destinationPort?.Key ?? \"auto\", signalType.ToString());\n\n            RoutingInputPort goodInputPort = null;\n\n            // Use indexed lookup instead of LINQ query\n            var allDestinationTieLines = GetTieLinesForDestination(destination.Key);\n\n            IEnumerable<TieLine> destinationTieLines;\n            TieLine directTie = null;\n\n            if (destinationPort == null)\n            {\n                destinationTieLines = allDestinationTieLines.Where(t =>\n                    t.Type.HasFlag(signalType) || signalType == eRoutingSignalType.AudioVideo);\n            }\n            else\n            {\n                destinationTieLines = allDestinationTieLines.Where(t =>\n                    t.DestinationPort.Key == destinationPort.Key && t.Type.HasFlag(signalType));\n            }\n\n            // find the TieLine without a port\n            if (destinationPort == null && sourcePort == null)\n            {\n                directTie = destinationTieLines.FirstOrDefault(t => t.SourcePort.ParentDevice.Key == source.Key);\n            }\n            // find a tieLine to a specific destination port without a specific source port\n            else if (destinationPort != null && sourcePort == null)\n            {\n                directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.Key == destinationPort.Key && t.SourcePort.ParentDevice.Key == source.Key);\n            }\n            // find a tieline to a specific source port without a specific destination port\n            else if (destinationPort == null & sourcePort != null)\n            {\n                directTie = destinationTieLines.FirstOrDefault(t => t.SourcePort.ParentDevice.Key == source.Key && t.SourcePort.Key == sourcePort.Key);\n            }\n            // find a tieline to a specific source port and destination port\n            else if (destinationPort != null && sourcePort != null)\n            {\n                directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.Key == destinationPort.Key && t.SourcePort.ParentDevice.Key == source.Key && t.SourcePort.Key == sourcePort.Key);\n            }\n\n            if (directTie != null) // Found a tie directly to the source\n            {\n                goodInputPort = directTie.DestinationPort;\n            }\n            else // no direct-connect.  Walk back devices.\n            {\n                Debug.LogVerbose(destination, \"is not directly connected to {sourceKey}. Walking down tie lines\", source.Key);\n\n                // No direct tie? Run back out on the inputs' attached devices... \n                // Only the ones that are routing devices\n                var midpointTieLines = destinationTieLines.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);\n\n                //Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration\n                if (alreadyCheckedDevices == null)\n                    alreadyCheckedDevices = new List<IRoutingInputsOutputs>();\n                alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);\n\n                foreach (var tieLine in midpointTieLines)\n                {\n                    var midpointDevice = tieLine.SourcePort.ParentDevice as IRoutingInputsOutputs;\n\n                    // Check if this previous device has already been walked\n                    if (alreadyCheckedDevices.Contains(midpointDevice))\n                    {\n                        Debug.LogVerbose(destination, \"Skipping input {midpointDeviceKey} on {destinationKey}, this was already checked\", midpointDevice.Key, destination.Key);\n                        continue;\n                    }\n\n                    var midpointOutputPort = tieLine.SourcePort;\n\n                    Debug.LogVerbose(destination, \"Trying to find route on {midpointDeviceKey}\", midpointDevice.Key);\n\n                    // haven't seen this device yet.  Do it.  Pass the output port to the next\n                    // level to enable switching on success\n                    var upstreamRoutingSuccess = midpointDevice.GetRouteToSource(source, midpointOutputPort,\n                        alreadyCheckedDevices, signalType, cycle, routeTable, null, sourcePort);\n\n                    if (upstreamRoutingSuccess)\n                    {\n                        Debug.LogVerbose(destination, \"Upstream device route found\");\n                        Debug.LogVerbose(destination, \"Route found on {midpointDeviceKey}\", midpointDevice.Key);\n                        Debug.LogVerbose(destination, \"TieLine: SourcePort: {SourcePort} DestinationPort: {DestinationPort}\", tieLine.SourcePort, tieLine.DestinationPort);\n                        goodInputPort = tieLine.DestinationPort;\n                        break; // Stop looping the inputs in this cycle\n                    }\n                }\n            }\n\n\n            if (goodInputPort == null)\n            {\n                Debug.LogVerbose(destination, \"No route found to {0}\", source.Key);\n\n                // Cache this as an impossible route\n                _impossibleRoutes.TryAdd(routeKey, 0);\n\n                return false;\n            }\n\n            // we have a route on corresponding inputPort. *** Do the route ***\n\n            if (destination is IRoutingSink)\n            {\n                // it's a sink device\n                routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));\n            }\n            else if (destination is IRouting)\n            {\n                routeTable.Routes.Add(new RouteSwitchDescriptor(outputPortToUse, goodInputPort));\n            }\n            else // device is merely IRoutingInputOutputs\n                Debug.LogVerbose(destination, \"No routing. Passthrough device\");\n\n            return true;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/ICardPortsDevice.cs",
    "content": "﻿//using System;\n//using System.Collections.Generic;\n//using System.Linq;\n//using System.Text;\n//using Crestron.SimplSharp;\n\n//using PepperDash.Core;\n\n//namespace PepperDash.Essentials.Core\n//{\n//    /// <summary>\n//    /// Defines a class that has cards, like a DM chassis controller, where\n//    /// we need to access ports on those cards\n//    /// </summary>\n//    public interface ICardPortsDevice : IKeyed\n//    {\n//        RoutingInputPort GetChildInputPort(string card, string port);\n//        RoutingOutputPort GetChildOutputPort(string card, string port);\n//    }\n//}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/ICurrentSources.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\n\r\nnamespace PepperDash.Essentials.Core.Routing\r\n{\r\n  /// <summary>\r\n  /// The current sources for the room, keyed by eRoutingSignalType.\r\n  /// This allows for multiple sources to be tracked, such as audio and video.\r\n  /// </summary>\r\n  /// <remarks>\r\n  /// This interface is used to provide access to the current sources in a room,\r\n  /// allowing for more complex routing scenarios where multiple signal types are involved.\r\n  /// </remarks>\r\n  public interface ICurrentSources\r\n  {\r\n    /// <summary>\r\n    /// Gets the current sources for the room, keyed by eRoutingSignalType.\r\n    /// This dictionary contains the current source for each signal type, such as audio, video, and control signals.\r\n    /// </summary>\r\n    Dictionary<eRoutingSignalType, SourceListItem> CurrentSources { get; }\r\n\r\n    /// <summary>\r\n    /// Gets the current source keys for the room, keyed by eRoutingSignalType.\r\n    /// This dictionary contains the keys for the current source for each signal type, such as audio, video, and control signals.\r\n    /// </summary>\r\n    Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; }\r\n\r\n    /// <summary>\r\n    /// Event raised when the current sources change.\r\n    /// </summary>\r\n    event EventHandler CurrentSourcesChanged;\r\n\r\n    /// <summary>\r\n    /// Sets the current source for a specific signal type.\r\n    /// This method updates the current source for the specified signal type and notifies any subscribers of the change.\r\n    /// </summary>\r\n    /// <param name=\"signalType\">The signal type to update.</param>\r\n    /// <param name=\"sourceListKey\">The key for the source list.</param>\r\n    /// <param name=\"sourceListItem\">The source list item to set as the current source.</param>\r\n    void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem);\r\n\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IHasCurrentSourceInfoChange.cs",
    "content": "﻿/* Unmerged change from project 'PepperDash.Essentials.Core (net6)'\nBefore:\nnamespace PepperDash.Essentials.Core.Routing.Interfaces\nAfter:\nusing PepperDash;\nusing PepperDash.Essentials;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Routing;\nusing PepperDash.Essentials.Core.Routing;\nusing PepperDash.Essentials.Core.Routing.Interfaces\n*/\nusing System;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Delegate for SourceInfoChangeHandler\n    /// </summary>\n    public delegate void SourceInfoChangeHandler(SourceListItem info, ChangeType type);\n    //*******************************************************************************************\n    // Interfaces\n\n    /// <summary>\n    /// For rooms with a single presentation source, change event\n    /// </summary>\n    [Obsolete(\"Use ICurrentSources instead\")]\n    public interface IHasCurrentSourceInfoChange\n    {\n        /// <summary>\n        /// The key for the current source info, used to look up the source in the SourceList\n        /// </summary>\n        string CurrentSourceInfoKey { get; set; }\n\n        /// <summary>\n        /// The current source info for the room, used to look up the source in the SourceList\n        /// </summary>\n        SourceListItem CurrentSourceInfo { get; set; }\n\n        /// <summary>\n        /// Event that is raised when the current source info changes.\n        /// This is used to notify the system of changes to the current source info.\n        /// The event handler receives the new source info and the type of change that occurred.\n        /// </summary>\n        event SourceInfoChangeHandler CurrentSourceChange;\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IInputSync.cs",
    "content": "﻿using PepperDash.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.Routing\n{\n    /// <summary>\n    /// Defines the contract for IVideoSync\n    /// </summary>\n    public interface IVideoSync : IKeyed\n    {\n        /// <summary>\n        /// Gets whether or not video sync is detected\n        /// </summary>\n        bool VideoSyncDetected { get; }\n\n        /// <summary>\n        /// Event raised when video sync changes\n        /// </summary>\n        event EventHandler VideoSyncChanged;\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IMatrixRouting.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core.Routing\n{\n    /// <summary>\n    /// Defines the contract for IMatrixRouting\n    /// </summary>\n    public interface IMatrixRouting\n    {\n        /// <summary>\n        /// Gets the input slots\n        /// </summary>\n        Dictionary<string, IRoutingInputSlot> InputSlots { get; }\n\n        /// <summary>\n        /// Gets the output slots\n        /// </summary>\n        Dictionary<string, IRoutingOutputSlot> OutputSlots { get; }\n\n        /// <summary>\n        /// Routes the specified input slot to the specified output slot for the specified signal type\n        /// </summary>\n        /// <param name=\"inputSlotKey\">key of the input slot</param>\n        /// <param name=\"outputSlotKey\">key of the output slot</param>\n        /// <param name=\"type\">signal type</param>\n        void Route(string inputSlotKey, string outputSlotKey, eRoutingSignalType type);\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRmcRouting\n    /// </summary>\n    public interface IRmcRouting : IRoutingNumeric\n    {\n        /// <summary>\n        /// Feedback for the current Audio/Video source as a number\n        /// </summary>\n        IntFeedback AudioVideoSourceNumericFeedback { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRmcRoutingWithFeedback.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRmcRoutingWithFeedback\n    /// </summary>\n    public interface IRmcRoutingWithFeedback : IRmcRouting\n    {\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRouting.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines a midpoint device as have internal routing.  Any devices in the middle of the\n    /// signal chain, that do switching, must implement this for routing to work otherwise\n    /// the routing algorithm will treat the IRoutingInputsOutputs device as a passthrough\n    /// device.\n    /// </summary>\n    public interface IRouting : IRoutingInputsOutputs\n\t{\n        /// <summary>\n        /// Executes a switch on the device\n        /// </summary>\n        /// <param name=\"inputSelector\">input selector</param>\n        /// <param name=\"outputSelector\">output selector</param>\n        /// <param name=\"signalType\">type of signal</param>\n\t\tvoid ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);        \n    }    \n\n    /*public interface IRouting<TInputSelector,TOutputSelector> : IRoutingInputsOutputs\n    {\n        void ExecuteSwitch(TInputSelector inputSelector, TOutputSelector outputSelector, eRoutingSignalType signalType);\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs",
    "content": "﻿using System;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoutingFeedback\n    /// </summary>\n    public interface IRoutingFeedback : IKeyName\n    {\n        /// <summary>\n        /// Event raised when a numeric switch changes\n        /// </summary>\n        event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;\n        //void OnSwitchChange(RoutingNumericEventArgs e);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingHasVideoInputSyncFeedbacks.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DM;\n\nusing PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\n /// <summary>\n /// Defines the contract for IRoutingHasVideoInputSyncFeedbacks\n /// </summary>\n\tpublic interface IRoutingHasVideoInputSyncFeedbacks\n\t{\n\t\t/// <summary>\n\t\t/// Video Input Sync Feedbacks\n\t\t/// </summary>\n\t\tFeedbackCollection<BoolFeedback> VideoInputSyncFeedbacks { get; }\n\t}\t\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingInputSlot.cs",
    "content": "﻿namespace PepperDash.Essentials.Core.Routing\n{\n    /// <summary>\n    /// Defines the contract for IRoutingInputSlot\n    /// </summary>\n    public interface IRoutingInputSlot: IRoutingSlot, IOnline, IVideoSync\n    {\n        /// <summary>\n        /// Gets the Tx device key\n        /// </summary>\n        string TxDeviceKey { get; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs",
    "content": "﻿using PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoutingInputs\n    /// </summary>\n    public interface IRoutingInputs : IKeyed\n\t{\n        /// <summary>\n        /// Collection of Input Ports\n        /// </summary>\n\t\tRoutingPortCollection<RoutingInputPort> InputPorts { get; }\n\t}\n\n/*    public interface IRoutingInputs<TSelector> : IKeyed\n    {\n        RoutingPortCollection<RoutingInputPort<TSelector>, TSelector> InputPorts { get; }\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingInputsOutputs.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoutingInputsOutputs\n    /// </summary>\n    public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs\n\t{\n\t}\n\n/*    /// <summary>\n    /// For devices like RMCs, baluns, other devices with no switching.\n    /// </summary>\n    public interface IRoutingInputsOutputs<TInputSelector, TOutputSelector> : IRoutingInputs<TInputSelector>, IRoutingOutputs<TOutputSelector>\n    {\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingNumeric.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoutingNumeric\n    /// </summary>\n    public interface IRoutingNumeric : IRouting\n    {\n        /// <summary>\n        /// Executes a numeric switch on the device\n        /// </summary>\n        /// <param name=\"input\">input selector</param>\n        /// <param name=\"output\">output selector</param>\n        /// <param name=\"type\">type of signal</param>\n        void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingNumericWithFeedback.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoutingNumericWithFeedback\n    /// </summary>\n    public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback\n    {\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingOutputSlot.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core.Routing\n{\n    /// <summary>\n    /// Defines the contract for IRoutingOutputSlot\n    /// </summary>\n    public interface IRoutingOutputSlot : IRoutingSlot\n    {\n        /// <summary>\n        /// Event raised when output slot changes\n        /// </summary>\n        event EventHandler OutputSlotChanged;\n\n        /// <summary>\n        /// Gets the Rx device key\n        /// </summary>\n        string RxDeviceKey { get; }\n\n        /// <summary>\n        /// Gets the current routes\n        /// </summary>\n        Dictionary<eRoutingSignalType, IRoutingInputSlot> CurrentRoutes { get; }\n    }    \n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs",
    "content": "﻿using PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\n    /// <summary>\n    /// Defines the contract for IRoutingOutputs\n    /// </summary>\n    public interface IRoutingOutputs : IKeyed\n\t{\n        /// <summary>\n        /// Collection of Output Ports\n        /// </summary>\n\t\tRoutingPortCollection<RoutingOutputPort> OutputPorts { get; }\n\t}\n\n/*    public interface IRoutingOutputs<TSelector> : IKeyed\n    {\n        RoutingPortCollection<RoutingOutputPort<TSelector>, TSelector> OutputPorts { get; }\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs",
    "content": "﻿using PepperDash.Essentials.Core.Routing;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoutingSink\n    /// </summary>\n    public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange\n    {\n    }\n\n    /// <summary>\n    /// For fixed-source endpoint devices with an input port\n    /// </summary>\n    public interface IRoutingSinkWithInputPort : IRoutingSink\n    {\n        /// <summary>\n        /// Gets the current input port for this routing sink.\n        /// </summary>\n        RoutingInputPort CurrentInputPort { get; }\n    }\n\n    /// <summary>\n    /// Interface for routing sinks that have access to the current source information.\n    /// </summary>\n    public interface IRoutingSinkWithCurrentSources : IRoutingSink, ICurrentSources\n    {\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithFeedback.cs",
    "content": "﻿using PepperDash.Essentials.Core.Routing;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core\n{\n    \n    /// <summary>\n    /// Defines the contract for IRoutingSinkWithFeedback\n    /// </summary>\n    public interface IRoutingSinkWithFeedback : IRoutingSinkWithSwitching\n    {\n        \n    }\n\n/*    /// <summary>\n    /// For fixed-source endpoint devices\n    /// </summary>\n    public interface IRoutingSinkWithFeedback<TSelector> : IRoutingSinkWithSwitching<TSelector>\n    {\n        RouteSwitchDescriptor CurrentRoute { get; }\n\n        event EventHandler InputChanged;\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithSwitching.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Delegate for InputChangedEventHandler\n    /// </summary>\n    public delegate void InputChangedEventHandler(IRoutingSinkWithSwitching destination, RoutingInputPort currentPort);\n\n    /// <summary>\n    /// Defines the contract for IRoutingSinkWithSwitching\n    /// </summary>\n    public interface IRoutingSinkWithSwitching : IRoutingSink\n\t{\t\t\n        /// <summary>\n        /// Executes a switch on the device\n        /// </summary>\n        /// <param name=\"inputSelector\">input selector</param>\n\t\tvoid ExecuteSwitch(object inputSelector);        \n    }\n\n    /// <summary>\n    /// Defines the contract for IRoutingSinkWithSwitchingWithInputPort\n    /// </summary>\n    public interface IRoutingSinkWithSwitchingWithInputPort:IRoutingSinkWithSwitching, IRoutingSinkWithInputPort\n    {\n        /// <summary>\n        /// Event raised when the input changes\n        /// </summary>\n        event InputChangedEventHandler InputChanged;\n    }\n\n/*    /// <summary>\n    /// Endpoint device like a display, that selects inputs\n    /// </summary>\n    /// <summary>\n    /// Defines the contract for IRoutingSinkWithSwitching\n    /// </summary>\n    public interface IRoutingSinkWithSwitching<TSelector> : IRoutingSink<TSelector>\n    {\n        void ExecuteSwitch(TSelector inputSelector);\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingSlot.cs",
    "content": "﻿using PepperDash.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.Routing\n{\n    /// <summary>\n    /// Defines the contract for IRoutingSlot\n    /// </summary>\n    public interface IRoutingSlot:IKeyName\n    {\n        /// <summary>\n        /// Gets the slot number\n        /// </summary>\n        int SlotNumber { get; }\n\n        /// <summary>\n        /// Gets the supported signal types\n        /// </summary>\n        eRoutingSignalType SupportedSignalTypes { get; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoutingSource\n    /// </summary>\n    public interface IRoutingSource : IRoutingOutputs\n    {\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IRoutingWithClear\n    /// </summary>\n    public interface IRoutingWithClear : IRouting\n    {\n        /// <summary>\n        /// Clears a route to an output, however a device needs to do that\n        /// </summary>\n        /// <param name=\"outputSelector\">Output to clear</param>\n        /// <param name=\"signalType\">signal type to clear</param>\n        void ClearRoute(object outputSelector, eRoutingSignalType signalType);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs",
    "content": "﻿using System.Collections.Generic;\nusing System;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Delegate for handling route change events on devices implementing <see cref=\"IRoutingWithFeedback\"/>.\n    /// </summary>\n    /// <param name=\"midpoint\">The routing device where the change occurred.</param>\n    /// <param name=\"newRoute\">A descriptor of the new route that was established.</param>\n    /// <summary>\n    /// Delegate for RouteChangedEventHandler\n    /// </summary>\n    public delegate void RouteChangedEventHandler(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute);\n    /// <summary>\n    /// Defines a routing device (<see cref=\"IRouting\"/>) that provides feedback about its current routes.\n    /// </summary>\n    public interface IRoutingWithFeedback : IRouting\n    {\n        /// <summary>\n        /// Gets a list describing the currently active routes on this device.\n        /// </summary>\n        List<RouteSwitchDescriptor> CurrentRoutes { get; }\n\n        /// <summary>\n        /// Event triggered when a route changes on this device.\n        /// </summary>\n        event RouteChangedEventHandler RouteChanged;\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/ITxRouting.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for ITxRouting\n    /// </summary>\n    public interface ITxRouting : IRoutingNumeric\n    {\n        /// <summary>\n        /// Feedback indicating the currently routed video source by its numeric identifier.\n        /// </summary>\n        IntFeedback VideoSourceNumericFeedback { get; }\n        /// <summary>\n        /// Feedback indicating the currently routed audio source by its numeric identifier.\n        /// </summary>\n        IntFeedback AudioSourceNumericFeedback { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/ITxRoutingWithFeedback.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for ITxRoutingWithFeedback\n    /// </summary>\n    public interface ITxRoutingWithFeedback : ITxRouting\n    {\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharpPro;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a collection of individual route steps between a Source and a Destination device for a specific signal type.\n    /// </summary>\n    public class RouteDescriptor\n    {\n        /// <summary>\n        /// The destination device (sink or midpoint) for the route.\n        /// </summary>\n        public IRoutingInputs Destination { get; private set; }\n\n        /// <summary>\n        /// The InputPort on the destination device for this route, if applicable.  May be null if the route is not for a specific input port.\n        /// </summary>\n        public RoutingInputPort InputPort { get; private set; }\n\n        /// <summary>\n        /// Gets the source device (sink or midpoint) for the route.\n        /// </summary>\n        public IRoutingOutputs Source { get; private set; }\n\n        /// <summary>\n        /// Gets the OutputPort on the source device for this route, if applicable.  May be null if the route is not for a specific output port.\n        /// </summary>\n        public RoutingOutputPort OutputPort { get; private set; }\n\n        /// <summary>\n        /// Gets the signal type for this route.\n        /// </summary>\n        public eRoutingSignalType SignalType { get; private set; }\n\n        /// <summary>\n        /// Gets the collection of route switch descriptors for this route.\n        /// </summary>\n        public List<RouteSwitchDescriptor> Routes { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RouteDescriptor\"/> class for a route without a specific destination input port.\n        /// </summary>\n        /// <param name=\"source\">The source device.</param>\n        /// <param name=\"destination\">The destination device.</param>\n        /// <param name=\"signalType\">The type of signal being routed.</param>\n        public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) : this(source, destination, null, signalType)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RouteDescriptor\"/> class for a route with a specific destination input port.\n        /// </summary>\n        /// <param name=\"source\">The source device.</param>\n        /// <param name=\"destination\">The destination device.</param>\n        /// <param name=\"inputPort\">The destination input port (optional).</param>\n        /// <param name=\"signalType\">The signal type for this route.</param>\n        public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, eRoutingSignalType signalType) : this(source, destination, inputPort, null, signalType)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RouteDescriptor\"/> class for a route with specific destination input and source output ports.\n        /// </summary>\n        /// <param name=\"source\"></param>\n        /// <param name=\"destination\"></param>\n        /// <param name=\"inputPort\"></param>\n        /// <param name=\"outputPort\"></param>\n        /// <param name=\"signalType\"></param>\n        public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, RoutingOutputPort outputPort, eRoutingSignalType signalType)\n        {\n            Destination = destination;\n            InputPort = inputPort;\n            Source = source;\n            OutputPort = outputPort;\n            SignalType = signalType;\n            Routes = new List<RouteSwitchDescriptor>();\n        }\n\n        /// <summary>\n        /// ExecuteRoutes method\n        /// </summary>\n        public void ExecuteRoutes()\n        {\n            foreach (var route in Routes)\n            {\n                Debug.LogVerbose(\"ExecuteRoutes: {0}\", route.ToString());\n\n                if (route.SwitchingDevice is IRoutingSinkWithSwitching sink)\n                {\n                    sink.ExecuteSwitch(route.InputPort.Selector);\n                    continue;\n                }\n\n                if (route.SwitchingDevice is IRouting switchingDevice)\n                {\n                    switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);\n\n                    route.OutputPort.InUseTracker.AddUser(Destination, \"destination-\" + SignalType);\n\n                    Debug.LogVerbose(\"Output port {0} routing. Count={1}\", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Releases the usage tracking for the route and optionally clears the route on the switching devices.\n        /// </summary>\n        /// <param name=\"clearRoute\">If true, attempts to clear the route on the switching devices (e.g., set input to null/0).</param>\n\n\n        public void ReleaseRoutes(bool clearRoute = false)\n        {\n            foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting))\n            {\n                if (route.SwitchingDevice is IRouting switchingDevice)\n                {\n                    if (clearRoute)\n                    {\n                        try\n                        {\n                            switchingDevice.ExecuteSwitch(null, route.OutputPort.Selector, SignalType);\n                        }\n                        catch (Exception e)\n                        {\n                            Debug.LogError(\"Error executing switch: {exception}\", e.Message);\n                            Debug.LogDebug(e, \"Stack Trace: \");\n                        }\n                    }\n\n                    if (route.OutputPort == null)\n                    {\n                        continue;\n                    }\n\n                    if (route.OutputPort.InUseTracker != null)\n                    {\n                        route.OutputPort.InUseTracker.RemoveUser(Destination, \"destination-\" + SignalType);\n                        Debug.LogVerbose(\"Port {0} releasing. Count={1}\", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);\n                    }\n                    else\n                    {\n                        Debug.LogVerbose(\"InUseTracker is null for OutputPort {0}\", route.OutputPort.Key);\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// Returns a string representation of the route descriptor, including source, destination, and individual route steps.\n        /// </summary>\n        /// <returns>A string describing the route.</returns>\n        public override string ToString()\n        {\n            var routesText = Routes.Select(r => r.ToString()).ToArray();\n            return $\"Route table from {Source.Key} to {Destination.Key} for {SignalType}:\\r\\n    {string.Join(\"\\r\\n    \", routesText)}\";\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RouteDescriptorCollection.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// A collection of RouteDescriptors - typically the static DefaultCollection is used\n    /// </summary>\n    public class RouteDescriptorCollection\n    {\n        /// <summary>\n        /// Gets the default collection of RouteDescriptors.\n        /// </summary>\n        public static RouteDescriptorCollection DefaultCollection\n        {\n            get\n            {\n                if (_DefaultCollection == null)\n                    _DefaultCollection = new RouteDescriptorCollection();\n                return _DefaultCollection;\n            }\n        }\n        private static RouteDescriptorCollection _DefaultCollection;\n\n        private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();\n\n        /// <summary>\n        /// Gets an enumerable collection of all RouteDescriptors in this collection.\n        /// </summary>\n        public IEnumerable<RouteDescriptor> Descriptors => RouteDescriptors.AsReadOnly();\n\n        /// <summary>\n        /// Adds a RouteDescriptor to the list.  If an existing RouteDescriptor for the\n        /// destination exists already, it will not be added - in order to preserve\n        /// proper route releasing.\n        /// </summary>\n        /// <param name=\"descriptor\"></param>\n        public void AddRouteDescriptor(RouteDescriptor descriptor)\n        {\n            if (descriptor == null)\n            {\n                return;\n            }\n\n            // Check if a route already exists with the same source, destination, input port, AND signal type\n            var existingRoute = RouteDescriptors.FirstOrDefault(t =>\n                t.Source == descriptor.Source &&\n                t.Destination == descriptor.Destination &&\n                t.SignalType == descriptor.SignalType &&\n                ((t.InputPort == null && descriptor.InputPort == null) ||\n                 (t.InputPort != null && descriptor.InputPort != null && t.InputPort.Key == descriptor.InputPort.Key)) &&\n                ((t.OutputPort == null && descriptor.OutputPort == null) ||\n                    (t.OutputPort != null && descriptor.OutputPort != null && t.OutputPort.Key == descriptor.OutputPort.Key)));\n\n            if (existingRoute != null)\n            {\n                Debug.LogInformation(descriptor.Destination,\n                    \"Route from {source}:{outputPort} to {destination}:{inputPort} ({signalType}) already exists in this collection\",\n                    descriptor?.Source?.Key,\n                    descriptor?.OutputPort?.Key ?? \"auto\",\n                    descriptor?.Destination?.Key,\n                    descriptor?.InputPort?.Key ?? \"auto\",\n                    descriptor?.SignalType\n                    );\n                return;\n            }\n            Debug.LogVerbose(\"Adding route descriptor: {source}:{outputPort} -> {destination}:{inputPort} ({signalType})\",\n                descriptor?.Source?.Key,\n                descriptor?.OutputPort?.Key ?? \"auto\",\n                descriptor?.Destination?.Key,\n                descriptor?.InputPort?.Key ?? \"auto\",\n                descriptor?.SignalType);\n            RouteDescriptors.Add(descriptor);\n        }\n\n        /// <summary>\n        /// Gets the RouteDescriptor for a destination. Returns null if no RouteDescriptor for a destination exists.\n        /// </summary>\n        public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Getting route descriptor for '{destination}'\", destination?.Key ?? null);\n\n            return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);\n        }\n\n        /// <summary>\n        /// Gets the route descriptor for a specific destination and input port\n        /// </summary>\n        /// <param name=\"destination\">The destination device</param>\n        /// <param name=\"inputPortKey\">The input port key</param>\n        /// <returns>The matching RouteDescriptor or null if not found</returns>\n        public RouteDescriptor GetRouteDescriptorForDestinationAndInputPort(IRoutingInputs destination, string inputPortKey)\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Getting route descriptor for '{destination}':'{inputPortKey}'\", destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? \"auto\" : inputPortKey);\n            return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination && rd.InputPort != null && rd.InputPort.Key == inputPortKey);\n        }\n\n        /// <summary>\n        /// Removes a RouteDescriptor from the collection based on the specified destination and input port key.\n        /// </summary>\n        /// <param name=\"destination\">The destination for which the route descriptor is to be removed.</param>\n        /// <param name=\"inputPortKey\">The key of the input port associated with the route descriptor. If empty, the method will attempt to remove a descriptor based solely on the destination.</param>\n        /// <returns>The removed RouteDescriptor object if a matching descriptor was found; otherwise, null.</returns>\n        public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination, string inputPortKey = \"\")\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Removing route descriptor for '{destination}':'{inputPortKey}'\", destination.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? \"auto\" : inputPortKey);\n\n            var descr = string.IsNullOrEmpty(inputPortKey)\n                ? GetRouteDescriptorForDestination(destination)\n                : GetRouteDescriptorForDestinationAndInputPort(destination, inputPortKey);\n            if (descr != null)\n                RouteDescriptors.Remove(descr);\n\n            Debug.LogMessage(LogEventLevel.Information, \"Found route descriptor {routeDescriptor}\", destination, descr);\n\n            return descr;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RouteRequest.cs",
    "content": "﻿using PepperDash.Core;\nusing Serilog.Events;\nusing System;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a RouteRequest\n    /// </summary>\n    public class RouteRequest\n    {\n        /// <summary>\n        /// The specific input port on the destination device to use for the route. Can be null if the port should be automatically determined or is not applicable.\n        /// </summary>\n        public RoutingInputPort DestinationPort { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SourcePort\n        /// </summary>\n        public RoutingOutputPort SourcePort { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Destination\n        /// </summary>\n        public IRoutingInputs Destination { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Source\n        /// </summary>\n        public IRoutingOutputs Source { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SignalType\n        /// </summary>\n        public eRoutingSignalType SignalType { get; set; }\n\n        /// <summary>\n        /// Handles the route request after a device's cooldown period has finished.\n        /// This method is typically subscribed to the IsCoolingDownFeedback.OutputChange event.\n        /// </summary>\n        /// <param name=\"sender\">The object that triggered the event (usually the cooling device).</param>\n        /// <param name=\"args\">Event arguments indicating the cooldown state change.</param>\n        public void HandleCooldown(object sender, FeedbackEventArgs args)\n        {\n            try\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Handling cooldown route request: {destination}:{destinationPort} -> {source}:{sourcePort} {type}\", null, Destination?.Key ?? \"empty destination\", DestinationPort?.Key ?? \"no destination port\", Source?.Key ?? \"empty source\", SourcePort?.Key ?? \"empty source port\", SignalType.ToString());\n\n                if (args.BoolValue == true)\n                {\n                    return;\n                }\n\n                Debug.LogMessage(LogEventLevel.Information, \"Cooldown complete. Making route from {destination} to {source}\", Destination?.Key, Source?.Key);\n\n                Destination.ReleaseAndMakeRoute(Source, SignalType, DestinationPort?.Key ?? string.Empty, SourcePort?.Key ?? string.Empty);\n\n                if (sender is IWarmingCooling coolingDevice)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, \"Unsubscribing from cooling feedback for {destination}\", null, Destination.Key);\n                    coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;\n                }\n            } catch(Exception ex)\n            {\n                Debug.LogMessage(ex, \"Exception handling cooldown\", Destination);\n            }\n        }\n\n        /// <summary>\n        /// Returns a string representation of the route request.\n        /// </summary>\n        /// <returns>A string describing the source and destination of the route request.</returns>\n        public override string ToString()\n        {\n            return $\"Route {Source?.Key ?? \"No Source Device\"}:{SourcePort?.Key ?? \"auto\"} to {Destination?.Key ?? \"No Destination Device\"}:{DestinationPort?.Key ?? \"auto\"}\";\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RouteRequestQueueItem.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.Core.Queues;\nusing System;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Routing\n{\n    /// <summary>\n    /// Represents a RouteRequestQueueItem\n    /// </summary>\n    public class RouteRequestQueueItem : IQueueMessage\n    {\n        /// <summary>\n        /// The action to perform for the route request.\n        /// </summary>\n        private readonly Action<RouteRequest> action;\n        /// <summary>\n        /// The route request data.\n        /// </summary>\n        private readonly RouteRequest routeRequest;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RouteRequestQueueItem\"/> class.\n        /// </summary>\n        /// <param name=\"routeAction\">The action to perform.</param>\n        /// <param name=\"request\">The route request data.</param>\n        public RouteRequestQueueItem(Action<RouteRequest> routeAction, RouteRequest request)\n        {\n            action = routeAction;\n            routeRequest = request;\n        }\n\n        /// <summary>\n        /// Dispatches the route request action.\n        /// </summary>\n        public void Dispatch()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Dispatching route request {routeRequest}\", null, routeRequest);\n            action(routeRequest);\n        }\n    }\n\n    /// <summary>\n    /// Represents a ReleaseRouteQueueItem\n    /// </summary>\n    public class ReleaseRouteQueueItem : IQueueMessage\n    {\n        /// <summary>\n        /// The action to perform for releasing the route.\n        /// </summary>\n        private readonly Action<IRoutingInputs, string, bool> action;\n        /// <summary>\n        /// The destination device whose route is being released.\n        /// </summary>\n        private readonly IRoutingInputs destination;\n        /// <summary>\n        /// The specific input port key on the destination to release, or null/empty for any/default.\n        /// </summary>\n        private readonly string inputPortKey;\n        /// <summary>\n        /// Indicates whether to clear the route (send null) or just release the usage tracking.\n        /// </summary>\n        private readonly bool clearRoute;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ReleaseRouteQueueItem\"/> class.\n        /// </summary>\n        /// <param name=\"action\">The action to perform.</param>\n        /// <param name=\"destination\">The destination device.</param>\n        /// <param name=\"inputPortKey\">The input port key.</param>\n        /// <param name=\"clearRoute\">True to clear the route, false to just release.</param>\n        public ReleaseRouteQueueItem(Action<IRoutingInputs, string, bool> action, IRoutingInputs destination, string inputPortKey, bool clearRoute)\n        {\n            this.action = action;\n            this.destination = destination;\n            this.inputPortKey = inputPortKey;\n            this.clearRoute = clearRoute;\n        }\n\n        /// <summary>\n        /// Dispatch method\n        /// </summary>\n        public void Dispatch()\n        {\n            Debug.LogMessage(LogEventLevel.Information, \"Dispatching release route request for {destination}:{inputPortKey}\", null, destination?.Key ?? \"no destination\", string.IsNullOrEmpty(inputPortKey) ? \"auto\" : inputPortKey);\n            action(destination, inputPortKey, clearRoute);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RouteSwitchDescriptor.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a RouteSwitchDescriptor\n    /// </summary>\n    public class RouteSwitchDescriptor\n    {\n        /// <summary>\n        /// Gets or sets the SwitchingDevice\n        /// </summary>\n        public IRoutingInputs SwitchingDevice { get { return InputPort?.ParentDevice; } }\n        /// <summary>\n        /// The output port being switched from (relevant for matrix switchers). Null for sink devices.\n        /// </summary>\n        public RoutingOutputPort OutputPort { get; set; }\n        /// <summary>\n        /// The input port being switched to.\n        /// </summary>\n        public RoutingInputPort InputPort { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RouteSwitchDescriptor\"/> class for sink devices (no output port).\n        /// </summary>\n        /// <param name=\"inputPort\">The input port being switched to.</param>\n        public RouteSwitchDescriptor(RoutingInputPort inputPort)\n        {\n            InputPort = inputPort;\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RouteSwitchDescriptor\"/> class for matrix switchers.\n        /// </summary>\n        /// <param name=\"outputPort\">The output port being switched from.</param>\n        /// <param name=\"inputPort\">The input port being switched to.</param>\n        public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)\n        {\n            InputPort = inputPort;\n            OutputPort = outputPort;\n        }\n\n        /// <summary>\n        /// Returns a string representation of the route switch descriptor.\n        /// </summary>\n        /// <returns>A string describing the switch operation.</returns>\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            if (SwitchingDevice is IRouting)\n                return $\"{(SwitchingDevice != null ? SwitchingDevice.Key : \"No Device\")} switches output {(OutputPort != null ? OutputPort.Key : \"No output port\")} to input {(InputPort != null ? InputPort.Key : \"No input port\")}\";\n            else\n                return $\"{(SwitchingDevice != null ? SwitchingDevice.Key : \"No Device\")} switches to input {(InputPort != null ? InputPort.Key : \"No input port\")}\";\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Timers;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\n\nnamespace PepperDash.Essentials.Core.Routing\n{\n    /// <summary>\n    /// Manages routing feedback by subscribing to route changes on midpoint and sink devices,\n    /// tracing the route back to the original source, and updating the CurrentSourceInfo on sink devices.\n    /// </summary>\n    public class RoutingFeedbackManager : EssentialsDevice\n    {\n        /// <summary>\n        /// Maps midpoint device keys to the set of sink device keys that are downstream\n        /// </summary>\n        private Dictionary<string, HashSet<string>> midpointToSinksMap;\n\n        /// <summary>\n        /// Debounce timers for each sink device to prevent rapid successive updates\n        /// </summary>\n        private readonly Dictionary<string, Timer> updateTimers = new Dictionary<string, Timer>();\n\n        /// <summary>\n        /// Lock object protecting all access to <see cref=\"updateTimers\"/>.\n        /// </summary>\n        private readonly object _timerLock = new object();\n\n        /// <summary>\n        /// Debounce delay in milliseconds\n        /// </summary>\n        private const long DEBOUNCE_MS = 500;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RoutingFeedbackManager\"/> class.\n        /// </summary>\n        /// <param name=\"key\">The unique key for this manager device.</param>\n        /// <param name=\"name\">The name of this manager device.</param>\n        public RoutingFeedbackManager(string key, string name)\n            : base(key, name)\n        {\n            AddPreActivationAction(BuildMidpointSinkMap);\n            AddPreActivationAction(SubscribeForMidpointFeedback);\n            AddPreActivationAction(SubscribeForSinkFeedback);\n        }\n\n        /// <summary>\n        /// Builds a map of which sink devices are downstream of each midpoint device\n        /// for performance optimization in HandleMidpointUpdate\n        /// </summary>\n        private void BuildMidpointSinkMap()\n        {\n            midpointToSinksMap = new Dictionary<string, HashSet<string>>();\n\n            var sinks = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();\n\n            foreach (var sink in sinks)\n            {\n                if (sink.CurrentInputPort == null)\n                    continue;\n\n                // Find all upstream midpoints for this sink\n                var upstreamMidpoints = GetUpstreamMidpoints(sink);\n\n                foreach (var midpointKey in upstreamMidpoints)\n                {\n                    if (!midpointToSinksMap.ContainsKey(midpointKey))\n                        midpointToSinksMap[midpointKey] = new HashSet<string>();\n\n                    midpointToSinksMap[midpointKey].Add(sink.Key);\n                }\n            }\n\n            Debug.LogMessage(\n                Serilog.Events.LogEventLevel.Information,\n                \"Built midpoint-to-sink map with {count} midpoints\",\n                this,\n                midpointToSinksMap.Count\n            );\n        }\n\n        /// <summary>\n        /// Gets all upstream midpoint device keys for a given sink\n        /// </summary>\n        private HashSet<string> GetUpstreamMidpoints(IRoutingSinkWithSwitchingWithInputPort sink)\n        {\n            var result = new HashSet<string>();\n            var visited = new HashSet<string>();\n\n            if (sink.CurrentInputPort == null)\n                return result;\n\n            var tieLine = TieLineCollection.Default.FirstOrDefault(tl =>\n                tl.DestinationPort.Key == sink.CurrentInputPort.Key &&\n                tl.DestinationPort.ParentDevice.Key == sink.CurrentInputPort.ParentDevice.Key);\n\n            if (tieLine == null)\n                return result;\n\n            TraceUpstreamMidpoints(tieLine, result, visited);\n            return result;\n        }\n\n        /// <summary>\n        /// Recursively traces upstream to find all midpoint devices\n        /// </summary>\n        private void TraceUpstreamMidpoints(TieLine tieLine, HashSet<string> midpoints, HashSet<string> visited)\n        {\n            if (tieLine == null || visited.Contains(tieLine.SourcePort.ParentDevice.Key))\n                return;\n\n            visited.Add(tieLine.SourcePort.ParentDevice.Key);\n\n            if (tieLine.SourcePort.ParentDevice is IRoutingWithFeedback midpoint)\n            {\n                midpoints.Add(midpoint.Key);\n\n                // Find upstream TieLines connected to this midpoint's inputs\n                var midpointInputs = (midpoint as IRoutingInputs)?.InputPorts;\n                if (midpointInputs != null)\n                {\n                    foreach (var inputPort in midpointInputs)\n                    {\n                        var upstreamTieLine = TieLineCollection.Default.FirstOrDefault(tl =>\n                            tl.DestinationPort.Key == inputPort.Key &&\n                            tl.DestinationPort.ParentDevice.Key == inputPort.ParentDevice.Key);\n\n                        if (upstreamTieLine != null)\n                            TraceUpstreamMidpoints(upstreamTieLine, midpoints, visited);\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// Subscribes to the RouteChanged event on all devices implementing <see cref=\"IRoutingWithFeedback\"/>.\n        /// </summary>\n        private void SubscribeForMidpointFeedback()\n        {\n            var midpointDevices = DeviceManager.AllDevices.OfType<IRoutingWithFeedback>();\n\n            foreach (var device in midpointDevices)\n            {\n                device.RouteChanged += HandleMidpointUpdate;\n            }\n        }\n\n        /// <summary>\n        /// Subscribes to the InputChanged event on all devices implementing <see cref=\"IRoutingSinkWithSwitchingWithInputPort\"/>.\n        /// </summary>\n        private void SubscribeForSinkFeedback()\n        {\n            var sinkDevices =\n                DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();\n\n            foreach (var device in sinkDevices)\n            {\n                device.InputChanged += HandleSinkUpdate;\n            }\n        }\n\n        /// <summary>\n        /// Handles the RouteChanged event from a midpoint device.\n        /// Only triggers updates for sink devices that are downstream of this midpoint.\n        /// </summary>\n        /// <param name=\"midpoint\">The midpoint device that reported a route change.</param>\n        /// <param name=\"newRoute\">The descriptor of the new route.</param>\n        private void HandleMidpointUpdate(\n            IRoutingWithFeedback midpoint,\n            RouteSwitchDescriptor newRoute\n        )\n        {\n            try\n            {\n                // Only update affected sinks (performance optimization)\n                if (midpointToSinksMap != null && midpointToSinksMap.TryGetValue(midpoint.Key, out var affectedSinkKeys))\n                {\n                    Debug.LogMessage(\n                        Serilog.Events.LogEventLevel.Debug,\n                        \"Midpoint {midpoint} changed, updating {count} downstream sinks\",\n                        this,\n                        midpoint.Key,\n                        affectedSinkKeys.Count\n                    );\n\n                    foreach (var sinkKey in affectedSinkKeys)\n                    {\n                        if (DeviceManager.GetDeviceForKey(sinkKey) is IRoutingSinkWithSwitchingWithInputPort sink)\n                        {\n                            UpdateDestination(sink, sink.CurrentInputPort);\n                        }\n                    }\n                }\n                else\n                {\n                    Debug.LogMessage(\n                        Serilog.Events.LogEventLevel.Debug,\n                        \"Midpoint {midpoint} changed but has no downstream sinks in map\",\n                        this,\n                        midpoint.Key\n                    );\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(\n                    ex,\n                    \"Error handling midpoint update from {midpointKey}:{Exception}\",\n                    this,\n                    midpoint.Key,\n                    ex\n                );\n            }\n        }\n\n        /// <summary>\n        /// Removes a sink from every midpoint set in the map and re-adds it based on its\n        /// current input port. Call this whenever a sink's selected input changes so that\n        /// HandleMidpointUpdate always sees an up-to-date downstream set.\n        /// </summary>\n        private void RebuildMapForSink(IRoutingSinkWithSwitchingWithInputPort sink)\n        {\n            if (midpointToSinksMap == null)\n                return;\n\n            // Remove this sink from all existing midpoint sets\n            foreach (var set in midpointToSinksMap.Values)\n                set.Remove(sink.Key);\n\n            // Drop any midpoint entries that are now empty\n            var emptyKeys = midpointToSinksMap\n                .Where(kvp => kvp.Value.Count == 0)\n                .Select(kvp => kvp.Key)\n                .ToList();\n            foreach (var k in emptyKeys)\n                midpointToSinksMap.Remove(k);\n\n            // Re-add the sink under every midpoint that is upstream of its new input\n            if (sink.CurrentInputPort == null)\n                return;\n\n            var upstreamMidpoints = GetUpstreamMidpoints(sink);\n            foreach (var midpointKey in upstreamMidpoints)\n            {\n                if (!midpointToSinksMap.ContainsKey(midpointKey))\n                    midpointToSinksMap[midpointKey] = new HashSet<string>();\n\n                midpointToSinksMap[midpointKey].Add(sink.Key);\n            }\n        }\n\n        /// <summary>\n        /// Handles the InputChanged event from a sink device.\n        /// Updates the midpoint-to-sink map for the new input path, then triggers\n        /// a source-info update for the sink.\n        /// </summary>\n        /// <param name=\"sender\">The sink device that reported an input change.</param>\n        /// <param name=\"currentInputPort\">The new input port selected on the sink device.</param>\n        private void HandleSinkUpdate(\n            IRoutingSinkWithSwitching sender,\n            RoutingInputPort currentInputPort\n        )\n        {\n            try\n            {\n                // Keep the map current so HandleMidpointUpdate can find this sink\n                if (sender is IRoutingSinkWithSwitchingWithInputPort sinkWithInputPort)\n                    RebuildMapForSink(sinkWithInputPort);\n\n                UpdateDestination(sender, currentInputPort);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(\n                    ex,\n                    \"Error handling Sink update from {senderKey}:{Exception}\",\n                    this,\n                    sender.Key,\n                    ex\n                );\n            }\n        }\n\n        /// <summary>\n        /// Updates the CurrentSourceInfo and CurrentSourceInfoKey properties on a destination (sink) device\n        /// based on its currently selected input port by tracing the route back through tie lines.\n        /// Uses debouncing to prevent rapid successive updates.\n        /// </summary>\n        /// <param name=\"destination\">The destination sink device to update.</param>\n        /// <param name=\"inputPort\">The currently selected input port on the destination device.</param>\n        private void UpdateDestination(\n            IRoutingSinkWithSwitching destination,\n            RoutingInputPort inputPort\n        )\n        {\n            if (destination == null)\n                return;\n\n            var key = destination.Key;\n\n            // Cancel and replace any existing timer under the lock so no callback\n            // can race with us while we swap the entry.\n            Timer timerToDispose = null;\n            Timer newTimer = null;\n\n            newTimer = new Timer(DEBOUNCE_MS) { AutoReset = false };\n            newTimer.Elapsed += (s, e) =>\n            {\n                try\n                {\n                    UpdateDestinationImmediate(destination, inputPort);\n                }\n                catch (Exception ex)\n                {\n                    Debug.LogMessage(\n                        ex,\n                        \"Error in debounced update for destination {destinationKey}: {message}\",\n                        this,\n                        destination.Key,\n                        ex.Message\n                    );\n                }\n                finally\n                {\n                    // Remove the entry first so a concurrent UpdateDestination call\n                    // cannot re-dispose whatever timer we're about to dispose.\n                    Timer selfTimer = null;\n                    lock (_timerLock)\n                    {\n                        if (updateTimers.TryGetValue(key, out var current) && ReferenceEquals(current, newTimer))\n                        {\n                            selfTimer = current;\n                            updateTimers.Remove(key);\n                        }\n                    }\n                    selfTimer?.Dispose();\n                }\n            };\n\n            lock (_timerLock)\n            {\n                if (updateTimers.TryGetValue(key, out var existingTimer))\n                    timerToDispose = existingTimer;\n\n                updateTimers[key] = newTimer;\n            }\n\n            // Dispose the old timer outside the lock to avoid holding the lock during disposal.\n            // Dispose implicitly stops the timer, preventing its Elapsed event from firing.\n            timerToDispose?.Dispose();\n\n            // Start after the lock is released so the Elapsed callback cannot deadlock\n            // trying to acquire _timerLock while we still hold it.\n            newTimer.Start();\n        }\n\n        /// <summary>\n        /// Immediately updates the CurrentSourceInfo for a destination device.\n        /// Called after debounce delay.\n        /// </summary>\n        private void UpdateDestinationImmediate(\n            IRoutingSinkWithSwitching destination,\n            RoutingInputPort inputPort\n        )\n        {\n            Debug.LogMessage(\n                Serilog.Events.LogEventLevel.Debug,\n                \"Updating destination {destination} with inputPort {inputPort}\",\n                this,\n                destination?.Key,\n                inputPort?.Key\n            );\n\n            if (inputPort == null)\n            {\n                Debug.LogMessage(\n                    Serilog.Events.LogEventLevel.Debug,\n                    \"Destination {destination} has not reported an input port yet\",\n                    this,\n                    destination.Key\n                );\n                return;\n            }\n\n            TieLine firstTieLine;\n            try\n            {\n                var tieLines = TieLineCollection.Default;\n\n                firstTieLine = tieLines.FirstOrDefault(tl =>\n                    tl.DestinationPort.Key == inputPort.Key\n                    && tl.DestinationPort.ParentDevice.Key == inputPort.ParentDevice.Key\n                );\n\n                if (firstTieLine == null)\n                {\n                    Debug.LogMessage(\n                        Serilog.Events.LogEventLevel.Debug,\n                        \"No tieline found for inputPort {inputPort}. Clearing current source\",\n                        this,\n                        inputPort\n                    );\n\n                    var tempSourceListItem = new SourceListItem\n                    {\n                        SourceKey = \"$transient\",\n                        Name = inputPort.Key,\n                    };\n\n                    destination.CurrentSourceInfo = tempSourceListItem;\n                    ;\n                    destination.CurrentSourceInfoKey = \"$transient\";\n                    return;\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Error getting first tieline: {Exception}\", this, ex);\n                return;\n            }\n\n            // Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, \"Getting source for first TieLine {tieLine}\", this, firstTieLine);\n\n            TieLine sourceTieLine;\n            try\n            {\n                sourceTieLine = GetRootTieLine(firstTieLine);\n\n                if (sourceTieLine == null)\n                {\n                    Debug.LogMessage(\n                        Serilog.Events.LogEventLevel.Debug,\n                        \"No route found to source for inputPort {inputPort}. Clearing current source\",\n                        this,\n                        inputPort\n                    );\n\n                    var tempSourceListItem = new SourceListItem\n                    {\n                        SourceKey = \"$transient\",\n                        Name = \"None\",\n                    };\n\n                    destination.CurrentSourceInfo = tempSourceListItem;\n                    destination.CurrentSourceInfoKey = string.Empty;\n                    return;\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(this, \"Error getting sourceTieLine: {message}\", ex.Message);\n                Debug.LogDebug(ex, \"StackTrace: \");\n                return;\n            }\n\n            // Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, \"Found root TieLine {tieLine}\", this, sourceTieLine);\n\n            // Does not handle combinable scenarios or other scenarios where a display might be part of multiple rooms yet.\n            var room = DeviceManager\n                .AllDevices.OfType<IEssentialsRoom>()\n                .FirstOrDefault(\n                    (r) =>\n                    {\n                        if (r is IHasMultipleDisplays roomMultipleDisplays)\n                        {\n                            return roomMultipleDisplays.Displays.Any(d =>\n                                d.Value.Key == destination.Key\n                            );\n                        }\n\n                        if (r is IHasDefaultDisplay roomDefaultDisplay)\n                        {\n                            return roomDefaultDisplay.DefaultDisplay.Key == destination.Key;\n                        }\n\n                        if (ConfigReader.ConfigObject.GetDestinationListForKey(r.DestinationListKey)?.FirstOrDefault(d => d.Value.SinkKey == destination.Key) != null)\n                        {\n                            return true;\n                        }\n\n                        return false;\n                    }\n                );\n\n            if (room == null)\n            {\n                Debug.LogMessage(\n                    Serilog.Events.LogEventLevel.Debug,\n                    \"No room found for display {destination}\",\n                    this,\n                    destination.Key\n                );\n                return;\n            }\n\n            // Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, \"Found room {room} for destination {destination}\", this, room.Key, destination.Key);\n\n            var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(room.SourceListKey);\n\n            if (sourceList == null)\n            {\n                Debug.LogDebug(this,\n                    \"No source list found for source list key {key}. Unable to find source for tieLine {sourceTieLine}\",\n                    room.SourceListKey,\n                    sourceTieLine\n                );\n                return;\n            }\n\n            // Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, \"Found sourceList for room {room}\", this, room.Key);\n\n            var sourceListItem = sourceList.FirstOrDefault(sli =>\n            {\n                //// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose,\n                //   \"SourceListItem {sourceListItem}:{sourceKey} tieLine sourceport device key {sourcePortDeviceKey}\",\n                //   this,\n                //   sli.Key,\n                //   sli.Value.SourceKey,\n                //   sourceTieLine.SourcePort.ParentDevice.Key);\n\n                return sli.Value.SourceKey.Equals(\n                    sourceTieLine.SourcePort.ParentDevice.Key,\n                    StringComparison.InvariantCultureIgnoreCase\n                );\n            });\n\n            var source = sourceListItem.Value;\n            var sourceKey = sourceListItem.Key;\n\n            if (source == null)\n            {\n                Debug.LogDebug(this,\n                    \"No source found for device {key}. Creating transient source for {destination}\",\n                    sourceTieLine.SourcePort.ParentDevice.Key,\n                    destination\n                );\n\n                var tempSourceListItem = new SourceListItem\n                {\n                    SourceKey = \"$transient\",\n                    Name = sourceTieLine.SourcePort.Key,\n                };\n\n                destination.CurrentSourceInfoKey = \"$transient\";\n                destination.CurrentSourceInfo = tempSourceListItem;\n                return;\n            }\n\n            //Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, \"Got Source {@source} with key {sourceKey}\", this, source, sourceKey);\n\n            destination.CurrentSourceInfoKey = sourceKey;\n            destination.CurrentSourceInfo = source;\n        }\n\n        /// <summary>\n        /// Traces a route back from a given tie line to find the root source tie line.\n        /// Leverages the existing Extensions.GetRouteToSource method with loop protection.\n        /// </summary>\n        /// <param name=\"tieLine\">The starting tie line (typically connected to a sink or midpoint).</param>\n        /// <returns>The <see cref=\"TieLine\"/> connected to the original source device, or null if the source cannot be determined.</returns>\n        private TieLine GetRootTieLine(TieLine tieLine)\n        {\n            try\n            {\n                if (!(tieLine.DestinationPort.ParentDevice is IRoutingInputs sink))\n                {\n                    Debug.LogDebug(this,\n                        \"TieLine destination {device} is not IRoutingInputs\",\n                        tieLine.DestinationPort.ParentDevice.Key\n                    );\n                    return null;\n                }\n\n                // Get all potential sources (devices that only have outputs, not inputs+outputs)\n                var sources = DeviceManager.AllDevices\n                            .OfType<IRoutingOutputs>()\n                            .Where(s => !(s is IRoutingInputsOutputs));\n\n                // Try each signal type that this TieLine supports\n                var signalTypes = new[]\n                {\n                    eRoutingSignalType.Audio,\n                    eRoutingSignalType.Video,\n                    eRoutingSignalType.AudioVideo,\n                    eRoutingSignalType.SecondaryAudio,\n                    eRoutingSignalType.UsbInput,\n                    eRoutingSignalType.UsbOutput\n                };\n\n                foreach (var signalType in signalTypes)\n                {\n                    if (!tieLine.Type.HasFlag(signalType))\n                        continue;\n\n                    foreach (var source in sources)\n                    {\n                        // Use the optimized route discovery with loop protection\n                        var (route, _) = sink.GetRouteToSource(\n                            source,\n                            signalType,\n                            tieLine.DestinationPort,\n                            null\n                        );\n\n                        if (route != null && route.Routes != null && route.Routes.Count > 0)\n                        {\n                            // Routes[0] is the hop nearest the source: its InputPort is the\n                            // port on the first switching device that receives the signal from\n                            // the source side. The TieLine whose DestinationPort matches that\n                            // port is the exact tie that was traversed, giving us the precise\n                            // source output port via SourcePort — regardless of how many output\n                            // ports the source device has.\n                            var firstHop = route.Routes[0];\n                            var sourceTieLine = TieLineCollection.Default.FirstOrDefault(tl =>\n                                tl.DestinationPort.Key == firstHop.InputPort.Key &&\n                                tl.DestinationPort.ParentDevice.Key == firstHop.InputPort.ParentDevice.Key);\n\n                            if (sourceTieLine != null)\n                            {\n                                Debug.LogDebug(this,\n                                \"Found route from {source} to {sink} with {count} hops\",\n                                    source.Key,\n                                    sink.Key,\n                                    route.Routes.Count\n                                );\n                                return sourceTieLine;\n                            }\n                        }\n                    }\n                }\n\n                Debug.LogDebug(this, \"No route found to any source from {sink}\", sink.Key);\n                return null;\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(this, \"Error getting root tieLine: {message}\", ex.Message);\n                Debug.LogDebug(ex, \"StackTrace: \");\n\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a RoutingInputPort\n    /// </summary>\n    public class RoutingInputPort : RoutingPort\n\t{\n        /// <summary>\n        /// The IRoutingInputs object this lives on\n        /// </summary>\n        [JsonIgnore]\n        public IRoutingInputs ParentDevice { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor for a basic RoutingInputPort\n\t\t/// </summary>\n\t\t/// <param name=\"selector\">An object used to refer to this port in the IRouting device's ExecuteSwitch method.\n\t\t/// May be string, number, whatever</param>\n\t\t/// <param name=\"parent\">The IRoutingInputs object this lives on</param>\n        /// <param name=\"key\">key of the port</param>\n        /// <param name=\"type\">type of the routing signal</param>\n        /// <param name=\"connType\">connection type of the port</param>\n\t\tpublic RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,\n\t\t\tobject selector, IRoutingInputs parent)\n\t\t\t: this (key, type, connType, selector, parent, false)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor for a virtual routing input port that lives inside a device. For example\n\t\t/// the ports that link a DM card to a DM matrix bus\n\t\t/// </summary>\n\t\t/// <param name=\"isInternal\">true for internal ports</param>\n        /// <param name=\"key\">key of the port</param>\n        /// <param name=\"type\">type of the routing signal</param>\n        /// <param name=\"connType\">connection type of the port</param>\n        /// <param name=\"selector\">An object used to refer to this port in the IRouting device's ExecuteSwitch method.\n        /// May be string, number, whatever</param>\n        /// <param name=\"parent\">The IRoutingInputs object this lives on</param>\n\t\tpublic RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,\n\t\t\tobject selector, IRoutingInputs parent, bool isInternal)\n\t\t\t: base(key, type, connType, selector, isInternal)\n\t\t{\n\t\t\tif (parent == null)\n\t\t\t\tthrow new ArgumentNullException(nameof(parent));\n\t\t\tParentDevice = parent;\n\t\t}\n\n        /// <summary>\n        /// Returns a string representation of the input port.\n        /// </summary>\n        /// <returns>A string in the format \"ParentDeviceKey|PortKey|SignalType|ConnectionType\".</returns>\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return $\"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}\";\n        }\n    }\n\n    /*/// <summary>\n    /// Basic RoutingInput with no statuses.\n    /// </summary>\n    public class RoutingInputPort<TSelector> : RoutingPort<TSelector>\n    {\n        /// <summary>\n        /// Gets or sets the ParentDevice\n        /// </summary>\n        public IRoutingInputs<TSelector> ParentDevice { get; private set; }\n\n        /// <summary>\n        /// Constructor for a basic RoutingInputPort\n        /// </summary>\n        /// <param name=\"selector\">An object used to refer to this port in the IRouting device's ExecuteSwitch method.\n        /// May be string, number, whatever</param>\n        /// <param name=\"parent\">The IRoutingInputs object this lives on</param>\n        public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,\n            TSelector selector, IRoutingInputs<TSelector> parent)\n            : this(key, type, connType, selector, parent, false)\n        {\n        }\n\n        /// <summary>\n        /// Constructor for a virtual routing input port that lives inside a device. For example\n        /// the ports that link a DM card to a DM matrix bus\n        /// </summary>\n        /// <param name=\"isInternal\">true for internal ports</param>\n        public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,\n            TSelector selector, IRoutingInputs<TSelector> parent, bool isInternal)\n            : base(key, type, connType, selector, isInternal)\n        {\n            ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));\n        }\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RoutingInputPortWithVideoStatuses.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a RoutingInputPortWithVideoStatuses\n    /// </summary>\n    public class RoutingInputPortWithVideoStatuses : RoutingInputPort\n\t{\n\t\t/// <summary>\n\t\t/// Provides feedback outputs for video statuses associated with this port.\n\t\t/// </summary>\n\t\tpublic VideoStatusOutputs VideoStatus { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the <see cref=\"RoutingInputPortWithVideoStatuses\"/> class.\n\t\t/// </summary>\n\t\t/// <param name=\"key\">The unique key for this port.</param>\n\t\t/// <param name=\"type\">The signal type supported by this port.</param>\n\t\t/// <param name=\"connType\">The physical connection type of this port.</param>\n\t\t/// <param name=\"selector\">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>\n\t\t/// <param name=\"parent\">The <see cref=\"IRoutingInputs\"/> device this port belongs to.</param>\n\t\t/// <param name=\"funcs\">A <see cref=\"VideoStatusFuncsWrapper\"/> containing delegates to retrieve video status values.</param>\n\t\tpublic RoutingInputPortWithVideoStatuses(string key, \n\t\t\teRoutingSignalType type, eRoutingPortConnectionType connType, object selector, \n\t\t\tIRoutingInputs parent, VideoStatusFuncsWrapper funcs) :\n\t\t\tbase(key, type, connType, selector, parent)\n\t\t{\n\t\t\tVideoStatus = new VideoStatusOutputs(funcs);\t\t\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RoutingNumericEventArgs.cs",
    "content": "﻿using System;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a RoutingNumericEventArgs\n    /// </summary>\n    public class RoutingNumericEventArgs : EventArgs\n    {\n        /// <summary>\n        /// The numeric representation of the output, if applicable.\n        /// </summary>\n        public uint? Output { get; set; }\n        /// <summary>\n        /// The numeric representation of the input, if applicable.\n        /// </summary>\n        public uint? Input { get; set; }\n\n        /// <summary>\n        /// The type of signal involved in the routing change.\n        /// </summary>\n        public eRoutingSignalType SigType { get; set; }\n        /// <summary>\n        /// The input port involved in the routing change, if applicable.\n        /// </summary>\n        public RoutingInputPort InputPort { get; set; }\n        /// <summary>\n        /// The output port involved in the routing change, if applicable.\n        /// </summary>\n        public RoutingOutputPort OutputPort { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RoutingNumericEventArgs\"/> class using numeric identifiers.\n        /// </summary>\n        /// <param name=\"output\">The numeric output identifier.</param>\n        /// <param name=\"input\">The numeric input identifier.</param>\n        /// <param name=\"sigType\">The signal type.</param>\n        public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RoutingNumericEventArgs\"/> class using port objects.\n        /// </summary>\n        /// <param name=\"outputPort\">The output port object.</param>\n        /// <param name=\"inputPort\">The input port object.</param>\n        /// <param name=\"sigType\">The signal type.</param>\n        public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,\n            eRoutingSignalType sigType)\n            : this(null, null, outputPort, inputPort, sigType)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RoutingNumericEventArgs\"/> class with default values.\n        /// </summary>\n        public RoutingNumericEventArgs()\n            : this(null, null, null, null, 0)\n        {\n \n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RoutingNumericEventArgs\"/> class with potentially mixed identifiers.\n        /// </summary>\n        /// <param name=\"output\">The numeric output identifier (optional).</param>\n        /// <param name=\"input\">The numeric input identifier (optional).</param>\n        /// <param name=\"outputPort\">The output port object (optional).</param>\n        /// <param name=\"inputPort\">The input port object (optional).</param>\n        /// <param name=\"sigType\">The signal type.</param>\n        public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,\n            RoutingInputPort inputPort, eRoutingSignalType sigType)\n        {\n            OutputPort = outputPort;\n            InputPort = inputPort;\n\n            Output = output;\n            Input = input;\n            SigType = sigType;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a RoutingOutputPort\n    /// </summary>\n    public class RoutingOutputPort : RoutingPort\n    {\n        /// <summary>\n        /// The IRoutingOutputs object this port lives on.\n        /// </summary>\n        [JsonIgnore]\n        public IRoutingOutputs ParentDevice { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Tracks which destinations are currently using this output port.\n\t\t/// </summary>\n\t\tpublic InUseTracking InUseTracker { get; private set; }\n\n\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the <see cref=\"RoutingOutputPort\"/> class.\n\t\t/// </summary>\n\t\t/// <param name=\"key\">The unique key for this port.</param>\n\t\t/// <param name=\"type\">The signal type supported by this port.</param>\n\t\t/// <param name=\"connType\">The physical connection type of this port.</param>\n\t\t/// <param name=\"selector\">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>\n\t\t/// <param name=\"parent\">The <see cref=\"IRoutingOutputs\"/> device this port belongs to.</param>\n\t\tpublic RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,\n\t\t\tobject selector, IRoutingOutputs parent)\n\t\t\t: this(key, type, connType, selector, parent, false)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the <see cref=\"RoutingOutputPort\"/> class, potentially marking it as internal.\n\t\t/// </summary>\n\t\t/// <param name=\"key\">The unique key for this port.</param>\n\t\t/// <param name=\"type\">The signal type supported by this port.</param>\n\t\t/// <param name=\"connType\">The physical connection type of this port.</param>\n\t\t/// <param name=\"selector\">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>\n\t\t/// <param name=\"parent\">The <see cref=\"IRoutingOutputs\"/> device this port belongs to.</param>\n\t\t/// <param name=\"isInternal\">True if this port represents an internal connection within a device (e.g., card to backplane).</param>\n\t\tpublic RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,\n\t\t\tobject selector, IRoutingOutputs parent, bool isInternal)\n\t\t\t: base(key, type, connType, selector, isInternal)\n\t\t{\n            ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));\n\t\t\tInUseTracker = new InUseTracking();\n\t\t}\n\n        /// <summary>\n        /// Returns a string representation of the output port.\n        /// </summary>\n        /// <returns>A string in the format \"ParentDeviceKey|PortKey|SignalType|ConnectionType\".</returns>\n        public override string ToString()\n        {\n            return $\"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}\";\n        }\n\t}\n\n    /*public class RoutingOutputPort<TSelector> : RoutingPort<TSelector>\n    {\n        /// <summary>\n        /// Gets or sets the ParentDevice\n        /// </summary>\n        public IRoutingOutputs ParentDevice { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the InUseTracker\n        /// </summary>\n        public InUseTracking InUseTracker { get; private set; }\n\n\n        /// <summary>\n        /// </summary>\n        /// <param name=\"selector\">An object used to refer to this port in the IRouting device's ExecuteSwitch method.\n        /// May be string, number, whatever</param>\n        /// <param name=\"parent\">The IRoutingOutputs object this port lives on</param>\n        public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,\n            TSelector selector, IRoutingOutputs parent)\n            : this(key, type, connType, selector, parent, false)\n        {\n        }\n\n        public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,\n            TSelector selector, IRoutingOutputs parent, bool isInternal)\n            : base(key, type, connType, selector, isInternal)\n        {\n            ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));\n            InUseTracker = new InUseTracking();\n        }\n\n        /// <summary>\n        /// ToString method\n        /// </summary>\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return ParentDevice.Key + \":\" + Key;\n        }\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RoutingPort.cs",
    "content": "﻿using PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Base class for <see cref=\"RoutingInputPort\"/> and <see cref=\"RoutingOutputPort\"/>.\n    /// </summary>\n    public abstract class RoutingPort : IKeyed\n\t{\n\t\t/// <summary>\n\t\t/// The unique key identifying this port within its parent device.\n\t\t/// </summary>\n\t\tpublic string Key { get; private set; }\n\t\t/// <summary>\n\t\t/// The type of signal this port handles (e.g., Audio, Video, AudioVideo).\n\t\t/// </summary>\n\t\tpublic eRoutingSignalType Type { get; private set; }\n\t\t/// <summary>\n\t\t/// The physical connection type of this port (e.g., Hdmi, Rca, Dm).\n\t\t/// </summary>\n\t\tpublic eRoutingPortConnectionType ConnectionType { get; private set; }\n\t\t/// <summary>\n\t\t/// An object (often a number or string) used by the parent routing device to select this port during switching.\n\t\t/// </summary>\n\t\tpublic readonly object Selector;\n\t\t/// <summary>\n\t\t/// Indicates if this port represents an internal connection within a device (e.g., card to backplane).\n\t\t/// </summary>\n\t\tpublic bool IsInternal { get; private set; }\n        /// <summary>\n        /// An object used to match feedback values to this port, if applicable.\n        /// </summary>\n        public object FeedbackMatchObject { get; set; }\n        /// <summary>\n        /// A reference to the underlying hardware port object (e.g., SimplSharpPro Port), if applicable.\n        /// </summary>\n        public object Port { get; set; }\n\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the <see cref=\"RoutingPort\"/> class.\n\t\t/// </summary>\n\t\t/// <param name=\"key\">The unique key for this port.</param>\n\t\t/// <param name=\"type\">The signal type supported by this port.</param>\n\t\t/// <param name=\"connType\">The physical connection type of this port.</param>\n\t\t/// <param name=\"selector\">The selector object for switching.</param>\n\t\t/// <param name=\"isInternal\">True if this port is internal.</param>\n\t\tpublic RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, bool isInternal)\n\t\t{\n\t\t\tKey = key;\n\t\t\tType = type;\n\t\t\tConnectionType = connType;\n\t\t\tSelector = selector;\n\t\t\tIsInternal = isInternal;\n\t\t}        \n    }\n\n    /*public abstract class RoutingPort<TSelector>:IKeyed\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; private set; }\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public eRoutingSignalType Type { get; private set; }\n        /// <summary>\n        /// Gets or sets the ConnectionType\n        /// </summary>\n        public eRoutingPortConnectionType ConnectionType { get; private set; }\n        public readonly TSelector Selector;\n        /// <summary>\n        /// Gets or sets the IsInternal\n        /// </summary>\n        public bool IsInternal { get; private set; }\n        /// <summary>\n        /// Gets or sets the FeedbackMatchObject\n        /// </summary>\n        public object FeedbackMatchObject { get; set; }\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        public object Port { get; set; }\n\n        public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, TSelector selector, bool isInternal)\n        {\n            Key = key;\n            Type = type;\n            ConnectionType = connType;\n            Selector = selector;\n            IsInternal = isInternal;\n        }\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RoutingPortCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Represents a RoutingPortCollection, which is essentially a List with an indexer for case-insensitive lookup of ports by their key names.\n /// </summary>\n\tpublic class RoutingPortCollection<T> : List<T> where T: RoutingPort\n\t{\n\t\t/// <summary>\n\t\t/// Case-insensitive port lookup linked to ports' keys\n\t\t/// </summary>\n\t\tpublic T this[string key] \n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase));\n\t\t\t}\n\t\t}\n\t}\n\n/*    /// <summary>\n\t/// Basically a List , with an indexer to find ports by key name\n\t/// </summary>\n\tpublic class RoutingPortCollection<T, TSelector> : List<T> where T : RoutingPort<TSelector>\n    {\n        /// <summary>\n        /// Case-insensitive port lookup linked to ports' keys\n        /// </summary>\n        public T this[string key]\n        {\n            get\n            {\n                return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase));\n            }\n        }\n    }*/\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/RoutingPortNames.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Defines constant string values for common routing port keys.\n    /// These should correspond directly with the portNames var in the config tool.\n\t/// </summary>\n\tpublic class RoutingPortNames\n\t{\n        /// <summary>\n        /// antennaIn\n        /// </summary>\n\t\tpublic const string AntennaIn = \"antennaIn\";\n        /// <summary>\n        /// anyAudioIn\n        /// </summary>\n\t\tpublic const string AnyAudioIn = \"anyAudioIn\";\n        /// <summary>\n        /// anyAudioOut\n        /// </summary>\n\t\tpublic const string AnyAudioOut = \"anyAudioOut\";\n        /// <summary>\n        /// anyOut\n        /// </summary>\n        public const string AnyOut = \"anyOut\";\n        /// <summary>\n        /// anyVideoIn\n        /// </summary>\n        public const string AnyVideoIn = \"anyVideoIn\";\n        /// <summary>\n        /// anyVideoOut\n        /// </summary>\n\t\tpublic const string AnyVideoOut = \"anyVideoOut\";\n        /// <summary>\n        /// balancedAudioOut\n        /// </summary>\n\t\tpublic const string BalancedAudioOut = \"balancedAudioOut\";\n        /// <summary>\n        /// codecOsd\n        /// </summary>\n        public const string CodecOsd = \"codecOsd\";\n        /// <summary>\n        /// componentIn\n        /// </summary>\n\t\tpublic const string ComponentIn = \"componentIn\";\n        /// <summary>\n        /// componentOut\n        /// </summary>\n\t\tpublic const string ComponentOut = \"componentOut\";\n        /// <summary>\n        /// compositeIn\n        /// </summary>\n\t\tpublic const string CompositeIn = \"compositeIn\";\n        /// <summary>\n        /// compositeOut\n        /// </summary>\n\t\tpublic const string CompositeOut = \"compositeOut\";\n        /// <summary>\n        /// displayPortIn\n        /// </summary>\n\t\tpublic const string DisplayPortIn = \"displayPortIn\";\n        /// <summary>\n        /// displayPortIn1\n        /// </summary>\n\t\tpublic const string DisplayPortIn1 = \"displayPortIn1\";\n        /// <summary>\n        /// displayPortIn2\n        /// </summary>\n\t\tpublic const string DisplayPortIn2 = \"displayPortIn2\";\n        /// <summary>\n        /// displayPortIn3\n        /// </summary>\n\t\tpublic const string DisplayPortIn3 = \"displayPortIn3\";\n        /// <summary>\n        /// displayPortOut\n        /// </summary>\n\t\tpublic const string DisplayPortOut = \"displayPortOut\";\n        /// <summary>\n        /// dmIn\n        /// </summary>\n\t\tpublic const string DmIn = \"dmIn\";\n        /// <summary>\n        /// dmOut\n        /// </summary>\n\t\tpublic const string DmOut = \"dmOut\";\n        /// <summary>\n        /// dviIn\n        /// </summary>\n\t\tpublic const string DviIn = \"dviIn\";\n        /// <summary>\n        /// dviIn1\n        /// </summary>\n        public const string DviIn1 = \"dviIn1\";\n        /// <summary>\n        /// dviOut\n        /// </summary>\n        public const string DviOut = \"dviOut\";\n        /// <summary>\n        /// hdmiIn\n        /// </summary>\n\t\tpublic const string HdmiIn = \"hdmiIn\";\n        /// <summary>\n        /// hdmiIn1\n        /// </summary>\n\t\tpublic const string HdmiIn1 = \"hdmiIn1\";\n        /// <summary>\n        /// hdmiIn1PC\n        /// </summary>\n        public const string HdmiIn1PC = \"hdmiIn1PC\";\n        /// <summary>\n        /// hdmiIn2\n        /// </summary>\n\t\tpublic const string HdmiIn2 = \"hdmiIn2\";\n        /// <summary>\n        /// hdmiIn2PC\n        /// </summary>\n        public const string HdmiIn2PC = \"hdmiIn2PC\";\n        /// <summary>\n        /// hdmiIn3\n        /// </summary>\n\t\tpublic const string HdmiIn3 = \"hdmiIn3\";\n        /// <summary>\n        /// hdmiIn4\n        /// </summary>\n\t\tpublic const string HdmiIn4 = \"hdmiIn4\";\n        /// <summary>\n        /// hdmiIn5\n        /// </summary>\n\t\tpublic const string HdmiIn5 = \"hdmiIn5\";\n        /// <summary>\n        /// hdmiIn6\n        /// </summary>\n\t\tpublic const string HdmiIn6 = \"hdmiIn6\";\n        /// <summary>\n        /// hdmiOut\n        /// </summary>\n\t\tpublic const string HdmiOut = \"hdmiOut\";\n        /// <summary>\n        /// hdmiOut1\n        /// </summary>\n        public const string HdmiOut1 = \"hdmiOut1\";\n        /// <summary>\n        /// hdmiOut2\n        /// </summary>\n        public const string HdmiOut2 = \"hdmiOut2\";\n        /// <summary>\n        /// hdmiOut3\n        /// </summary>\n        public const string HdmiOut3 = \"hdmiOut3\";\n        /// <summary>\n        /// hdmiOut4\n        /// </summary>\n        public const string HdmiOut4 = \"hdmiOut4\";\n        /// <summary>\n        /// hdmiOut5\n        /// </summary>\n        public const string HdmiOut5 = \"hdmiOut5\";\n        /// <summary>\n        /// hdmiOut6\n        /// </summary>\n        public const string HdmiOut6 = \"hdmiOut6\";\n        /// <summary>\n        /// none\n        /// </summary>\n        public const string None = \"none\";\n        /// <summary>\n        /// rgbIn\n        /// </summary>\n\t\tpublic const string RgbIn = \"rgbIn\";\n        /// <summary>\n        /// rgbIn1\n        /// </summary>\n        public const string RgbIn1 = \"rgbIn1\";\n        /// <summary>\n        /// rgbIn2\n        /// </summary>\n        public const string RgbIn2 = \"rgbIn2\";\n        /// <summary>\n        /// vgaIn\n        /// </summary>\n\t\tpublic const string VgaIn = \"vgaIn\";\n        /// <summary>\n        /// vgaIn1\n        /// </summary>\n        public const string VgaIn1 = \"vgaIn1\";\n        /// <summary>\n        /// vgaOut\n        /// </summary>\n\t\tpublic const string VgaOut = \"vgaOut\";\n        /// <summary>\n        /// IPC/OPS\n        /// </summary>\n        public const string IpcOps = \"ipcOps\";\n        /// <summary>\n        /// MediaPlayer\n        /// </summary>\n        public const string MediaPlayer = \"mediaPlayer\";\n        /// <summary>\n        /// UsbCIn\n        /// </summary>\n        public const string UsbCIn = \"usbCIn\";\n        /// <summary>\n        /// UsbCIn1\n        /// </summary>\n        public const string UsbCIn1 = \"usbCIn1\";\n        /// <summary>\n        /// UsbCIn2\n        /// </summary>\n        public const string UsbCIn2 = \"usbCIn2\";\n        /// <summary>\n        /// UsbCIn3\n        /// </summary>\n        public const string UsbCIn3 = \"usbCIn3\";\n        /// <summary>\n        /// UsbCOut\n        /// </summary>\n        public const string UsbCOut = \"usbCOut\";\n        /// <summary>\n        /// UsbCOut1\n        /// </summary>\n        public const string UsbCOut1 = \"usbCOut1\";\n        /// <summary>\n        /// UsbCOut2\n        /// </summary>\n        public const string UsbCOut2 = \"usbCOut2\";\n        /// <summary>\n        /// UsbCOut3\n        /// </summary>\n        public const string UsbCOut3 = \"usbCOut3\";\n        /// <summary>\n        /// HdBaseTIn\n        /// </summary>\n        public const string HdBaseTIn = \"hdBaseTIn\";\n        /// <summary>\n        /// HdBaseTOut\n        /// </summary>\n        public const string HdBaseTOut = \"hdBaseTOut\";\n        /// <summary>\n        /// SdiIn\n        /// </summary>\n        public const string SdiIn = \"sdiIn\";\n        /// <summary>\n        /// SdiOut\n        /// </summary>\n        public const string SdiOut = \"sdiOut\";\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/TieLine.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a connection between routing ports, linking a source output port to a destination input port.\n    /// This class is used to define signal paths for routing algorithms, including signal type overrides and internal connections.\n    /// </summary>\n    public class TieLine\n    {\n        /// <summary>\n        /// The source output port of the tie line.\n        /// </summary>\n        public RoutingOutputPort SourcePort { get; private set; }\n        /// <summary>\n        /// The destination input port of the tie line.\n        /// </summary>\n        public RoutingInputPort DestinationPort { get; private set; }\n        //public int InUseCount { get { return DestinationUsingThis.Count; } }\n\n        /// <summary>\n        /// Gets the type of this tie line. Returns the intersection of signal types supported by both \n        /// the source and destination ports (what signals can actually travel through this tie line),\n        /// or the OverrideType when it is set.\n        /// </summary>\n        public eRoutingSignalType Type\n        {\n            get\n            {\n                if (OverrideType.HasValue) return OverrideType.Value;\n                return SourcePort.Type & DestinationPort.Type;\n            }\n        }\n\n        /// <summary>\n        /// Use this to override the Type property. For example, when both ports support AudioVideo\n        /// but the physical cable only carries Audio or Video, setting this will limit the signal \n        /// paths available to the routing algorithm without affecting the actual port types.\n        /// When set, this value is used instead of the calculated intersection of source and destination types.\n        /// </summary>\n        public eRoutingSignalType? OverrideType { get; set; }\n\n        //List<IRoutingInputs> DestinationUsingThis = new List<IRoutingInputs>();\n\n        /// <summary>\n        /// Gets a value indicating whether this tie line represents an internal connection within a device (both source and destination ports are internal).\n        /// </summary>\n        public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } }\n        /// <summary>\n        /// Gets a value indicating whether the signal types of the source and destination ports differ.\n        /// </summary>\n        public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } }\n        /// <summary>\n        /// Gets a value indicating whether the connection types of the source and destination ports differ.\n        /// </summary>\n        public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } }\n        /// <summary>\n        /// A descriptive note about any type mismatch, if applicable.\n        /// </summary>\n        public string TypeMismatchNote { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"TieLine\"/> class.\n        /// </summary>\n        /// <param name=\"sourcePort\">The source output port.</param>\n        /// <param name=\"destinationPort\">The destination input port.</param>\n        public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort)\n        {\n            if (sourcePort == null || destinationPort == null)\n                throw new ArgumentNullException(\"source or destination port\");\n            SourcePort = sourcePort;\n            DestinationPort = destinationPort;\n        }\n\n        /// <summary>\n        /// Creates a tie line with an overriding Type. See help for OverrideType property for info.\n        /// </summary>\n        /// <param name=\"sourcePort\">The source output port.</param>\n        /// <param name=\"destinationPort\">The destination input port.</param>\n        /// <param name=\"overrideType\">The signal type to limit the link to. Overrides the calculated intersection of port types for routing calculations.</param>\n        public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType? overrideType) :\n            this(sourcePort, destinationPort)\n        {\n            OverrideType = overrideType;\n        }\n\n        /// <summary>\n        /// Creates a tie line with an overriding Type. See help for OverrideType property for info.\n        /// </summary>\n        /// <param name=\"sourcePort\">The source output port.</param>\n        /// <param name=\"destinationPort\">The destination input port.</param>\n        /// <param name=\"overrideType\">The signal type to limit the link to. Overrides the calculated intersection of port types for routing calculations.</param>\n        public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) :\n            this(sourcePort, destinationPort)\n        {\n            OverrideType = overrideType;\n        }\n\n        /// <summary>\n        /// Will link up video status from supporting inputs to connected outputs.\n        /// </summary>\n        public void Activate()\n        {\n            // Now does nothing\n        }\n\n        /// <summary>\n        /// Deactivates the tie line.\n        /// </summary>\n        public void Deactivate()\n        {\n            // Now does nothing\n        }\n\n        /// <summary>\n        /// Returns a string representation of the tie line.\n        /// </summary>\n        /// <returns>A string describing the source, destination, and type of the tie line.</returns>\n        public override string ToString()\n        {\n            return string.Format(\"Tie line: {0}:{1} --> {2}:{3} {4}\", SourcePort.ParentDevice.Key, SourcePort.Key,\n                DestinationPort.ParentDevice.Key, DestinationPort.Key, Type.ToString());\n        }\n    }\n\n    //********************************************************************************\n\n    /// <summary>\n    /// Represents a collection of <see cref=\"TieLine\"/> objects, which define signal paths for routing algorithms.\n    /// This class provides functionality for managing tie lines and includes a singleton instance for global access.\n    /// </summary>\n    public class TieLineCollection : List<TieLine>\n    {\n        /// <summary>\n        /// Gets the default singleton instance of the <see cref=\"TieLineCollection\"/>.\n        /// </summary>\n        public static TieLineCollection Default\n        {\n            get\n            {\n                if (_Default == null)\n                    _Default = new TieLineCollection();\n                return _Default;\n            }\n        }\n\n        /// <summary>\n        /// Backing field for the singleton instance.\n        /// </summary>\n        [JsonIgnore]\n        private static TieLineCollection _Default;\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharpPro;\n\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Config\n{\n\t/// <summary>\n\t/// Represents the configuration data for a single tie line between two routing ports.\n\t/// </summary>\n\tpublic class TieLineConfig\n\t{\n\t\t/// <summary>\n\t\t/// The key of the source device.\n\t\t/// </summary>\n\t\tpublic string SourceKey { get; set; }\n\n\t\t/// <summary>\n\t\t/// The key of the source card (if applicable, e.g., in a modular chassis).\n\t\t/// </summary>\n\t\tpublic string SourceCard { get; set; }\n\n\t\t/// <summary>\n\t\t/// The key of the source output port, used for routing configurations.\n\t\t/// </summary>\n\t\tpublic string SourcePort { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the DestinationKey\n\t\t/// </summary>\n\t\tpublic string DestinationKey { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the DestinationCard\n\t\t/// </summary>\n\t\tpublic string DestinationCard { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the DestinationPort\n\t\t/// </summary>\n\t\tpublic string DestinationPort { get; set; }\n\n\t\t/// <summary>\n\t\t/// Optional override for the signal type of the tie line. If set, this overrides the calculated \n\t\t/// intersection of source and destination port types for routing calculations. Useful when the \n\t\t/// physical cable supports fewer signal types than both ports are capable of.\n\t\t/// </summary>\n\t\t[JsonProperty(\"type\", NullValueHandling = NullValueHandling.Ignore)]\n\t\t[JsonConverter(typeof(StringEnumConverter))]\n\t\tpublic eRoutingSignalType? OverrideType { get; set; }\n\n\t\t/// <summary>\n\t\t/// Returns the appropriate tie line for either a card-based device or \n\t\t/// regular device with ports on-device.\n\t\t/// </summary>\n\t\t/// <returns>null if config data does not match ports, cards or devices</returns>\n\t\tpublic TieLine GetTieLine()\n\t\t{\n\t\t\tDebug.LogInformation(\"Build TieLine: {config}\", ToString());\n\n\t\t\t// Get the source device\n\t\t\tif (!(DeviceManager.GetDeviceForKey(SourceKey) is IRoutingOutputs sourceDev))\n\t\t\t{\n\t\t\t\tLogError(\"Routable source not found\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Get the destination device\n\t\t\tif (!(DeviceManager.GetDeviceForKey(DestinationKey) is IRoutingInputs destDev))\n\t\t\t{\n\t\t\t\tLogError(\"Routable destination not found\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t//Get the source port\n\t\t\tvar sourceOutputPort = sourceDev.OutputPorts[SourcePort];\n\n\t\t\tif (sourceOutputPort == null)\n\t\t\t{\n\t\t\t\tLogError(\"Source does not contain port\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t//Get the Destination port\n\t\t\tvar destinationInputPort = destDev.InputPorts[DestinationPort];\n\n\t\t\tif (destinationInputPort == null)\n\t\t\t{\n\t\t\t\tLogError(\"Destination does not contain port\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Validate signal type compatibility\n\t\t\tif (OverrideType.HasValue)\n\t\t\t{\n\t\t\t\t// When override type is specified, both ports must support it\n\t\t\t\tif (!sourceOutputPort.Type.HasFlag(OverrideType.Value))\n\t\t\t\t{\n\t\t\t\t\tLogError($\"Override type '{OverrideType.Value}' is not supported by source port '{SourcePort}' (type: {sourceOutputPort.Type})\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tif (!destinationInputPort.Type.HasFlag(OverrideType.Value))\n\t\t\t\t{\n\t\t\t\t\tLogError($\"Override type '{OverrideType.Value}' is not supported by destination port '{DestinationPort}' (type: {destinationInputPort.Type})\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Without override type, ports must have at least one common signal type flag\n\t\t\t\tif ((sourceOutputPort.Type & destinationInputPort.Type) == 0)\n\t\t\t\t{\n\t\t\t\t\tLogError($\"Incompatible signal types: source port '{SourcePort}' (type: {sourceOutputPort.Type}) has no common signal types with destination port '{DestinationPort}' (type: {destinationInputPort.Type})\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn new TieLine(sourceOutputPort, destinationInputPort, OverrideType);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Logs an error message related to creating this tie line configuration.\n\t\t/// </summary>\n\t\t/// <param name=\"msg\">The specific error message.</param>\n\t\tprivate void LogError(string msg)\n\t\t{\n\t\t\tDebug.LogError(\"Cannot create tie line: {message}\", msg);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns a string representation of the tie line configuration.\n\t\t/// </summary>\n\t\t/// <returns>A string describing the source and destination of the configured tie line.</returns>\n\t\tpublic override string ToString()\n\t\t{\n\t\t\treturn $\"{SourceKey}.{SourceCard}.{SourcePort} --> {DestinationKey}.{DestinationCard}.{DestinationPort}\";\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/eRoutingPortConnectionType.cs",
    "content": "﻿namespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Enumeration of eRoutingPortConnectionType values\n    /// </summary>\n    public enum eRoutingPortConnectionType\n\t{\n        /// <summary>\n        /// No connection type\n        /// </summary>\n\t\tNone, \n        \n        /// <summary>\n        /// Backplane only connection\n        /// </summary>\n        BackplaneOnly, \n        \n        /// <summary>\n        /// Connection via cable\n        /// </summary>\n        DisplayPort, \n        \n        /// <summary>\n        /// DVI connection\n        /// </summary>\n        Dvi, \n        \n        /// <summary>\n        /// HDMI connection\n        /// </summary>\n        Hdmi, \n        \n        /// <summary>\n        /// RGB connection\n        /// </summary>\n        Rgb, \n        \n        /// <summary>\n        /// VGA connection\n        /// </summary>\n        Vga, \n        \n        /// <summary>\n        /// Line audio connection\n        /// </summary>\n        LineAudio, \n        \n        /// <summary>\n        /// Digital audio connection\n        /// </summary>\n        DigitalAudio, \n        \n        /// <summary>\n        /// SDI connection\n        /// </summary>\n        Sdi, \n\n        /// <summary>\n        /// Composite connection\n        /// </summary>\n\t\tComposite, \n        \n        /// <summary>\n        /// Component connection\n        /// </summary>\n        Component, \n        \n        /// <summary>\n        /// DM CAT connection\n        /// </summary>\n        DmCat, \n        \n        /// <summary>\n        /// DM MM Fiber connection\n        /// </summary>\n        DmMmFiber, \n        \n        /// <summary>\n        /// DM SM Fiber connection\n        /// </summary>\n        DmSmFiber, \n        \n        /// <summary>\n        /// Speaker connection\n        /// </summary>\n        Speaker, \n        \n        /// <summary>\n        /// Microphone connection\n        /// </summary>\n        Streaming, \n        \n        /// <summary>\n        /// USB-C connection\n        /// </summary>\n        UsbC, \n        \n        /// <summary>\n        /// HDBaseT connection\n        /// </summary>\n        HdBaseT\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs",
    "content": "﻿using System;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Enumeration of eRoutingSignalType values\n    /// </summary>\n    [Flags]\n    public enum eRoutingSignalType\n\t{\n        /// <summary>\n        /// Audio signal type\n        /// </summary>\n\t\tAudio = 1,\n\n        /// <summary>\n        /// Video signal type\n        /// </summary>\n\t\tVideo = 2,     \n\n        /// <summary>\n        /// AudioVideo signal type\n        /// </summary>\n\t\tAudioVideo = Audio | Video,\n\n        /// <summary>\n        /// Control signal type\n        /// </summary>\n        [Obsolete(\"UsbOutput is no longer supported and will be removed in a future release.\")]\n        UsbOutput = 8,\n\n        /// <summary>\n        /// Control signal type\n        /// </summary>\n        [Obsolete(\"UsbInput is no longer supported and will be removed in a future release.\")]\n        UsbInput = 16,\n\n        /// <summary>\n        /// Secondary audio signal type\n        /// </summary>\n        [Obsolete(\"SecondaryAudio is no longer supported and will be removed in a future release.\")]\n        SecondaryAudio = 32\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Secrets/CrestronGlobalSecretsProvider.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronDataStore;\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a CrestronGlobalSecretsProvider\n    /// </summary>\n    public class CrestronGlobalSecretsProvider : ISecretProvider\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; set; }\n        //Added for reference\n        /// <summary>\n        /// Gets or sets the Description\n        /// </summary>\n        public string Description { get; private set; }\n\n        /// <summary>\n        /// Constructor for CrestronGlobalSecretsProvider\n        /// </summary>\n        /// <param name=\"key\">The key for the secret provider</param>\n        public CrestronGlobalSecretsProvider(string key)\n        {\n            Key = key;\n            Description = String.Format(\"Default secret provider serving all local applications\");\n\n        }\n\n        static CrestronGlobalSecretsProvider()\n        {\n            //Added for future encrypted reference\n            var secureSupported = CrestronSecureStorage.Supported;\n\n            CrestronDataStoreStatic.InitCrestronDataStore();\n            if (secureSupported)\n            {\n                //doThingsFuture\n            }\n        }\n\n        /// <summary>\n        /// Set secret for item in the CrestronSecretsProvider\n        /// </summary>\n        /// <param name=\"key\">Secret Key</param>\n        /// <param name=\"value\">Secret Value</param>\n        public bool SetSecret(string key, object value)\n        {\n            var secret = value as string;\n            CrestronDataStore.CDS_ERROR returnCode;\n\n            if (String.IsNullOrEmpty(secret))\n            {\n                returnCode = CrestronDataStoreStatic.clearGlobal(key);\n                if (returnCode == CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, this, \"Successfully removed secret \\\"{0}\\\"\", secret);\n                    return true;\n                }\n            }\n\n            else\n            {\n                returnCode = CrestronDataStoreStatic.SetGlobalStringValue(key, secret);\n                if (returnCode == CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, this, \"Successfully set secret \\\"{0}\\\"\", secret);\n                    return true;\n                }\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, this, \"Unable to set secret for {0}:{1} - {2}\", Key, key, returnCode.ToString());\n            return false;\n        }\n\n        /// <summary>\n        /// Retrieve secret for item in the CrestronSecretsProvider\n        /// </summary>\n        /// <param name=\"key\">Secret Key</param>\n        /// <returns>ISecret Object containing key, provider, and value</returns>\n        \n        \n        \n        public ISecret GetSecret(string key)\n        {\n            string mySecret;\n            var getErrorCode = CrestronDataStoreStatic.GetGlobalStringValue(key, out mySecret);\n\n            switch (getErrorCode)\n            {\n                case CrestronDataStore.CDS_ERROR.CDS_SUCCESS:\n                    Debug.LogMessage(LogEventLevel.Verbose, this, \"Secret Successfully retrieved for {0}:{1}\", Key, key);\n                    return new CrestronSecret(key, mySecret, this);\n                default:\n                    Debug.LogMessage(LogEventLevel.Information, this, \"Unable to retrieve secret for {0}:{1} - {2}\",\n                        Key, key, getErrorCode.ToString());\n                    return null;\n            }\n        }\n\n        /// <summary>\n        /// Determine if a secret is present within the provider without retrieving it\n        /// </summary>\n        /// <param name=\"key\">Secret Key</param>\n        /// <returns>bool if present</returns>\n        public bool TestSecret(string key)\n        {\n            string mySecret;\n            return CrestronDataStoreStatic.GetGlobalStringValue(key, out mySecret) == CrestronDataStore.CDS_ERROR.CDS_SUCCESS;\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Secrets/CrestronLocalSecretsProvider.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronDataStore;\nusing PepperDash.Core;\nusing Crestron.SimplSharpPro;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a CrestronLocalSecretsProvider\n    /// </summary>\n    public class CrestronLocalSecretsProvider : ISecretProvider\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; set; }\n        //Added for reference\n        /// <summary>\n        /// Gets or sets the Description\n        /// </summary>\n        public string Description { get; private set; }\n\n        /// <summary>\n        /// Constructor for CrestronLocalSecretsProvider\n        /// </summary>\n        /// <param name=\"key\">The key for the secret provider</param>\n        public CrestronLocalSecretsProvider(string key)\n        {\n            Key = key;\n            Description = String.Format(\"Default secret provider serving Essentials Application {0}\", InitialParametersClass.ApplicationNumber);\n        }\n\n        static CrestronLocalSecretsProvider()\n        {\n            //Added for future encrypted reference\n            var secureSupported = CrestronSecureStorage.Supported;\n\n            CrestronDataStoreStatic.InitCrestronDataStore();\n            if (secureSupported)\n            {\n                //doThingsFuture\n            }\n        }\n\n        /// <summary>\n        /// Set secret for item in the CrestronSecretsProvider\n        /// </summary>\n        /// <param name=\"key\">Secret Key</param>\n        /// <param name=\"value\">Secret Value</param>\n        public bool SetSecret(string key, object value)\n        {\n            var secret = value as string;\n            CrestronDataStore.CDS_ERROR returnCode;\n\n            if (String.IsNullOrEmpty(secret))\n            {\n                returnCode = CrestronDataStoreStatic.clearLocal(key);\n                if (returnCode == CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, this, \"Successfully removed secret \\\"{0}\\\"\", secret);\n                    return true;\n                }\n            }\n\n            else  \n            {\n                returnCode = CrestronDataStoreStatic.SetLocalStringValue(key, secret);\n                if (returnCode == CrestronDataStore.CDS_ERROR.CDS_SUCCESS)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, this, \"Successfully set secret \\\"{0}\\\"\", secret);\n                    return true;\n                }\n            }\n\n            Debug.LogMessage(LogEventLevel.Information, this, \"Unable to set secret for {0}:{1} - {2}\", Key, key, returnCode.ToString());\n            return false; \n        }\n\n        /// <summary>\n        /// Retrieve secret for item in the CrestronSecretsProvider\n        /// </summary>\n        /// <param name=\"key\">Secret Key</param>\n        /// <returns>ISecret Object containing key, provider, and value</returns>\n        /// <summary>\n        /// GetSecret method\n        /// </summary>\n        public ISecret GetSecret(string key)\n        {\n            string mySecret;\n            var getErrorCode = CrestronDataStoreStatic.GetLocalStringValue(key, out mySecret);\n\n            switch (getErrorCode)\n            {\n                case CrestronDataStore.CDS_ERROR.CDS_SUCCESS:\n                    Debug.LogMessage(LogEventLevel.Verbose, this, \"Secret Successfully retrieved for {0}:{1}\", Key, key);\n                    return new CrestronSecret(key, mySecret, this);\n                default:\n                    Debug.LogMessage(LogEventLevel.Information, this, \"Unable to retrieve secret for {0}:{1} - {2}\",\n                        Key, key, getErrorCode.ToString());\n                    return null;\n            }\n        }\n\n        /// <summary>\n        /// Determine if a secret is present within the provider without retrieving it\n        /// </summary>\n        /// <param name=\"key\">Secret Key</param>\n        /// <returns>bool if present</returns>\n        /// <summary>\n        /// TestSecret method\n        /// </summary>\n        public bool TestSecret(string key)\n        {\n            string mySecret;\n            return CrestronDataStoreStatic.GetLocalStringValue(key, out mySecret) == CrestronDataStore.CDS_ERROR.CDS_SUCCESS;\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Secrets/CrestronSecret.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Special container class for CrestronSecret provider\n    /// </summary>\n    public class CrestronSecret : ISecret\n    {\n        /// <summary>\n        /// Gets the Provider\n        /// </summary>\n        public ISecretProvider Provider { get; private set; }\n\n        /// <summary>\n        /// Gets the Key\n        /// </summary>\n        public string Key { get; private set; }\n\n        /// <summary>\n        /// Gets the Value\n        /// </summary>\n        public object Value { get; private set; }\n\n        /// <summary>\n        /// Constructor for CrestronSecret\n        /// </summary>\n        /// <param name=\"key\">key for the secret</param>\n        /// <param name=\"value\">value of the secret</param>\n        /// <param name=\"provider\">provider of the secret</param>\n        public CrestronSecret(string key, string value, ISecretProvider provider)\n        {\n            Key = key;\n            Value = value;\n            Provider = provider;\n        }\n\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Secrets/Interfaces.cs",
    "content": "﻿using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for ISecretProvider\n    /// </summary>\n    public interface ISecretProvider : IKeyed\n    {\n        /// <summary>\n        /// Set secret value for provider by key\n        /// </summary>\n        /// <param name=\"key\">key of secret to set</param>\n        /// <param name=\"value\">value to set secret to</param>\n        /// <returns></returns>\n        bool SetSecret(string key, object value);\n\n        /// <summary>\n        /// Return object containing secret from provider\n        /// </summary>\n        /// <param name=\"key\">key of secret to retrieve</param>\n        /// <returns></returns>\n        ISecret GetSecret(string key);\n\n        /// <summary>\n        /// Verifies presence of secret\n        /// </summary>\n        /// <param name=\"key\">key of secret to chek</param>\n        /// <returns></returns>\n        bool TestSecret(string key);\n\n        /// <summary>\n        /// Description of the secrets provider\n        /// </summary>\n        string Description { get; }\n    }\n\n    /// <summary>\n    /// interface for delivering secrets in Essentials.\n    /// </summary>\n    public interface ISecret\n    {\n        /// <summary>\n        /// Instance of ISecretProvider that the secret belongs to\n        /// </summary>\n        ISecretProvider Provider { get; }\n\n        /// <summary>\n        /// Key of the secret in the provider\n        /// </summary>\n        string Key { get; }\n\n        /// <summary>\n        /// Value of the secret\n        /// </summary>\n        object Value { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Secrets/SecretsManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// SecretsManager static class\n    /// </summary>\n    public static class SecretsManager\n    {\n        /// <summary>\n        /// Gets the Secrets dictionary\n        /// </summary>\n        public static Dictionary<string, ISecretProvider> Secrets { get; private set; }\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        public static void Initialize()\n        {\n\n            AddSecretProvider(\"default\", new CrestronLocalSecretsProvider(\"default\"));\n\n            AddSecretProvider(\"CrestronGlobalSecrets\", new CrestronGlobalSecretsProvider(\"CrestronGlobalSecrets\"));\n\n            CrestronConsole.AddNewConsoleCommand(SetSecretProcess, \"setsecret\",\n                \"Adds secret to secrets provider\",\n                ConsoleAccessLevelEnum.AccessOperator);\n\n            CrestronConsole.AddNewConsoleCommand(UpdateSecretProcess, \"updatesecret\",\n                \"Updates secret in secrets provider\",\n                ConsoleAccessLevelEnum.AccessAdministrator);\n\n            CrestronConsole.AddNewConsoleCommand(DeleteSecretProcess, \"deletesecret\",\n                \"Deletes secret from secrest provider\",\n                ConsoleAccessLevelEnum.AccessAdministrator);\n\n            CrestronConsole.AddNewConsoleCommand(ListProviders, \"secretproviderlist\",\n                \"Return list of all valid secrets providers\",\n                ConsoleAccessLevelEnum.AccessAdministrator);\n\n            CrestronConsole.AddNewConsoleCommand(GetProviderInfo, \"secretproviderinfo\",\n                \"Return data about secrets provider\",\n                ConsoleAccessLevelEnum.AccessAdministrator);\n        }\n\n        static SecretsManager()\n        {\n            Secrets = new Dictionary<string, ISecretProvider>();\n        }\n\n        /// <summary>\n        /// Get Secret Provider from dictionary by key\n        /// </summary>\n        /// <param name=\"key\">Dictionary Key for provider</param>\n        /// <returns>ISecretProvider</returns>\n        /// <summary>\n        /// GetSecretProviderByKey method\n        /// </summary>\n        public static ISecretProvider GetSecretProviderByKey(string key)\n        {\n            ISecretProvider secret;\n\n            Secrets.TryGetValue(key, out secret);\n\n            if (secret == null)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, \"SecretsManager unable to retrieve SecretProvider with the key '{0}'\", key);\n            }\n            return secret;\n        }\n\n        /// <summary>\n        /// GetProviderInfo method\n        /// </summary>\n        public static void GetProviderInfo(string cmd)\n        {\n            string response;\n            var args = cmd.Split(' ');\n\n            if (cmd.Length == 0 || (args.Length == 1 && args[0] == \"?\"))\n            {\n                response = \"Returns data about secrets provider.  Format 'secretproviderinfo <provider>'\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            if (args.Length == 1)\n            {\n                var provider = GetSecretProviderByKey(args[0]);\n\n                if (provider == null)\n                {\n                    response = \"Invalid secrets provider key\";\n                    CrestronConsole.ConsoleCommandResponse(response);\n                    return;\n                }\n\n                response = String.Format(\"{0} : {1}\", provider.Key, provider.Description);\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n            }\n\n            response = \"Improper number of arguments\";\n            CrestronConsole.ConsoleCommandResponse(response);\n\n        }\n\n\n        /// <summary>\n        /// Console Command that returns all valid secrets in the essentials program.\n        /// </summary>\n        /// <param name=\"cmd\"></param>\n        /// <summary>\n        /// ListProviders method\n        /// </summary>\n        public static void ListProviders(string cmd)\n        {\n            var response = String.Empty;\n            var args = cmd.Split(' ');\n\n            if (cmd.Length == 0)\n            {\n                if (Secrets != null && Secrets.Count > 0)\n                {\n                    response = Secrets.Aggregate(response,\n                        (current, secretProvider) => current + (secretProvider.Key + \"\\n\\r\"));\n                }\n                else\n                {\n                    response = \"No Secrets Providers Available\";\n                }\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            if (args.Length == 1 && args[0] == \"?\")\n            {\n                response = \"Reports all valid and preset Secret providers\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n            }\n\n\n            response = \"Improper number of arguments\";\n            CrestronConsole.ConsoleCommandResponse(response);\n\n        }\n\n        /// <summary>\n        /// Add secret provider to secrets dictionary\n        /// </summary>\n        /// <param name=\"key\">Key of new entry</param>\n        /// <param name=\"provider\">New Provider Entry</param>\n        /// <summary>\n        /// AddSecretProvider method\n        /// </summary>\n        public static void AddSecretProvider(string key, ISecretProvider provider)\n        {\n            if (!Secrets.ContainsKey(key))\n            {\n                Secrets.Add(key, provider);\n                Debug.LogMessage(LogEventLevel.Debug, \"Secrets provider '{0}' added to SecretsManager\", key);\n                return;\n            }\n            Debug.LogMessage(LogEventLevel.Information, \"Unable to add Provider '{0}' to Secrets.  Provider with that key already exists\", key );\n        }\n\n        /// <summary>\n        /// Add secret provider to secrets dictionary, with optional overwrite parameter\n        /// </summary>\n        /// <param name=\"key\">Key of new entry</param>\n        /// <param name=\"provider\">New provider entry</param>\n        /// <param name=\"overwrite\">true to overwrite any existing providers in the dictionary</param>\n        /// <summary>\n        /// AddSecretProvider method\n        /// </summary>\n        public static void AddSecretProvider(string key, ISecretProvider provider, bool overwrite)\n        {\n            if (!Secrets.ContainsKey(key))\n            {\n                Secrets.Add(key, provider);\n                Debug.LogMessage(LogEventLevel.Debug, \"Secrets provider '{0}' added to SecretsManager\", key);\n                return;\n            }\n            if (overwrite)\n            {\n                Secrets.Add(key, provider);\n                Debug.LogMessage(LogEventLevel.Debug, \"Provider with the key '{0}' already exists in secrets.  Overwriting with new secrets provider.\", key);\n                return;\n            }\n            Debug.LogMessage(LogEventLevel.Information, \"Unable to add Provider '{0}' to Secrets.  Provider with that key already exists\", key);\n        }\n\n        private static void SetSecretProcess(string cmd)\n        {\n            string response;\n            var args = cmd.Split(' ');\n\n            if (args.Length == 0)\n            {\n                //some Instructional Text\n                response = \"Adds secrets to secret provider. Format 'setsecret <provider> <secretKey> <secret>'\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n            }\n\n            if (args.Length == 1 && args[0] == \"?\")\n            {\n                response = \"Adds secrets to secret provider. Format 'setsecret <provider> <secretKey> <secret>'\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n            }\n\n            if (args.Length < 3)\n            {\n                response =  \"Improper number of arguments\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            var provider = GetSecretProviderByKey(args[0]);\n\n            if (provider == null)\n            {\n                //someFail\n                response =  \"Provider key invalid\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            var key = args[1];\n            var secret = args[2];\n\n            CrestronConsole.ConsoleCommandResponse(SetSecret(provider, key, secret));\n        }\n\n        private static void UpdateSecretProcess(string cmd)\n        {\n            string response;\n            var args = cmd.Split(' ');\n\n            if (args.Length == 0)\n            {\n                //some Instructional Text\n                response = \"Updates secrets in secret provider. Format 'updatesecret <provider> <secretKey> <secret>'\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            if (args.Length == 1 && args[0] == \"?\")\n            {\n                response = \"Updates secrets in secret provider. Format 'updatesecret <provider> <secretKey> <secret>'\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n            }\n\n\n            if (args.Length < 3)\n            {\n                //someFail\n                response = \"Improper number of arguments\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            var provider = GetSecretProviderByKey(args[0]);\n\n            if (provider == null)\n            {\n                //someFail\n                response = \"Provider key invalid\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            var key = args[1];\n            var secret = args[2];\n\n            CrestronConsole.ConsoleCommandResponse(UpdateSecret(provider, key, secret));\n\n        }\n\n        private static string UpdateSecret(ISecretProvider provider, string key, string secret)\n        {\n            var secretPresent = provider.TestSecret(key);\n\n            Debug.LogMessage(LogEventLevel.Verbose, provider, \"SecretsProvider {0} {1} contain a secret entry for {2}\", provider.Key, secretPresent ? \"does\" : \"does not\", key);\n\n            if (!secretPresent)\n                return\n                    String.Format(\n                        \"Unable to update secret for {0}:{1} - Please use the 'SetSecret' command to modify it\");\n            var response = provider.SetSecret(key, secret)\n                ? String.Format(\n                    \"Secret successfully set for {0}:{1}\",\n                    provider.Key, key)\n                : String.Format(\n                    \"Unable to set secret for {0}:{1}\",\n                    provider.Key, key);\n            return response;\n        }\n\n        private static string SetSecret(ISecretProvider provider, string key, string secret)\n        {\n            var secretPresent = provider.TestSecret(key);\n\n            Debug.LogMessage(LogEventLevel.Verbose, provider, \"SecretsProvider {0} {1} contain a secret entry for {2}\", provider.Key, secretPresent ? \"does\" : \"does not\", key);\n\n            if (secretPresent)\n                return\n                    String.Format(\n                        \"Unable to set secret for {0}:{1} - Please use the 'UpdateSecret' command to modify it\");\n            var response = provider.SetSecret(key, secret)\n                ? String.Format(\n                    \"Secret successfully set for {0}:{1}\",\n                    provider.Key, key)\n                : String.Format(\n                    \"Unable to set secret for {0}:{1}\",\n                    provider.Key, key);\n            return response;\n\n        }\n\n        private static void DeleteSecretProcess(string cmd)\n        {\n            string response;\n            var args = cmd.Split(' ');\n\n            if (args.Length == 0)\n            {\n                //some Instructional Text\n                response = \"Deletes secrets in secret provider. Format 'deletesecret <provider> <secretKey>'\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n            if (args.Length == 1 && args[0] == \"?\")\n            {\n                response = \"Deletes secrets in secret provider. Format 'deletesecret <provider> <secretKey>'\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n            }\n\n\n\n            if (args.Length < 2)\n            {\n                //someFail\n                response =  \"Improper number of arguments\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            var provider = GetSecretProviderByKey(args[0]);\n\n            if (provider == null)\n            {\n                //someFail\n                response =  \"Provider key invalid\";\n                CrestronConsole.ConsoleCommandResponse(response);\n                return;\n\n            }\n\n            var key = args[1];\n\n\n            provider.SetSecret(key, \"\");\n            response = provider.SetSecret(key, \"\")\n                ? String.Format(\n                    \"Secret successfully deleted for {0}:{1}\",\n                    provider.Key, key)\n                : String.Format(\n                    \"Unable to delete secret for {0}:{1}\",\n                    provider.Key, key);\n            CrestronConsole.ConsoleCommandResponse(response);\n            return;\n\n\n        }\n    }\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Secrets/SecretsPropertiesConfig.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Provide a way to easily deserialize into a secret object from config\n    /// </summary>\n    public class SecretsPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Provider\n        /// </summary>\n        [JsonProperty(\"provider\")]\n        public string Provider { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        [JsonProperty(\"key\")]\n        public string Key { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Shades/Shade Interfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Core.Shades\n{\n    /// <summary>\n    /// Defines the contract for IShades\n    /// </summary>\n    public interface IShades\n    {\n\t\t/// <summary>\n\t\t/// List of shades controlled by this device\n\t\t/// </summary>\n        List<IShadesOpenCloseStop> Shades { get; }\n    }\n\n    /// <summary>\n    /// Requirements for a device that implements basic Open/Close/Stop shade control (Uses 3 relays)\n    /// </summary>\n    public interface IShadesOpenCloseStop\n    {\n\t\t/// <summary>\n\t\t/// Opens the shade\n\t\t/// </summary>\n        void Open();\n\n\t\t/// <summary>\n\t\t/// Closes the shade\n\t\t/// </summary>\n        void Close();\n\n\t\t/// <summary>\n\t\t/// Stops the shade\n\t\t/// </summary>\n        void Stop();\n    }\n\n\t/// <summary>\n\t/// Requirements for a device that implements Open/Close/Stop shade control with presets\n\t/// </summary>\n    public interface IShadesOpenClosePreset : IShadesOpenCloseStop\n    {\n\t\t/// <summary>\n\t\t/// Recalls the preset\n\t\t/// </summary>\n\t\t/// <param name=\"presetNumber\">preset number to recall</param>\n        void RecallPreset(uint presetNumber);\n\n\t\t/// <summary>\n\t\t/// Saves the preset\n\t\t/// </summary>\n\t\t/// <param name=\"presetNumber\">preset number to save</param>\n        void SavePreset(uint presetNumber);\n\n\t\t/// <summary>\n\t\t/// Label for the preset button\n\t\t/// </summary>\n        string StopOrPresetButtonLabel { get; }\n\n\t\t/// <summary>\n\t\t/// Event raised when a preset is recalled\n\t\t/// </summary>\n        event EventHandler PresetSaved;\n    }\n\n\n    /// <summary>\n    /// Defines the contract for IShadesRaiseLowerFeedback\n    /// </summary>\n    public interface IShadesRaiseLowerFeedback\n    {\n\t\t/// <summary>\n\t\t/// Feedback to indicate if the shade is lowering\n\t\t/// </summary>\n\t\tBoolFeedback ShadeIsLoweringFeedback { get; }\n\n\t\t/// <summary>\n\t\t/// Feedback to indicate if the shade is raising\n\t\t/// </summary>\n\t\tBoolFeedback ShadeIsRaisingFeedback { get; }\n    }\n\n\t/// <summary>\n\t/// Requirements for a shade/scene that is open or closed\n\t/// </summary>\n\tpublic interface IShadesOpenClosedFeedback: IShadesOpenCloseStop\n\t{\n\t\t/// <summary>\n\t\t/// Feedback to indicate if the shade is open\n\t\t/// </summary>\n\t\tBoolFeedback ShadeIsOpenFeedback { get; }\n\n\t\t/// <summary>\n\t\t/// Feedback to indicate if the shade is closed\n\t\t/// </summary>\n\t\tBoolFeedback ShadeIsClosedFeedback { get; }\n\t}\n\n\t/// <summary>\n\t/// Used to implement raise/stop/lower/stop from single button\n\t/// </summary>\n\tpublic interface IShadesStopOrMove\n\t{\n\t\t/// <summary>\n\t\t/// Raises the shade or stops it if it's already moving\n\t\t/// </summary>\n\t\tvoid OpenOrStop();\n\n\t\t/// <summary>\n\t\t/// Lowers the shade or stops it if it's already moving\n\t\t/// </summary>\n\t\tvoid CloseOrStop();\n\n\t\t/// <summary>\n\t\t/// Opens, closes, or stops the shade depending on current state\n\t\t/// </summary>\n\t\tvoid OpenCloseOrStop();\n\t}\n\n /// <summary>\n /// Defines the contract for IShadesStopFeedback\n /// </summary>\n\tpublic interface IShadesStopFeedback : IShadesOpenCloseStop\n\t{\n\t\t/// <summary>\n\t\t/// Feedback to indicate if the shade is stopped\n\t\t/// </summary>\n\t\tBoolFeedback IsStoppedFeedback { get; }\n\t}\t\n\t\n\t/// <summary>\n\t/// Requirements for position\n\t/// </summary>\n\tpublic interface IShadesPosition\n\t{\n\t\t/// <summary>\n\t\t/// Gets the current position of the shade\n\t\t/// </summary>\n\t\t/// <param name=\"value\">value of the position to set</param>\n\t\tvoid SetPosition(ushort value);\n\t}\n\n\t/// <summary>\n\t/// Basic feedback for shades position\n\t/// </summary>\n\tpublic interface IShadesFeedback: IShadesPosition, IShadesStopFeedback\n\t{\n\t\t/// <summary>\n\t\t/// Feedback to indicate the current position of the shade\n\t\t/// </summary>\n\t\tIntFeedback PositionFeedback { get; }\n\t}\n\n\t/// <summary>\n\t/// Feedback for scenes\n\t/// </summary>\n\tpublic interface ISceneFeedback\n\t{\n\t\t/// <summary>\n\t\t/// Runs the scene\n\t\t/// </summary>\n\t\tvoid Run();\n\n\t\t/// <summary>\n\t\t/// Feedback to indicate if all shades are at the scene position\n\t\t/// </summary>\n\t\tBoolFeedback AllAreAtSceneFeedback { get; }\n\t}\n\n\t/// <summary>\n\t/// Combines basic shade interfaces for Crestron Basic shades\n\t/// </summary>\n\tpublic interface ICrestronBasicShade : IShadesOpenClosedFeedback, \n\t\tIShadesStopOrMove, IShadesFeedback, IShadesRaiseLowerFeedback\n\t{\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Shades/ShadeBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.CrestronIO;\n\nnamespace PepperDash.Essentials.Core.Shades\n{\n    /// <summary>\n    /// Base class for shades\n    /// </summary>\n    [Obsolete(\"Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1\")]\n    public abstract class ShadeBase : EssentialsDevice, IShadesOpenCloseStop\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key of the shade device</param>\n        /// <param name=\"name\">name of the shade device</param>\n        public ShadeBase(string key, string name)\n            : base(key, name)\n        {\n\n        }\n\n        #region iShadesOpenClose Members\n\n        /// <summary>\n        /// Opens the shade\n        /// </summary>\n        public abstract void Open();\n\n        /// <summary>\n        /// Stops the shade\n        /// </summary>\n        public abstract void Stop();\n\n        /// <summary>\n        /// Closes the shade\n        /// </summary>\n        public abstract void Close();\n\n        #endregion\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/SigHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Helper class for various Sig events\n\t/// </summary>\n\tpublic class SigHelper\n\t{\n\t\t/// <summary>\n\t\t/// Runs action when Sig is pressed\n\t\t/// </summary>\n\t\t/// <param name=\"sig\">signal pressed</param>\n\t\t/// <param name=\"act\">action to run</param>\n\t\tpublic static void Pressed(Sig sig, Action act) { if (sig.BoolValue) act(); }\n\n\t\t/// <summary>\n\t\t/// Runs action when Sig is released\n\t\t/// </summary>\n\t\tpublic static void Released(Sig sig, Action act) { if (!sig.BoolValue) act(); }\n\n  /// <summary>\n  /// SetBoolOutAction method\n  /// </summary>\n\t\tpublic static void SetBoolOutAction(BoolOutputSig sig, Action<bool> a)\n\t\t{\n\t\t\tif (sig != null)\n\t\t\t\tsig.UserObject = a;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Safely clears action of non-null sig.\n\t\t/// </summary>\n\t\tpublic static void ClearBoolOutAction(BoolOutputSig sig)\n\t\t{\n\t\t\tif (sig != null)\n\t\t\t\tsig.UserObject = null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Does a timed ramp, where the time is scaled proportional to the \n\t\t/// remaining range to cover\n\t\t/// </summary>\n\t\t/// <param name=\"sig\">Ushort sig to scale</param>\n\t\t/// <param name=\"newLevel\">Level to go to</param>\n\t\t/// <param name=\"time\">In ms (not hundredths like Crestron Sig ramp function)</param>\n  /// <summary>\n  /// RampTimeScaled method\n  /// </summary>\n\t\tpublic static void RampTimeScaled(Sig sig, ushort newLevel, uint time)\n\t\t{\n\t\t\tushort level = sig.UShortValue;\n\t\t\tint diff = Math.Abs(level - newLevel);\n\t\t\tuint scaledTime = (uint)(diff * time / 65535);\n\t\t\tRamp(sig, newLevel, scaledTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Ramps signal\n\t\t/// </summary>\n\t\t/// <param name=\"sig\"></param>\n\t\t/// <param name=\"level\"></param>\n\t\t/// <param name=\"time\">In ms (not hundredths like Crestron Sig ramp function)</param>\n  /// <summary>\n  /// Ramp method\n  /// </summary>\n\t\tpublic static void Ramp(Sig sig, ushort level, uint time)\n\t\t{\n\t\t\tsig.CreateRamp(level, time / 10);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/SmartObjects/SmartObjectDPad.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core.SmartObjects\n{\n /// <summary>\n /// Represents a SmartObjectDPad\n /// </summary>\n\tpublic class SmartObjectDPad : SmartObjectHelperBase\n\t{\n    /// <summary>\n    /// Gets or sets the SigUp\n    /// </summary>\n\t\tpublic BoolOutputSig SigUp { get { return GetBoolOutputNamed(\"Up\"); } }\n\n    /// <summary>\n    /// Gets or sets the SigDown\n    /// </summary>\n\t\tpublic BoolOutputSig SigDown { get { return GetBoolOutputNamed(\"Down\"); } }\n\n    /// <summary>\n    /// Gets or sets the SigLeft\n    /// </summary>\n\t\tpublic BoolOutputSig SigLeft { get { return GetBoolOutputNamed(\"Left\"); } }\n\n    /// <summary>\n    /// Gets or sets the SigRight\n    /// </summary>\n\t\tpublic BoolOutputSig SigRight { get { return GetBoolOutputNamed(\"Right\"); } }\n\n    /// <summary>\n    /// Gets or sets the SigCenter\n    /// </summary>\n\t\tpublic BoolOutputSig SigCenter { get { return GetBoolOutputNamed(\"Center\"); } }\n\n    /// <summary>\n    /// Constructor\n    /// </summary>\n    /// <param name=\"so\">smart object</param>\n    /// <param name=\"useUserObjectHandler\">use user object handler if true</param>\n\t\tpublic SmartObjectDPad(SmartObject so, bool useUserObjectHandler)\n\t\t\t: base(so, useUserObjectHandler)\n\t\t{\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/SmartObjects/SmartObjectDynamicList.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core.SmartObjects\n{\n /// <summary>\n /// Represents a SmartObjectDynamicList\n /// </summary>\n\tpublic class SmartObjectDynamicList : SmartObjectHelperBase\n\t{\n\t\t/// <summary>\n\t\t/// Sig name for Scroll To Item\n\t\t/// </summary>\n\t\tpublic const string SigNameScrollToItem = \"Scroll To Item\";\n\n\t\t/// <summary>\n\t\t/// Sig name for Set Number of Items\n\t\t/// </summary>\n\t\tpublic const string SigNameSetNumberOfItems = \"Set Number of Items\";\n\n\t\t/// <summary>\n\t\t/// Gets or sets the NameSigOffset\n\t\t/// </summary>\n\t\tpublic uint NameSigOffset { get; private set; }\n\n\t\t///\t<summary>\n\t\t/// Gets or sets the Count\n\t\t/// </summary>\t\n\t\tpublic ushort Count \n\t\t{\n\t\t\tget \n\t\t\t{\t\n\t\t\t\treturn SmartObject.UShortInput[SigNameSetNumberOfItems].UShortValue; \n\t\t\t}\n\t\t\tset { SmartObject.UShortInput[SigNameSetNumberOfItems].UShortValue = value; }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets the MaxCount\n\t\t/// </summary>\n\t\tpublic int MaxCount { get; private set; }\n\n        /// <summary>\n        /// Wrapper for smart object\n        /// </summary>\n        /// <param name=\"so\"></param>\n        /// <param name=\"useUserObjectHandler\">True if the standard user object action handler will be used</param>\n        /// <param name=\"nameSigOffset\">The starting join of the string sigs for the button labels</param>\n\t\tpublic SmartObjectDynamicList(SmartObject so, bool useUserObjectHandler, uint nameSigOffset) : base(so, useUserObjectHandler) \n\t\t{\t\t\n\t\t\ttry\n\t\t\t{\n\t\t\t\t// Just try to touch the count signal to make sure this is indeed a dynamic list\n\t\t\t\tvar c = Count;\n\t\t\t\tNameSigOffset = nameSigOffset;\n\t\t\t\tMaxCount = SmartObject.BooleanOutput.Count(s => s.Name.EndsWith(\"Pressed\"));\n\t\t\t\t//Debug.LogMessage(LogEventLevel.Verbose, \"Smart object {0} has {1} max\", so.ID, MaxCount);\n\t\t\t}\n\t\t\tcatch\n\t\t\t{\n\t\t\t\tvar msg = string.Format(\"SmartObjectDynamicList: Smart Object {0:X2}-{1} is not a dynamic list. Ignoring\", so.Device.ID, so.ID);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, msg);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SetItem method\n\t\t/// </summary>\n\t\tpublic void SetItem(uint index, string mainText, string iconName, Action<bool> action)\n\t\t{\n\t\t\tSetItemMainText(index, mainText);\n\t\t\tSetItemIcon(index, iconName);\n\t\t\tSetItemButtonAction(index, action);\n\t\t\t//try\n\t\t\t//{\n\t\t\t//    SetMainButtonText(index, text);\n\t\t\t//    SetIcon(index, iconName);\n\t\t\t//    SetButtonAction(index, action);\n\t\t\t//}\n\t\t\t//catch(Exception e)\n\t\t\t//{\n\t\t\t//    Debug.LogMessage(LogEventLevel.Debug, \"Cannot set Dynamic List item {0} on smart object {1}\", index, SmartObject.ID);\n\t\t\t//    ErrorLog.Warn(e.ToString());\n\t\t\t//}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SetItemMainText method\n\t\t/// </summary>\n\t\tpublic void SetItemMainText(uint index, string text)\n\t\t{\n\t\t\tif (index > MaxCount) return;\n\t\t\t// The list item template defines CIPS tags that refer to standard joins\n\t\t\t(SmartObject.Device as BasicTriList).StringInput[NameSigOffset + index].StringValue = text;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SetItemIcon method\n\t\t/// </summary>\n\t\tpublic void SetItemIcon(uint index, string iconName)\n\t\t{\n\t\t\tif (index > MaxCount) return;\n\t\t\tSmartObject.StringInput[string.Format(\"Set Item {0} Icon Serial\", index)].StringValue = iconName;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SetItemButtonAction method\n\t\t/// </summary>\n\t\tpublic void SetItemButtonAction(uint index, Action<bool> action)\n\t\t{\n\t\t\tif (index > MaxCount) return;\n\t\t\tSmartObject.BooleanOutput[string.Format(\"Item {0} Pressed\", index)].UserObject = action;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SetFeedback method\n\t\t/// </summary>\n\t\tpublic void SetFeedback(uint index, bool interlocked)\n\t\t{\n\t\t\tif (interlocked) \n\t\t\t\tClearFeedbacks();\n\t\t\tSmartObject.BooleanInput[string.Format(\"Item {0} Selected\", index)].BoolValue = true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ClearFeedbacks method\n\t\t/// </summary>\n\t\tpublic void ClearFeedbacks()\n\t\t{\n\t\t\tfor(int i = 1; i<= Count; i++)\n\t\t\t\tSmartObject.BooleanInput[string.Format(\"Item {0} Selected\", i)].BoolValue = false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes Action object from all buttons\n\t\t/// </summary>\n\t\tpublic void ClearActions()\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Verbose, \"SO CLEAR\");\n\t\t\tfor(ushort i = 1; i <= MaxCount; i++)\n\t\t\t\tSmartObject.BooleanOutput[string.Format(\"Item {0} Pressed\", i)].UserObject = null;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/SmartObjects/SmartObjectHelperBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.SmartObjects\n{\n\t/// <summary>\n\t/// Represents a SmartObjectHelperBase\n\t/// </summary>\n\tpublic class SmartObjectHelperBase\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the SmartObject\n\t\t/// </summary>\n\t\tpublic SmartObject SmartObject { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Validated\n\t\t/// </summary>\n\t\tpublic bool Validated { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"so\">smart object</param>\n\t\t/// <param name=\"useUserObjectHandler\">use the user object hadnler if true</param>\n\t\tpublic SmartObjectHelperBase(SmartObject so, bool useUserObjectHandler)\n\t\t{\n\t\t\tSmartObject = so;\n\t\t\tif (useUserObjectHandler)\n\t\t\t{\n\t\t\t\t// Prevent this from double-registering\n\t\t\t\tSmartObject.SigChange -= this.SmartObject_SigChange;\n\t\t\t\tSmartObject.SigChange += this.SmartObject_SigChange;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Destructor\n\t\t/// </summary>\n\t\t~SmartObjectHelperBase()\n\t\t{\n\t\t\tSmartObject.SigChange -= this.SmartObject_SigChange;\n\t\t}\n\n        /// <summary>\n        /// Helper to get a sig name with debugging when fail\n        /// </summary>\n        /// <param name=\"name\"></param>\n        /// <returns></returns>\n\t\tpublic BoolOutputSig GetBoolOutputNamed(string name)\n\t\t{\n\t\t\tif (SmartObject.BooleanOutput.Contains(name))\n\t\t\t\treturn SmartObject.BooleanOutput[name];\n            else\n                Debug.LogMessage(LogEventLevel.Information, \"WARNING: Cannot get signal. Smart object {0} on trilist {1:x2} does not contain signal '{2}'\",\n                    SmartObject.ID, SmartObject.Device.ID, name);\n\t\t\treturn null;\n\t\t}\n\n        /// <summary>\n        /// Sets action on signal after checking for existence.\n        /// </summary>\n        /// <param name=\"name\"></param>\n        /// <param name=\"a\"></param>\n        /// <summary>\n        /// SetBoolAction method\n        /// </summary>\n        public void SetBoolAction(string name, Action<bool> a)\n        {\n            if (SmartObject.BooleanOutput.Contains(name))\n                SmartObject.BooleanOutput[name].UserObject = a;\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"WARNING: Cannot set action. Smart object {0} on trilist {1:x2} does not contain signal '{2}'\",\n                    SmartObject.ID, SmartObject.Device.ID, name);\n            }\n        }\n\n\t\t/// <summary>\n\t\t/// Standard Action listener\n\t\t/// </summary>\n\t\t/// <param name=\"currentDevice\"></param>\n\t\t/// <param name=\"args\"></param>\n\t\tvoid SmartObject_SigChange(GenericBase currentDevice, SmartObjectEventArgs args)\n\t\t{\n\t\t\tvar uo = args.Sig.UserObject;\n\t\t\tif (uo is Action<bool>)\n\t\t\t\t(uo as Action<bool>)(args.Sig.BoolValue);\n\t\t\telse if (uo is Action<ushort>)\n\t\t\t\t(uo as Action<ushort>)(args.Sig.UShortValue);\n\t\t\telse if (uo is Action<string>)\n\t\t\t\t(uo as Action<string>)(args.Sig.StringValue);\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/SmartObjects/SmartObjectNumeric.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core.SmartObjects\n{\n      /// <summary>\n      /// Represents a SmartObjectNumeric\n      /// </summary>\n\tpublic class SmartObjectNumeric : SmartObjectHelperBase\n      {\n            /// <summary>\n            /// Gets or sets the Misc1SigName\n            /// </summary>\n            public string Misc1SigName { get; set; }\n\n            /// <summary>\n            /// Gets or sets the Misc2SigName\n            /// </summary>\n            public string Misc2SigName { get; set; }\n\n            /// <summary>\n            /// Gets or sets the Digit1\n            /// </summary>\n            public BoolOutputSig Digit1 { get { return GetBoolOutputNamed(\"1\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit2\n            /// </summary>\n            public BoolOutputSig Digit2 { get { return GetBoolOutputNamed(\"2\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit3\n            /// </summary>\n            public BoolOutputSig Digit3 { get { return GetBoolOutputNamed(\"3\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit4\n            /// </summary>\n            public BoolOutputSig Digit4 { get { return GetBoolOutputNamed(\"4\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit5\n            /// </summary>\n            public BoolOutputSig Digit5 { get { return GetBoolOutputNamed(\"5\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit6\n            /// </summary>\n            public BoolOutputSig Digit6 { get { return GetBoolOutputNamed(\"6\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit7\n            /// </summary>\n            public BoolOutputSig Digit7 { get { return GetBoolOutputNamed(\"7\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit8\n            /// </summary>\n            public BoolOutputSig Digit8 { get { return GetBoolOutputNamed(\"8\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit9\n            /// </summary>\n            public BoolOutputSig Digit9 { get { return GetBoolOutputNamed(\"9\"); } }\n\n            /// <summary>\n            /// Gets or sets the Digit0\n            /// </summary>\n            public BoolOutputSig Digit0 { get { return GetBoolOutputNamed(\"0\"); } }\n\n            /// <summary>\n            /// Gets or sets the Misc1\n            /// </summary>\n            public BoolOutputSig Misc1 { get { return GetBoolOutputNamed(Misc1SigName); } }\n\n            /// <summary>\n            /// Gets or sets the Misc2\n            /// </summary>\n            public BoolOutputSig Misc2 { get { return GetBoolOutputNamed(Misc2SigName); } }\n\n            /// <summary>\n            /// Constructor\n            /// </summary>\n            /// <param name=\"so\">smart object</param>\n            /// <param name=\"useUserObjectHandler\">use user handler if true</param>\n            public SmartObjectNumeric(SmartObject so, bool useUserObjectHandler) : base(so, useUserObjectHandler)\n            {\n                  Misc1SigName = \"Misc_1\";\n                  Misc2SigName = \"Misc_2\";\n            }\n      }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/SmartObjects/SubpageReferencList/SourceListSubpageReferenceList.cs",
    "content": "﻿\n\n\n//using System;\n//using System.Collections.Generic;\n//using System.Linq;\n//using System.Text;\n//using Crestron.SimplSharp;\n//using Crestron.SimplSharpPro;\n//using Crestron.SimplSharpPro.DeviceSupport;\n//using Crestron.SimplSharpPro.UI;\n\n//using PepperDash.Core;\n\n\n//namespace PepperDash.Essentials.Core\n//{\n\n//    //*****************************************************************************\n//    /// <summary>\n//    /// Wrapper class for subpage reference list.  Contains helpful methods to get at the various signal groupings\n//    /// and to get individual signals using an index and a join.\n//    /// </summary>\n//    public class SourceListSubpageReferenceList : SubpageReferenceList\n//    {\n//        public const uint SmartObjectJoin = 3801;\n\n//        Action<uint> SourceSelectCallback;\n\n//        EssentialsRoom CurrentRoom;\n\n//        public SourceListSubpageReferenceList(BasicTriListWithSmartObject tl, \n//            Action<uint> sourceSelectCallback)\n//            : base(tl, SmartObjectJoin, 3, 1, 3)\n//        {\n//            SourceSelectCallback = sourceSelectCallback;\n//        }\n\n//        void SetSourceList(Dictionary<uint, IPresentationSource> dict)\n//        {\n//            // Iterate all positions, including ones missing from the dict.\n//            var max = dict.Keys.Max();\n//            for (uint i = 1; i <= max; i++)\n//            {\n//                // Add the source if it's in the dict\n//                if (dict.ContainsKey(i))\n//                {\n//                    Items.Add(new SourceListSubpageReferenceListItem(i, dict[i], this, SourceSelectCallback));\n//                    // Plug the callback function into the buttons\n//                }\n//                // Blank the line\n//                else\n//                    Items.Add(new SourceListSubpageReferenceListItem(i, null, \n//                        this, SourceSelectCallback));\n//            }\n//            Count = (ushort)max;\n//        }\n\n//        /// <summary>\n//        /// Links the SRL to the Room's PresentationSourceChange event for updating of the UI\n//        /// </summary>\n//        /// <param name=\"room\"></param>\n//        public void AttachToRoom(EssentialsRoom room)\n//        {\n//            CurrentRoom = room;\n//            SetSourceList(room.Sources);\n//            CurrentRoom.PresentationSourceChange -= CurrentRoom_PresentationSourceChange;\n//            CurrentRoom.PresentationSourceChange += CurrentRoom_PresentationSourceChange;\n//            SetPresentationSourceFb(CurrentRoom.CurrentPresentationSource);\n//        }\n\n//        /// <summary>\n//        /// Disconnects the SRL from a Room's PresentationSourceChange\n//        /// </summary>\n//        public void DetachFromCurrentRoom()\n//        {\n//            ClearPresentationSourceFb(CurrentRoom.CurrentPresentationSource);\n//            if(CurrentRoom != null)\n//                CurrentRoom.PresentationSourceChange -= CurrentRoom_PresentationSourceChange;\n//            CurrentRoom = null;\n//        }\n\n//        // Handler to route source changes into list feedback\n//        void CurrentRoom_PresentationSourceChange(object sender, EssentialsRoomSourceChangeEventArgs args)\n//        {\n//            Debug.LogMessage(LogEventLevel.Verbose, \"SRL received source change\");\n//            ClearPresentationSourceFb(args.OldSource);\n//            SetPresentationSourceFb(args.NewSource);\n//        }\n\n//        void ClearPresentationSourceFb(IPresentationSource source)\n//        {\n//            if (source == null) return;\n//            var oldSourceItem = (SourceListSubpageReferenceListItem)Items.FirstOrDefault(\n//                i => ((SourceListSubpageReferenceListItem)i).SourceDevice == source);\n//            if (oldSourceItem != null)\n//                oldSourceItem.ClearFeedback();\n//        }\n\n//        void SetPresentationSourceFb(IPresentationSource source)\n//        {\n//            if (source == null) return;\n//            // Now set the new source to light up\n//            var newSourceItem = (SourceListSubpageReferenceListItem)Items.FirstOrDefault(\n//                i => ((SourceListSubpageReferenceListItem)i).SourceDevice == source);\n//            if (newSourceItem != null)\n//                newSourceItem.SetFeedback();\n//        }\n//    }\n\n//    public class SourceListSubpageReferenceListItem : SubpageReferenceListItem\n//    {\n//        public readonly IPresentationSource SourceDevice;\n\n//        public const uint ButtonPressJoin = 1;\n//        public const uint SelectedFeedbackJoin = 2;\n//        public const uint ButtonTextJoin = 1;\n//        public const uint IconNameJoin = 2;\n\n//        public SourceListSubpageReferenceListItem(uint index,\n//            IPresentationSource srcDevice, SubpageReferenceList owner, Action<uint> sourceSelectCallback)\n//            : base(index, owner)\n//        {\n//            if (srcDevice == null) throw new ArgumentNullException(\"srcDevice\");\n//            if (owner == null) throw new ArgumentNullException(\"owner\");\n//            if (sourceSelectCallback == null) throw new ArgumentNullException(\"sourceSelectCallback\");\n\n\n//            SourceDevice = srcDevice;\n//            var nameSig = owner.StringInputSig(index, ButtonTextJoin);\n//            // Should be able to see if there is not enough buttons right here\n//            if (nameSig == null)\n//            {\n//                Debug.LogMessage(LogEventLevel.Information, \"ERROR: Item {0} does not exist on source list SRL\", index);\n//                return;\n//            }\n//            nameSig.StringValue = srcDevice.Name;\n//            owner.StringInputSig(index, IconNameJoin).StringValue = srcDevice.IconName;\n\n//            // Assign a source selection action to the appropriate button's UserObject - on release\n//            owner.GetBoolFeedbackSig(index, ButtonPressJoin).UserObject = new Action<bool>(b => \n//                { if (!b) sourceSelectCallback(index); });\n\t\t\t\n//            // hook up the video icon\n//            var videoDev = srcDevice as IAttachVideoStatus;\n//            if (videoDev != null)\n//            {\n//                var status = videoDev.GetVideoStatuses();\n//                if (status != null)\n//                {\n//                    Debug.LogMessage(LogEventLevel.Debug, \"Linking {0} video status to SRL\", videoDev.Key);\n//                    videoDev.GetVideoStatuses().VideoSyncFeedback.LinkInputSig(owner.BoolInputSig(index, 3));\n//                }\n//            }\n//        }\n\n//        public void SetFeedback()\n//        {\n//            Owner.BoolInputSig(Index, SelectedFeedbackJoin).BoolValue = true;\n//        }\n\n//        public void ClearFeedback()\n//        {\n//            Owner.BoolInputSig(Index, SelectedFeedbackJoin).BoolValue = false;\n//        }\n//    }\n//}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/SmartObjects/SubpageReferencList/SubpageReferenceList.cs",
    "content": "﻿\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Crestron.SimplSharpPro.UI;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\t////*****************************************************************************\n\t///// <summary>\n\t///// Base class for all subpage reference list controllers\n\t///// </summary>\n\t//public class SubpageReferenceListController\n\t//{\n\t//    public SubpageReferenceList TheList { get; protected set; }\n\t//}\n\n\t//*****************************************************************************\n\t/// <summary>\n\t/// Wrapper class for subpage reference list.  Contains helpful methods to get at the various signal groupings\n\t/// and to get individual signals using an index and a join.\n\t/// </summary>\n\tpublic class SubpageReferenceList\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the Count\n\t\t/// </summary>\n\t\tpublic ushort Count\n\t\t{\n\t\t\tget { return SetNumberOfItemsSig.UShortValue; }\n\t\t\tset { SetNumberOfItemsSig.UShortValue = value; }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets the MaxDefinedItems\n\t\t/// </summary>\n\t\tpublic ushort MaxDefinedItems { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the ScrollToItemSig\n\t\t/// </summary>\n\t\tpublic UShortInputSig ScrollToItemSig { get; private set; }\n\n\t\tUShortInputSig SetNumberOfItemsSig;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the BoolIncrement\n\t\t/// </summary>\n\t\tpublic uint BoolIncrement { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the UShortIncrement\n\t\t/// </summary>\n\t\tpublic uint UShortIncrement { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the StringIncrement\n\t\t/// </summary>\n\t\tpublic uint StringIncrement { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the SRL\n\t\t/// </summary>\n\t\tprotected readonly SmartObject SRL;\n\n\t\t/// <summary>\n\t\t/// Gets the list of items in the SRL\n\t\t/// </summary>\n\t\tprotected readonly List<SubpageReferenceListItem> Items = new List<SubpageReferenceListItem>();\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"triList\">trilist for the smart object</param>\n\t\t/// <param name=\"smartObjectId\">smart object ID</param>\n\t\t/// <param name=\"boolIncrement\"></param>\n\t\t/// <param name=\"ushortIncrement\"></param>\n\t\t/// <param name=\"stringIncrement\"></param>\n\t\tpublic SubpageReferenceList(BasicTriListWithSmartObject triList, uint smartObjectId,\n\t\t\tuint boolIncrement, uint ushortIncrement, uint stringIncrement)\n\t\t{\n\t\t\tSmartObject obj;\n\t\t\t// Fail cleanly if not defined\n\t\t\tif (triList.SmartObjects == null || triList.SmartObjects.Count == 0)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"TriList {0:X2} Smart objects have not been loaded\", triList.ID, smartObjectId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (triList.SmartObjects.TryGetValue(smartObjectId, out obj))\n\t\t\t{\n\t\t\t\tSRL = triList.SmartObjects[smartObjectId];\n\t\t\t\tScrollToItemSig = SRL.UShortInput[\"Scroll To Item\"];\n\t\t\t\tSetNumberOfItemsSig = SRL.UShortInput[\"Set Number of Items\"];\n\t\t\t\tBoolIncrement = boolIncrement;\n\t\t\t\tUShortIncrement = ushortIncrement;\n\t\t\t\tStringIncrement = stringIncrement;\n\n\t\t\t\t// Count the enable lines to see what max items is\n\t\t\t\tMaxDefinedItems = (ushort)SRL.BooleanInput\n\t\t\t\t\t.Where(s => s.Name.Contains(\"Enable\")).Count();\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, \"SRL {0} contains max {1} items\", SRL.ID, MaxDefinedItems);\n\n\t\t\t\tSRL.SigChange -= new SmartObjectSigChangeEventHandler(SRL_SigChange);\n\t\t\t\tSRL.SigChange += new SmartObjectSigChangeEventHandler(SRL_SigChange);\n\t\t\t}\n\t\t\telse\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, \"ERROR: TriList 0x{0:X2} Cannot load smart object {1}. Verify correct SGD file is loaded\",\n\t\t\t\t\t\t\t\t\t\ttriList.ID, smartObjectId);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds item to saved list of displayed items (not necessarily in order)\n\t\t/// DOES NOT adjust Count\n\t\t/// </summary>\n\t\t/// <param name=\"item\"></param>\n\t\t/// <summary>\n\t\t/// AddItem method\n\t\t/// </summary>\n\t\tpublic void AddItem(SubpageReferenceListItem item)\n\t\t{\n\t\t\tItems.Add(item);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Clear method\n\t\t/// </summary>\n\t\tpublic void Clear()\n\t\t{\n\t\t\t// If a line item needs to disconnect an CueActionPair or do something to release RAM\n\t\t\tforeach (var item in Items) item.Clear();\n\t\t\t// Empty the list\n\t\t\tItems.Clear();\n\t\t\t// Clean up the SRL\n\t\t\tCount = 1;\n\n\t\t\tScrollToItemSig.UShortValue = 1;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Refresh method\n\t\t/// </summary>\n\t\tpublic void Refresh()\n\t\t{\n\t\t\tforeach (var item in Items) item.Refresh();\n\t\t}\n\n\n\t\t// Helpers to get sigs by their weird SO names\n\n\t\t/// <summary>\n\t\t/// Returns the Sig associated with a given SRL line index\n\t\t/// and the join number of the object on the SRL subpage.\n\t\t/// Note: If the join number exceeds the increment range, or the count of Sigs on the \n\t\t/// list object, this will return null\n\t\t/// </summary>\n\t\t/// <param name=\"index\">The line or item position on the SRL</param>\n\t\t/// <param name=\"sigNum\">The join number of the item on the SRL subpage</param>\n\t\t/// <returns>A Sig or null if the numbers are out of range</returns>\n\t\tpublic BoolOutputSig GetBoolFeedbackSig(uint index, uint sigNum)\n\t\t{\n\t\t\tif (sigNum > BoolIncrement) return null;\n\t\t\treturn SRL.BooleanOutput.FirstOrDefault(s => s.Name.Equals(GetBoolFeedbackSigName(index, sigNum)));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns the Sig associated with a given SRL line index\n\t\t/// and the join number of the object on the SRL subpage.\n\t\t/// Note: If the join number exceeds the increment range, or the count of Sigs on the \n\t\t/// list object, this will return null\n\t\t/// </summary>\n\t\t/// <param name=\"index\">The line or item position on the SRL</param>\n\t\t/// <param name=\"sigNum\">The join number of the item on the SRL subpage</param>\n\t\t/// <returns>A Sig or null if the numbers are out of range</returns>\n\t\tpublic UShortOutputSig GetUShortOutputSig(uint index, uint sigNum)\n\t\t{\n\t\t\tif (sigNum > UShortIncrement) return null;\n\t\t\treturn SRL.UShortOutput.FirstOrDefault(s => s.Name.Equals(GetUShortOutputSigName(index, sigNum)));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns the Sig associated with a given SRL line index\n\t\t/// and the join number of the object on the SRL subpage.\n\t\t/// Note: If the join number exceeds the increment range, or the count of Sigs on the \n\t\t/// list object, this will return null\n\t\t/// </summary>\n\t\t/// <param name=\"index\">The line or item position on the SRL</param>\n\t\t/// <param name=\"sigNum\">The join number of the item on the SRL subpage</param>\n\t\t/// <returns>A Sig or null if the numbers are out of range</returns>\n\t\tpublic StringOutputSig GetStringOutputSig(uint index, uint sigNum)\n\t\t{\n\t\t\tif (sigNum > StringIncrement) return null;\n\t\t\treturn SRL.StringOutput.FirstOrDefault(s => s.Name.Equals(GetStringOutputSigName(index, sigNum)));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns the Sig associated with a given SRL line index\n\t\t/// and the join number of the object on the SRL subpage.\n\t\t/// Note: If the join number exceeds the increment range, or the count of Sigs on the \n\t\t/// list object, this will return null\n\t\t/// </summary>\n\t\t/// <param name=\"index\">The line on the SRL</param>\n\t\t/// <param name=\"sigNum\">The join number of the item on the SRL subpage</param>\n\t\t/// <returns>A Sig or null if the numbers are out of range</returns>\n\t\tpublic BoolInputSig BoolInputSig(uint index, uint sigNum)\n\t\t{\n\t\t\tif (sigNum > BoolIncrement) return null;\n\t\t\treturn SRL.BooleanInput.FirstOrDefault(s => s.Name.Equals(GetBoolInputSigName(index, sigNum)));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns the Sig associated with a given SRL line index\n\t\t/// and the join number of the object on the SRL subpage.\n\t\t/// Note: If the join number exceeds the increment range, or the count of Sigs on the \n\t\t/// list object, this will return null\n\t\t/// </summary>\n\t\t/// <param name=\"index\">The line on the SRL</param>\n\t\t/// <param name=\"sigNum\">The join number of the item on the SRL subpage</param>\n\t\t/// <returns>A Sig or null if the numbers are out of range</returns>\n\t\tpublic UShortInputSig UShortInputSig(uint index, uint sigNum)\n\t\t{\n\t\t\tif (sigNum > UShortIncrement) return null;\n\t\t\treturn SRL.UShortInput.FirstOrDefault(s => s.Name.Equals(GetUShortInputSigName(index, sigNum)));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns the Sig associated with a given SRL line index\n\t\t/// and the join number of the object on the SRL subpage.\n\t\t/// Note: If the join number exceeds the increment range, or the count of Sigs on the \n\t\t/// list object, this will return null\n\t\t/// </summary>\n\t\t/// <param name=\"index\">The line on the SRL</param>\n\t\t/// <param name=\"sigNum\">The join number of the item on the SRL subpage</param>\n\t\t/// <returns>A Sig or null if the numbers are out of range</returns>\n\t\tpublic StringInputSig StringInputSig(uint index, uint sigNum)\n\t\t{\n\t\t\tif (sigNum > StringIncrement) return null;\n\t\t\treturn SRL.StringInput.FirstOrDefault(s => s.Name.Equals(GetStringInputSigName(index, sigNum)));\n\t\t}\n\n\t\t// Helpers to get signal names\n\n\t\tstring GetBoolFeedbackSigName(uint index, uint sigNum)\n\t\t{\n\t\t\tvar num = (index - 1) * BoolIncrement + sigNum;\n\t\t\treturn String.Format(\"press{0}\", num);\n\t\t}\n\n\t\tstring GetUShortOutputSigName(uint index, uint sigNum)\n\t\t{\n\t\t\tvar num = (index - 1) * UShortIncrement + sigNum;\n\t\t\treturn String.Format(\"an_act{0}\", num);\n\t\t}\n\n\t\tstring GetStringOutputSigName(uint index, uint sigNum)\n\t\t{\n\t\t\tvar num = (index - 1) * StringIncrement + sigNum;\n\t\t\treturn String.Format(\"text-i{0}\", num);\n\t\t}\n\n\t\tstring GetBoolInputSigName(uint index, uint sigNum)\n\t\t{\n\t\t\tvar num = (index - 1) * BoolIncrement + sigNum;\n\t\t\treturn String.Format(\"fb{0}\", num);\n\t\t}\n\n\t\tstring GetUShortInputSigName(uint index, uint sigNum)\n\t\t{\n\t\t\tvar num = (index - 1) * UShortIncrement + sigNum;\n\t\t\treturn String.Format(\"an_fb{0}\", num);\n\t\t}\n\n\t\tstring GetStringInputSigName(uint index, uint sigNum)\n\t\t{\n\t\t\tvar num = (index - 1) * StringIncrement + sigNum;\n\t\t\treturn String.Format(\"text-o{0}\", num);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Stock SigChange handler\n\t\t/// </summary>\n\t\t/// <param name=\"currentDevice\"></param>\n\t\t/// <param name=\"args\"></param>\n\t\t/// <summary>\n\t\t/// SRL_SigChange method\n\t\t/// </summary>\n\t\tpublic static void SRL_SigChange(GenericBase currentDevice, SmartObjectEventArgs args)\n\t\t{\n\t\t\tvar uo = args.Sig.UserObject;\n\t\t\tif (uo is Action<bool>)\n\t\t\t\t(uo as Action<bool>)(args.Sig.BoolValue);\n\t\t\telse if (uo is Action<ushort>)\n\t\t\t\t(uo as Action<ushort>)(args.Sig.UShortValue);\n\t\t\telse if (uo is Action<string>)\n\t\t\t\t(uo as Action<string>)(args.Sig.StringValue);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/SmartObjects/SubpageReferencList/SubpageReferenceListItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.UI;\n\nnamespace PepperDash.Essentials.Core\n{\n /// <summary>\n /// Represents a SubpageReferenceListItem\n /// </summary>\n\tpublic class SubpageReferenceListItem\n\t{\n\t\t/// <summary>\n\t\t/// The list that this lives in\n\t\t/// </summary>\n\t\tprotected SubpageReferenceList Owner;\n\n\t\t/// <summary>\n\t\t/// The index of this item\n\t\t/// </summary>\n\t\tprotected uint Index;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"index\">index of the item</param>\n\t\t/// <param name=\"owner\">owner of the item</param>\n\t\tpublic SubpageReferenceListItem(uint index, SubpageReferenceList owner)\n\t\t{\n\t\t\tIndex = index;\n\t\t\tOwner = owner;\n\t\t}\n\n  /// <summary>\n  /// Clear method\n  /// </summary>\n  /// <inheritdoc />\n\t\tpublic virtual void Clear()\n\t\t{\n\t\t}\n\n  /// <summary>\n  /// Refresh method\n  /// </summary>\n\t\tpublic virtual void Refresh() { }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Timers/CountdownTimer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a SecondsCountdownTimer\n    /// </summary>\n    public class SecondsCountdownTimer: IKeyed\n    {\n        /// <summary>\n        /// Event fired when the timer starts\n        /// </summary>\n        public event EventHandler<EventArgs> HasStarted;\n\n        /// <summary>\n        /// Event fired when the timer finishes\n        /// </summary>\n        public event EventHandler<EventArgs> HasFinished;\n\n        /// <summary>\n        /// Event fired when the timer is cancelled\n        /// </summary>\n        public event EventHandler<EventArgs> WasCancelled;\n\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IsRunningFeedback\n        /// </summary>\n        public BoolFeedback IsRunningFeedback { get; private set; }\n        bool _isRunning;\n\n        /// <summary>\n        /// Gets or sets the PercentFeedback\n        /// </summary>\n        public IntFeedback PercentFeedback { get; private set; }\n        \n        /// <summary>\n        /// Gets or sets the TimeRemainingFeedback\n        /// </summary>\n        public StringFeedback TimeRemainingFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the SecondsRemainingFeedback\n        /// </summary>\n        public IntFeedback SecondsRemainingFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the CountsDown\n        /// </summary>\n        public bool CountsDown { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SecondsToCount\n        /// </summary>\n        public int SecondsToCount { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the StartTime\n        /// </summary>\n        public DateTime StartTime { get; private set; }\n        /// <summary>\n        /// Gets or sets the FinishTime\n        /// </summary>\n        public DateTime FinishTime { get; private set; }\n \n        private CTimer _secondTimer;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        public SecondsCountdownTimer(string key)\n        {\n            Key = key;\n            IsRunningFeedback = new BoolFeedback(() => _isRunning);\n\n            TimeRemainingFeedback = new StringFeedback(() =>\n                {\n                    // Need to handle up and down here.\n\n                    var timeSpan = FinishTime - DateTime.Now;\n\n                    Debug.LogMessage(LogEventLevel.Verbose,\n                        \"timeSpan.Minutes == {0}, timeSpan.Seconds == {1}, timeSpan.TotalSeconds == {2}\", this,\n                        timeSpan.Minutes, timeSpan.Seconds, timeSpan.TotalSeconds);\n\n                    if (Math.Floor(timeSpan.TotalSeconds) < 60 && Math.Floor(timeSpan.TotalSeconds) >= 0) //ignore milliseconds\n                    {\n                        return String.Format(\"{0:00}\", timeSpan.Seconds);\n                    }\n\n                    return Math.Floor(timeSpan.TotalSeconds) < 0\n                        ? \"00\"\n                        : String.Format(\"{0:00}:{1:00}\", timeSpan.Minutes, timeSpan.Seconds);\n                });\n\n            SecondsRemainingFeedback = new IntFeedback(() => (int)(FinishTime - DateTime.Now).TotalSeconds);\n\n            PercentFeedback =\n                new IntFeedback(\n                    () =>\n                        (int)\n                            (Math.Floor((FinishTime - DateTime.Now).TotalSeconds)/\n                             Math.Floor((FinishTime - StartTime).TotalSeconds)*100));\n        }\n\n        /// <summary>\n        /// Start method\n        /// </summary>\n        public void Start()\n        {\n            if (_isRunning)\n                return;\n            StartTime = DateTime.Now;\n            FinishTime = StartTime + TimeSpan.FromSeconds(SecondsToCount);\n\n            if (_secondTimer != null)\n                _secondTimer.Stop();\n            _secondTimer = new CTimer(SecondElapsedTimerCallback, null, 0, 1000);\n            _isRunning = true;\n            IsRunningFeedback.FireUpdate();\n\n            var handler = HasStarted;\n            if (handler != null)\n                handler(this, new EventArgs());\n        }\n\n        /// <summary>\n        /// Reset method\n        /// </summary>\n        public void Reset()\n        {\n            _isRunning = false;\n            IsRunningFeedback.FireUpdate();\n            Start();\n        }\n\n        /// <summary>\n        /// Cancel method\n        /// </summary>\n        public void Cancel()\n        {\n            StopHelper();\n            \n            var handler = WasCancelled;\n            if (handler != null)\n                handler(this, new EventArgs());\n        }\n\n        /// <summary>\n        /// Finish method\n        /// </summary>\n        public void Finish()\n        {\n            StopHelper();\n\n            var handler = HasFinished;\n            if (handler != null)\n                handler(this, new EventArgs());\n        }\n\n        void StopHelper()\n        {\n            if (_secondTimer != null)\n            {\n                _secondTimer.Stop();\n                _secondTimer = null;\n            }\n\n            _isRunning = false;\n            IsRunningFeedback.FireUpdate(); \n        }\n\n        void SecondElapsedTimerCallback(object o)\n        {\n            if (DateTime.Now >= FinishTime)\n            {\n                Finish();\n                return;\n            }\n\n            PercentFeedback.FireUpdate();\n            TimeRemainingFeedback.FireUpdate();\n            SecondsRemainingFeedback.FireUpdate();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Timers/RetriggerableTimer.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\n\nusing Newtonsoft.Json;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Core.Timers\n{\n    /// <summary>\n    /// A device that runs a retriggerable timer and can execute actions specified in config \n    /// </summary>\n    [Description(\"A retriggerable timer device\")]\n    public class RetriggerableTimer : EssentialsDevice\n    {\n        private RetriggerableTimerPropertiesConfig _propertiesConfig;\n\n        private CTimer _timer;\n        private long _timerIntervalMs;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">key of the timer</param>\n        /// <param name=\"config\">configuration for the timer</param>\n        public RetriggerableTimer(string key, DeviceConfig config)\n            : base(key, config.Name)\n        {\n            var props = config.Properties.ToObject<RetriggerableTimerPropertiesConfig>();\n            _propertiesConfig = props;\n\n            if (_propertiesConfig != null)\n            {\n                _timerIntervalMs = _propertiesConfig.TimerIntervalMs;\n            }\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            if (_propertiesConfig.StartTimerOnActivation)\n            {\n                StartTimer();\n            }\n\n            return base.CustomActivate();\n        }\n\n        private void CleanUpTimer()\n        {\n            if (_timer != null)\n            {\n                _timer.Stop();\n                _timer.Dispose();\n            }\n\n            _timer = null;\n        }\n\n        /// <summary>\n        /// StartTimer method\n        /// </summary>\n        public void StartTimer()\n        {\n             CleanUpTimer();\n             Debug.LogMessage(LogEventLevel.Information, this, \"Starting Timer\");\n\n             _timer = new CTimer(TimerElapsedCallback, GetActionFromConfig(eRetriggerableTimerEvents.Elapsed), _timerIntervalMs, _timerIntervalMs);\n        }\n\n        /// <summary>\n        /// StopTimer method\n        /// </summary>\n        public void StopTimer()\n        {\n            Debug.LogMessage(LogEventLevel.Information, this, \"Stopping Timer\");\n            _timer.Stop();\n\n            ExecuteAction(GetActionFromConfig(eRetriggerableTimerEvents.Stopped));\n        }\n\n        private DeviceActionWrapper GetActionFromConfig(eRetriggerableTimerEvents eventType)\n        {\n            var action = _propertiesConfig.Events[eRetriggerableTimerEvents.Elapsed];\n\n            if (action != null)\n                return action;\n            else return null;\n        }\n\n        /// <summary>\n        /// Executes the Elapsed action from confing when the timer elapses\n        /// </summary>\n        /// <param name=\"action\">action to be executed</param>\n        private void TimerElapsedCallback(object action)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Timer Elapsed. Executing Action\");\n\n            if (action == null)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Timer elapsed but unable to execute action. Action is null.\");\n                return;\n            }\n\n            var devAction = action as DeviceActionWrapper;\n            if (devAction != null)\n                ExecuteAction(devAction);\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Unable to cast action as DeviceActionWrapper. Cannot Execute\");\n            }\n\n        }\n\n        private void ExecuteAction(DeviceActionWrapper action)\n        {\n            if (action == null)\n                return;\n\n            try\n            {\n                DeviceJsonApi.DoDeviceAction(action);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Error Executing Action: {0}\", e);\n            }\n            //finally  // Not sure this is needed\n            //{\n            //    _Timer.Reset(0, _TimerIntervalMs);\n            //}\n        }\n    }\n\n    /// <summary>\n    /// Configuration Properties for RetriggerableTimer\n    /// </summary>\n    public class RetriggerableTimerPropertiesConfig\n    {\n        /// <summary>\n        /// Start the timer on device activation\n        /// </summary>\n        [JsonProperty(\"startTimerOnActivation\")]\n        public bool StartTimerOnActivation { get; set; }\n\n        /// <summary>\n        /// Timer interval in milliseconds\n        /// </summary>\n        [JsonProperty(\"timerIntervalMs\")]\n        public long TimerIntervalMs { get; set; }\n\n        /// <summary>\n        /// Events and their associated actions\n        /// </summary>\n        [JsonProperty(\"events\")]\n        public Dictionary<eRetriggerableTimerEvents, DeviceActionWrapper> Events { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public RetriggerableTimerPropertiesConfig()\n        {\n            Events = new Dictionary<eRetriggerableTimerEvents, DeviceActionWrapper>();\n        }\n    }\n\n    /// <summary>\n    /// Enumeration of eRetriggerableTimerEvents values\n    /// </summary>\n    public enum eRetriggerableTimerEvents\n    {\n        /// <summary>\n        /// Elapsed event\n        /// </summary>\n        Elapsed,\n\n        /// <summary>\n        /// Stopped event\n        /// </summary>\n        Stopped,\n    }\n\n    /// <summary>\n    /// Factory class\n    /// </summary>\n    public class RetriggerableTimerFactory : EssentialsDeviceFactory<RetriggerableTimer>\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public RetriggerableTimerFactory()\n        {\n            TypeNames = new List<string>() { \"retriggerabletimer\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <param name=\"dc\">device config</param>\n        /// <returns></returns>\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new RetriggerableTimer Device\");\n\n            return new RetriggerableTimer(dc.Key, dc);\n        }\n    }\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Touchpanels/CrestronTouchpanelPropertiesConfig.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Represents a CrestronTouchpanelPropertiesConfig\n    /// </summary>\n    public class CrestronTouchpanelPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the ControlProperties\n        /// </summary>\n        [JsonProperty(\"control\")]\n        public EssentialsControlPropertiesConfig ControlProperties { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IpId\n        /// </summary>\n        [JsonProperty(\"ipId\", NullValueHandling = NullValueHandling.Ignore)]\n        public string IpId { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DefaultRoomKey\n        /// </summary>\n        [JsonProperty(\"defaultRoomKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string DefaultRoomKey { get; set; }\n        \n        /// <summary>\n        /// Gets or sets the RoomListKey\n        /// </summary>\n        [JsonProperty(\"roomListKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string RoomListKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SgdFile\n        /// </summary>\n        [JsonProperty(\"sgdFile\", NullValueHandling = NullValueHandling.Ignore)]\n        public string SgdFile { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ProjectName\n        /// </summary>\n        [JsonProperty(\"projectName\", NullValueHandling = NullValueHandling.Ignore)]\n        public string ProjectName { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShowVolumeGauge\n        /// </summary>\n        [JsonProperty(\"showVolumeGauge\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? ShowVolumeGauge { get; set; }\n\n        /// <summary>\n        /// Gets or sets the UsesSplashPage\n        /// </summary>\n        [JsonProperty(\"usesSplashPage\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? UsesSplashPage { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShowDate\n        /// </summary>\n        [JsonProperty(\"showDate\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? ShowDate { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShowTime\n        /// </summary>\n        [JsonProperty(\"showTime\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? ShowTime { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Setup\n        /// </summary>\n        [JsonProperty(\"setup\", NullValueHandling = NullValueHandling.Ignore)]\n        public UiSetupPropertiesConfig Setup { get; set; }\n\n        /// <summary>\n        /// Gets or sets the HeaderStyle\n        /// </summary>\n        [JsonProperty(\"headerStyle\", NullValueHandling = NullValueHandling.Ignore)]\n        public string HeaderStyle { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IncludeInFusionRoomHealth\n        /// </summary>\n        [JsonProperty(\"includeInFusionRoomHealth\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IncludeInFusionRoomHealth { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ScreenSaverTimeoutMin\n        /// </summary>\n        [JsonProperty(\"screenSaverTimeoutMin\", NullValueHandling = NullValueHandling.Ignore)]\n        public uint? ScreenSaverTimeoutMin { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ScreenSaverMovePositionIntervalMs\n        /// </summary>\n        [JsonProperty(\"screenSaverMovePositionIntervalMs\", NullValueHandling = NullValueHandling.Ignore)]\n        public uint? ScreenSaverMovePositionIntervalMs { get; set; }\n\n\n        /// <summary>\n        /// The count of sources that will trigger the \"additional\" arrows to show on the SRL.\n        /// Defaults to 5\n        /// </summary>\n        [JsonProperty(\"sourcesOverflowCount\", NullValueHandling = NullValueHandling.Ignore)]\n        public int? SourcesOverflowCount { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public CrestronTouchpanelPropertiesConfig() : this(false) { }        \n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"setDefaultValues\">set values to default if true</param>\n        public CrestronTouchpanelPropertiesConfig(bool setDefaultValues = false)\n        {\n            if(!setDefaultValues) { return; }\n            SourcesOverflowCount = 5;\n            HeaderStyle = Habanero;\n\n            // Default values\n            ScreenSaverTimeoutMin = 5;\n            ScreenSaverMovePositionIntervalMs = 15000;\n        }        \n\n        /// <summary>\n        /// \"habanero\"\n        /// </summary>\n        public const string Habanero = \"habanero\";\n        /// <summary>\n        /// \"verbose\"\n        /// </summary>\n        public const string Verbose = \"verbose\";\n    }\n\n    /// <summary>\n    /// Represents a UiSetupPropertiesConfig\n    /// </summary>\n    public class UiSetupPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the IsVisible\n        /// </summary>\n        [JsonProperty(\"isVisible\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool IsVisible { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Touchpanels/Interfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core\n{\n    /// <summary>\n    /// Defines the contract for IHasBasicTriListWithSmartObject\n    /// </summary>\n    public interface IHasBasicTriListWithSmartObject\n    {\n        /// <summary>\n        /// Gets the Panel\n        /// </summary>\n        BasicTriListWithSmartObject Panel { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Touchpanels/Keyboards/HabaneroKeyboardController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nnamespace PepperDash.Essentials.Core.Touchpanels.Keyboards\n{\n    /// <summary>\n    /// Represents a HabaneroKeyboardController\n    /// </summary>\n    public class HabaneroKeyboardController\n    {\n        /// <summary>\n        /// Single-key press events, rather than using a built-up text string on the OutputFeedback\n        /// </summary>\n        public event EventHandler<KeyboardControllerPressEventArgs> KeyPress;\n\n        /// <summary>\n        /// Gets or sets the TriList\n        /// </summary>\n        public BasicTriList TriList { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the OutputFeedback\n        /// </summary>\n        public StringFeedback OutputFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IsVisible\n        /// </summary>\n        public bool IsVisible { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the DotComButtonString\n        /// </summary>\n        public string DotComButtonString { get; set; }\n\n        /// <summary>\n        /// Gets or sets the GoButtonText\n        /// </summary>\n        public string GoButtonText { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SecondaryButtonText\n        /// </summary>\n        public string SecondaryButtonText { get; set; }\n\n        /// <summary>\n        /// Gets or sets the GoButtonVisible\n        /// </summary>\n        public bool GoButtonVisible { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SecondaryButtonVisible\n        /// </summary>\n        public bool SecondaryButtonVisible { get; set; }\n\n        int ShiftMode = 0;\n        \n        StringBuilder Output;\n\n        /// <summary>\n        /// Gets or sets the HideAction\n        /// </summary>\n        public Action HideAction { get; set; }\n\n\t\tCTimer BackspaceTimer;\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"trilist\"></param>\n        public HabaneroKeyboardController(BasicTriList trilist)\n        {\n            TriList = trilist;\n            Output = new StringBuilder();\n            OutputFeedback = new StringFeedback(() => Output.ToString());\n            DotComButtonString = \".com\";\n        }\n\n        /// <summary>\n        /// Show method\n        /// </summary>\n        public void Show()\n        {\n            if (IsVisible)\n                return;\n\n            TriList.SetSigTrueAction(ClosePressJoin, Hide);\n            TriList.SetSigTrueAction(GoButtonPressJoin, () => OnKeyPress(KeyboardSpecialKey.GoButton));\n            TriList.SetSigTrueAction(SecondaryButtonPressJoin, () => OnKeyPress(KeyboardSpecialKey.SecondaryButton));\n            TriList.SetSigTrueAction(2921, () => Press(A(ShiftMode)));\n            TriList.SetSigTrueAction(2922, () => Press(B(ShiftMode)));\n            TriList.SetSigTrueAction(2923, () => Press(C(ShiftMode)));\n            TriList.SetSigTrueAction(2924, () => Press(D(ShiftMode)));\n            TriList.SetSigTrueAction(2925, () => Press(E(ShiftMode)));\n            TriList.SetSigTrueAction(2926, () => Press(F(ShiftMode)));\n            TriList.SetSigTrueAction(2927, () => Press(G(ShiftMode)));\n            TriList.SetSigTrueAction(2928, () => Press(H(ShiftMode)));\n            TriList.SetSigTrueAction(2929, () => Press(I(ShiftMode)));\n            TriList.SetSigTrueAction(2930, () => Press(J(ShiftMode)));\n            TriList.SetSigTrueAction(2931, () => Press(K(ShiftMode)));\n            TriList.SetSigTrueAction(2932, () => Press(L(ShiftMode)));\n            TriList.SetSigTrueAction(2933, () => Press(M(ShiftMode)));\n            TriList.SetSigTrueAction(2934, () => Press(N(ShiftMode)));\n            TriList.SetSigTrueAction(2935, () => Press(O(ShiftMode)));\n            TriList.SetSigTrueAction(2936, () => Press(P(ShiftMode)));\n            TriList.SetSigTrueAction(2937, () => Press(Q(ShiftMode)));\n            TriList.SetSigTrueAction(2938, () => Press(R(ShiftMode)));\n            TriList.SetSigTrueAction(2939, () => Press(S(ShiftMode)));\n            TriList.SetSigTrueAction(2940, () => Press(T(ShiftMode)));\n            TriList.SetSigTrueAction(2941, () => Press(U(ShiftMode)));\n            TriList.SetSigTrueAction(2942, () => Press(V(ShiftMode)));\n            TriList.SetSigTrueAction(2943, () => Press(W(ShiftMode)));\n            TriList.SetSigTrueAction(2944, () => Press(X(ShiftMode)));\n            TriList.SetSigTrueAction(2945, () => Press(Y(ShiftMode)));\n            TriList.SetSigTrueAction(2946, () => Press(Z(ShiftMode)));\n            TriList.SetSigTrueAction(2947, () => Press('.'));\n            TriList.SetSigTrueAction(2948, () => Press('@'));\n            TriList.SetSigTrueAction(2949, () => Press(' '));\n\t\t\tTriList.SetSigHeldAction(2950, 500, StartBackspaceRepeat, StopBackspaceRepeat, Backspace);\n\t\t\t//TriList.SetSigTrueAction(2950, Backspace);\n            TriList.SetSigTrueAction(2951, Shift);\n            TriList.SetSigTrueAction(2952, NumShift);\n            TriList.SetSigTrueAction(2953, Clear);\n            TriList.SetSigTrueAction(2954, () => Press(DotComButtonString));\n\n            TriList.SetBool(GoButtonVisibleJoin, GoButtonVisible);\n            TriList.SetString(GoButtonTextJoin, GoButtonText);\n            TriList.SetBool(SecondaryButtonVisibleJoin, SecondaryButtonVisible);\n            TriList.SetString(SecondaryButtonTextJoin, SecondaryButtonText);\n\n            TriList.SetBool(KeyboardVisible, true);\n            ShowKeys();\n            IsVisible = true;\n        }\n\n        /// <summary>\n        /// Hide method\n        /// </summary>\n        public void Hide()\n        {\n            if (!IsVisible)\n                return;\n\n            for (uint i = 2901; i < 2970; i++)\n                TriList.ClearBoolSigAction(i);\n\n            // run attached actions\n            if(HideAction != null)\n                HideAction();\n\n            TriList.SetBool(KeyboardVisible, false);\n            IsVisible = false;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"c\"></param>\n        /// <summary>\n        /// Press method\n        /// </summary>\n        public void Press(char c)\n        {\n            OnKeyPress(c.ToString());\n            Output.Append(c);\n            OutputFeedback.FireUpdate();\n            ResetShift();\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <summary>\n        /// Press method\n        /// </summary>\n        public void Press(string s)\n        {\n            OnKeyPress(s);\n            Output.Append(s);\n            OutputFeedback.FireUpdate();\n            ResetShift();\n        }\n\n        /// <summary>\n        /// EnableGoButton method\n        /// </summary>\n        public void EnableGoButton()\n        {\n            TriList.SetBool(GoButtonEnableJoin, true);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public void DisableGoButton()\n        {\n            TriList.SetBool(GoButtonEnableJoin, false);\n        }\n\n        void ResetShift()\n        {\n            if (ShiftMode == 1)\n            {\n                ShiftMode = 0;\n                ShowKeys();\n            }\n            else if (ShiftMode == 3)\n            {\n                ShiftMode = 2;\n                ShowKeys();\n            }\n        }\n\n        char A(int i) { return new char[] { 'a', 'A', '?', '?' }[i]; }\n        char B(int i) { return new char[] { 'b', 'B', ':', ':' }[i]; }\n        char C(int i) { return new char[] { 'c', 'C', '>', '>' }[i]; }\n        char D(int i) { return new char[] { 'd', 'D', '_', '_' }[i]; }\n        char E(int i) { return new char[] { 'e', 'E', '3', '#' }[i]; }\n        char F(int i) { return new char[] { 'f', 'F', '=', '=' }[i]; }\n        char G(int i) { return new char[] { 'g', 'G', '+', '+' }[i]; }\n        char H(int i) { return new char[] { 'h', 'H', '[', '[' }[i]; }\n        char I(int i) { return new char[] { 'i', 'I', '8', '*' }[i]; }\n        char J(int i) { return new char[] { 'j', 'J', ']', ']' }[i]; }\n        char K(int i) { return new char[] { 'k', 'K', '/', '/' }[i]; }\n        char L(int i) { return new char[] { 'l', 'L', '\\\\', '\\\\' }[i]; }\n        char M(int i) { return new char[] { 'm', 'M', '\"', '\"' }[i]; }\n        char N(int i) { return new char[] { 'n', 'N', '\\'', '\\'' }[i]; }\n        char O(int i) { return new char[] { 'o', 'O', '9', '(' }[i]; }\n        char P(int i) { return new char[] { 'p', 'P', '0', ')' }[i]; }\n        char Q(int i) { return new char[] { 'q', 'Q', '1', '!' }[i]; }\n        char R(int i) { return new char[] { 'r', 'R', '4', '$' }[i]; }\n        char S(int i) { return new char[] { 's', 'S', '-', '-' }[i]; }\n        char T(int i) { return new char[] { 't', 'T', '5', '%' }[i]; }\n        char U(int i) { return new char[] { 'u', 'U', '7', '&' }[i]; }\n        char V(int i) { return new char[] { 'v', 'V', ';', ';' }[i]; }\n        char W(int i) { return new char[] { 'w', 'W', '2', '@' }[i]; }\n        char X(int i) { return new char[] { 'x', 'X', '<', '<' }[i]; }\n        char Y(int i) { return new char[] { 'y', 'Y', '6', '^' }[i]; }\n        char Z(int i) { return new char[] { 'z', 'Z', ',', ',' }[i]; }\n\n\t\t/// <summary>\n\t\t/// Does what it says\n\t\t/// </summary>\n\t\tvoid StartBackspaceRepeat()\n\t\t{\n\t\t\tif (BackspaceTimer == null)\n\t\t\t{\n\t\t\t\tBackspaceTimer = new CTimer(o => Backspace(), null, 0, 175);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Does what it says\n\t\t/// </summary>\n\t\tvoid StopBackspaceRepeat()\n\t\t{\n\t\t\tif (BackspaceTimer != null)\n\t\t\t{\n\t\t\t\tBackspaceTimer.Stop();\n\t\t\t\tBackspaceTimer = null;\n\t\t\t}\n\t\t}\n\n        void Backspace()\n        {\n            OnKeyPress(KeyboardSpecialKey.Backspace);\n\n            if (Output.Length > 0)\n            {\n                Output.Remove(Output.Length - 1, 1);\n                OutputFeedback.FireUpdate();\n            }\n        }\n\n        void Clear()\n        {\n            OnKeyPress(KeyboardSpecialKey.Clear);\n\n            Output.Remove(0, Output.Length);\n            OutputFeedback.FireUpdate();\n        }\n\n        /* When in mode 0 (lowercase):\n         *      shift button: up arrow 0\n         *      numShift button: 123/#$@#$ 0\n         *      \n         *      - shift --> mode 1\n         *      - double-tap shift --> caps lock\n         *      - numShift --> mode 2\n         *      \n         * mode 1 (uppercase)\n         *      shift button: down arrow 1\n         *      numShift button: 123/##$# 0\n         *      \n         *      - shift --> mode 0\n         *      - numShift --> mode 2\n         *      \n         *      - Tapping any key will go back to mode 0\n         * \n         * mode 2 (numbers-sym)\n         *      Shift button: #$#$#$ 2\n         *      numShift: ABC 1\n         *      \n         *      - shift --> mode 3\n         *      - double-tap shift --> caps lock\n         *      - numShift --> mode 0\n         * \n         * mode 3 (sym)\n         *      Shift button: 123 3\n         *      numShift: ABC 1\n         *      \n         *      - shift --> mode 2\n         *      - numShift --> mode 0\n         *      \n         *      - Tapping any key will go back to mode 2\n         */\n        void Shift()\n        {\n            if (ShiftMode == 0)\n                ShiftMode = 1;\n            else if (ShiftMode == 1)\n                ShiftMode = 0;\n            else if (ShiftMode == 2)\n                ShiftMode = 3;\n            else\n                ShiftMode = 2;\n\n            ShowKeys();\n        }\n\n        void NumShift()\n        {\n            if (ShiftMode == 0 || ShiftMode == 1)\n                ShiftMode = 2;\n            else if (ShiftMode == 2 || ShiftMode == 3)\n                ShiftMode = 0;\n            ShowKeys();\n        }\n\n        void ShowKeys()\n        {\n            TriList.SetString(2921, A(ShiftMode).ToString());\n            TriList.SetString(2922, B(ShiftMode).ToString());\n            TriList.SetString(2923, C(ShiftMode).ToString());\n            TriList.SetString(2924, D(ShiftMode).ToString());\n            TriList.SetString(2925, E(ShiftMode).ToString());\n            TriList.SetString(2926, F(ShiftMode).ToString());\n            TriList.SetString(2927, G(ShiftMode).ToString());\n            TriList.SetString(2928, H(ShiftMode).ToString());\n            TriList.SetString(2929, I(ShiftMode).ToString());\n            TriList.SetString(2930, J(ShiftMode).ToString());\n            TriList.SetString(2931, K(ShiftMode).ToString());\n            TriList.SetString(2932, L(ShiftMode).ToString());\n            TriList.SetString(2933, M(ShiftMode).ToString());\n            TriList.SetString(2934, N(ShiftMode).ToString());\n            TriList.SetString(2935, O(ShiftMode).ToString());\n            TriList.SetString(2936, P(ShiftMode).ToString());\n            TriList.SetString(2937, Q(ShiftMode).ToString());\n            TriList.SetString(2938, R(ShiftMode).ToString());\n            TriList.SetString(2939, S(ShiftMode).ToString());\n            TriList.SetString(2940, T(ShiftMode).ToString());\n            TriList.SetString(2941, U(ShiftMode).ToString());\n            TriList.SetString(2942, V(ShiftMode).ToString());\n            TriList.SetString(2943, W(ShiftMode).ToString());\n            TriList.SetString(2944, X(ShiftMode).ToString());\n            TriList.SetString(2945, Y(ShiftMode).ToString());\n            TriList.SetString(2946, Z(ShiftMode).ToString());\n            TriList.SetString(2954, DotComButtonString);\n\n            TriList.SetUshort(2951, (ushort)ShiftMode); // 0 = up, 1 = down, 2 = #, 3 = 123 \n            TriList.SetUshort(2952, (ushort)(ShiftMode < 2 ? 0 : 1)); // 0 = #, 1 = abc\n        }\n\n        /// <summary>\n        /// Event fire helper for text \n        /// </summary>\n        /// <param name=\"text\"></param>\n        void OnKeyPress(string text)\n        {\n            var handler = KeyPress;\n            if (handler != null)\n                KeyPress(this, new KeyboardControllerPressEventArgs(text));\n        }\n\n        /// <summary>\n        /// event helper for special keys\n        /// </summary>\n        /// <param name=\"key\"></param>\n        void OnKeyPress(KeyboardSpecialKey key)\n        {\n            var handler = KeyPress;\n            if (handler != null)\n                KeyPress(this, new KeyboardControllerPressEventArgs(key));\n        }\n\n\n        /// <summary>\n        /// 2901\n        /// </summary>\n        public const uint KeyboardVisible = 2901;\n        /// <summary>\n        /// 2902\n        /// </summary>\n        public const uint ClosePressJoin = 2902;\n        /// <summary>\n        /// 2903\n        /// </summary>\n        public const uint GoButtonPressJoin = 2903;\n        /// <summary>\n        /// 2903\n        /// </summary>\n        public const uint GoButtonTextJoin = 2903;\n        /// <summary>\n        /// 2904\n        /// </summary>\n        public const uint SecondaryButtonPressJoin = 2904;\n        /// <summary>\n        /// 2904\n        /// </summary>\n        public const uint SecondaryButtonTextJoin = 2904;        \n        /// <summary>\n        /// 2905\n        /// </summary>\n        public const uint GoButtonVisibleJoin = 2905;\n        /// <summary>\n        /// 2906\n        /// </summary>\n        public const uint SecondaryButtonVisibleJoin = 2906;\n        /// <summary>\n        /// 2907\n        /// </summary>\n        public const uint GoButtonEnableJoin = 2907;\n        /// <summary>\n        /// 2910\n        /// </summary>\n        public const uint ClearPressJoin = 2910;\n        /// <summary>\n        /// 2911\n        /// </summary>\n        public const uint ClearVisibleJoin = 2911;\n\n    }\n\n    /// <summary>\n    /// Event args for keyboard key presses\n    /// </summary>\n    public class KeyboardControllerPressEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the Text\n        /// </summary>\n        public string Text { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the SpecialKey\n        /// </summary>\n        public KeyboardSpecialKey SpecialKey { get; private set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"text\"></param>\n        public KeyboardControllerPressEventArgs(string text)\n        {\n            Text = text;\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\">special keyboard key</param>\n        public KeyboardControllerPressEventArgs(KeyboardSpecialKey key)\n        {\n            SpecialKey = key;\n        }\n    }\n\n    /// <summary>\n    /// Enumeration of KeyboardSpecialKey values\n    /// </summary>\n    public enum KeyboardSpecialKey\n    {\n        /// <summary>\n        /// None\n        /// </summary>\n        None = 0, \n        \n        /// <summary>\n        /// Backspace\n        /// </summary>\n        Backspace, \n        \n        /// <summary>\n        /// Clear\n        /// </summary>\n        Clear, \n        \n        /// <summary>\n        /// GoButton\n        /// </summary>\n        GoButton, \n        \n        /// <summary>\n        /// SecondaryButton\n        /// </summary>\n        SecondaryButton\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Touchpanels/ModalDialog.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Represents a ModalDialog\n\t/// </summary>\n\tpublic class ModalDialog\n\t{\n\t\t/// <summary>\n\t\t/// Bool press 3991\n\t\t/// </summary>\n\t\tpublic const uint Button1Join = 3991;\n\t\t/// <summary>\n\t\t/// Bool press 3992\n\t\t/// </summary>\n\t\tpublic const uint Button2Join = 3992;\n\t\t/// <summary>\n\t\t/// 3993\n\t\t/// </summary>\n\t\tpublic const uint CancelButtonJoin = 3993;\n\t\t/// <summary>\n\t\t///For visibility of single button.  Bool feedback 3994\n\t\t/// </summary>\n\t\tpublic const uint OneButtonVisibleJoin = 3994;\n\t\t/// <summary>\n\t\t/// For visibility of two buttons. Bool feedback 3995.\n\t\t/// </summary>\n\t\tpublic const uint TwoButtonVisibleJoin = 3995;\n\t\t/// <summary>\n\t\t/// Shows the timer guage if in use. Bool feedback 3996\n\t\t/// </summary>\n\t\tpublic const uint TimerVisibleJoin = 3996;\n\t\t/// <summary>\n\t\t/// Visibility join to show \"X\" button 3997\n\t\t/// </summary>\n\t\tpublic const uint CancelVisibleJoin = 3997;\n\t\t/// <summary>\n\t\t/// Shows the modal subpage. Boolean feeback join 3999\n\t\t/// </summary>\n\t\tpublic const uint ModalVisibleJoin = 3999;\n\n\t\t///// <summary>\n\t\t///// The seconds value of the countdown timer. Ushort join 3991\n\t\t///// </summary>\n\t\t//public const uint TimerSecondsJoin = 3991;\n\t\t/// <summary>\n\t\t/// The full ushort value of the countdown timer for a gauge. Ushort join 3992\n\t\t/// </summary>\n\t\tpublic const uint TimerGaugeJoin = 3992;\n\n\t\t/// <summary>\n\t\t/// Text on button one. String join 3991\n\t\t/// </summary>\n\t\tpublic const uint Button1TextJoin = 3991;\n\t\t/// <summary>\n\t\t/// Text on button two. String join 3992\n\t\t/// </summary>\n\t\tpublic const uint Button2TextJoin = 3992;\n\t\t/// <summary>\n\t\t/// Message text. String join 3994\n\t\t/// </summary>\n\t\tpublic const uint MessageTextJoin = 3994;\n\t\t/// <summary>\n\t\t/// Title text. String join 3995\n\t\t/// </summary>\n\t\tpublic const uint TitleTextJoin = 3995;\n\t\t/// <summary>\n\t\t/// Icon name. String join 3996\n\t\t/// </summary>\n\t\tpublic const uint IconNameJoin = 3996;\n\n\t\t/// <summary>\n\t\t/// Returns true when modal is showing\n\t\t/// </summary>\n\t\tpublic bool ModalIsVisible\n\t\t{\n\t\t\tget { return TriList.BooleanInput[ModalVisibleJoin].BoolValue; }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\tpublic bool CanCancel { get; private set; }\n\n\n\t\tBasicTriList TriList;\n\n\t\tAction<uint> ModalCompleteAction;\n\n\t\tstatic object CompleteActionLock = new object();\n\n\t\t/// <summary>\n\t\t/// Creates a new modal to be shown on provided TriList\n\t\t/// </summary>\n\t\t/// <param name=\"triList\"></param>\n\t\tpublic ModalDialog(BasicTriList triList)\n\t\t{\n\t\t\tTriList = triList;\n\t\t\t// Attach actions to buttons\n\n\t\t\ttriList.SetSigFalseAction(Button1Join, () => OnModalComplete(1));\n\t\t\ttriList.SetSigFalseAction(Button2Join, () => OnModalComplete(2));\n\t\t\ttriList.SetSigFalseAction(CancelButtonJoin, () => { if (CanCancel) CancelDialog(); });\n\t\t\tCanCancel = true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Shows the dialog\n\t\t/// </summary>\n\t\t/// <param name=\"numberOfButtons\">Number of buttons to show. 0, 1, 2</param>\n\t\t/// <param name=\"title\">Title text</param>\n\t\t/// <param name=\"iconName\">Icon name</param>\n\t\t/// <param name=\"message\">Message text</param>\n\t\t/// <param name=\"button1Text\">Button 1 text</param>\n\t\t/// <param name=\"button2Text\">Button 2 text</param>\n\t\t/// <param name=\"showGauge\">True to show the gauge</param>\n\t\t/// <param name=\"showCancel\">True to show the cancel \"X\" button</param>\n\t\t/// <param name=\"completeAction\">The action to run when the dialog is dismissed. Parameter will be 1 or 2 if button pressed, or 0 if dialog times out</param>\n\t\t/// <returns>True when modal is created.</returns>\n\t\tpublic bool PresentModalDialog(uint numberOfButtons, string title, string iconName,\n\t\t\tstring message, string button1Text,\n\t\t\tstring button2Text, bool showGauge, bool showCancel, Action<uint> completeAction)\n\t\t{\n\t\t\t// Don't reset dialog if visible now\n\t\t\tif (!ModalIsVisible)\n\t\t\t{\n\t\t\t\tModalCompleteAction = completeAction;\n\t\t\t\tTriList.StringInput[TitleTextJoin].StringValue = title;\n\t\t\t\tif (string.IsNullOrEmpty(iconName)) iconName = \"Blank\";\n\t\t\t\tTriList.StringInput[IconNameJoin].StringValue = iconName;\n\t\t\t\tTriList.StringInput[MessageTextJoin].StringValue = message;\n\t\t\t\tif (numberOfButtons == 0)\n\t\t\t\t{\n\t\t\t\t\t// Show no buttons\n\t\t\t\t\tTriList.BooleanInput[OneButtonVisibleJoin].BoolValue = false;\n\t\t\t\t\tTriList.BooleanInput[TwoButtonVisibleJoin].BoolValue = false;\n\t\t\t\t}\n\t\t\t\telse if (numberOfButtons == 1)\n\t\t\t\t{\n\t\t\t\t\t// Show one button\n\t\t\t\t\tTriList.BooleanInput[OneButtonVisibleJoin].BoolValue = true;\n\t\t\t\t\tTriList.BooleanInput[TwoButtonVisibleJoin].BoolValue = false;\n\t\t\t\t\tTriList.StringInput[Button1TextJoin].StringValue = button1Text;\n\t\t\t\t}\n\t\t\t\telse if (numberOfButtons == 2)\n\t\t\t\t{\n\t\t\t\t\t// Show two\n\t\t\t\t\tTriList.BooleanInput[OneButtonVisibleJoin].BoolValue = false;\n\t\t\t\t\tTriList.BooleanInput[TwoButtonVisibleJoin].BoolValue = true;\n\t\t\t\t\tTriList.StringInput[Button1TextJoin].StringValue = button1Text;\n\t\t\t\t\tTriList.StringInput[Button2TextJoin].StringValue = button2Text;\n\t\t\t\t}\n\t\t\t\t// Show/hide guage\n\t\t\t\tTriList.BooleanInput[TimerVisibleJoin].BoolValue = showGauge;\n\n\t\t\t\tCanCancel = showCancel;\n\t\t\t\tTriList.BooleanInput[CancelVisibleJoin].BoolValue = showCancel;\n\n\t\t\t\t//Reveal and activate\n\t\t\t\tTriList.BooleanInput[ModalVisibleJoin].BoolValue = true;\n\n\t\t\t\tWakePanel();\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// WakePanel method\n\t\t/// </summary>\n\t\tpublic void WakePanel()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar panel = TriList as TswFt5Button;\n\n\t\t\t\tif (panel != null && panel.ExtenderSystemReservedSigs.BacklightOffFeedback.BoolValue)\n\t\t\t\t\tpanel.ExtenderSystemReservedSigs.BacklightOn();\n\t\t\t}\n\t\t\tcatch\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"Error Waking Panel.  Maybe testing with Xpanel?\");\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// CancelDialog method\n\t\t/// </summary>\n\t\tpublic void CancelDialog()\n\t\t{\n\t\t\tOnModalComplete(0);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hides dialog. Fires no action\n\t\t/// </summary>\n\t\tpublic void HideDialog()\n\t\t{\n\t\t\tTriList.BooleanInput[ModalVisibleJoin].BoolValue = false;\n\t\t}\n\n\t\t// When the modal is cleared or times out, clean up the various bits\n\t\tvoid OnModalComplete(uint buttonNum)\n\t\t{\n\t\t\tTriList.BooleanInput[ModalVisibleJoin].BoolValue = false;\n\n\t\t\tvar action = ModalCompleteAction;\n\t\t\tif (action != null)\n\t\t\t\taction(buttonNum);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Touchpanels/Mpc3Touchpanel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing Crestron.SimplSharpPro;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Touchpanels\n{\n\t/// <summary>\n\t/// A wrapper class for the touchpanel portion of an MPC3 class process to allow for configurable\n\t/// behavior of the keybad buttons\n\t/// </summary>\n\tpublic class Mpc3TouchpanelController : Device\n\t{\n\t\treadonly MPC3Basic _touchpanel;\n\n\t\treadonly Dictionary<string, KeypadButton> _buttons;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"key\">device key</param>\n\t\t/// <param name=\"name\">device name</param>\n\t\t/// <param name=\"processor\">control system processor</param>\n\t\t/// <param name=\"buttons\">dictionary of keypad buttons</param>\n\t\tpublic Mpc3TouchpanelController(string key, string name, CrestronControlSystem processor, Dictionary<string, KeypadButton> buttons)\n\t\t\t: base(key, name)\n\t\t{\n\t\t\t_touchpanel = processor.ControllerTouchScreenSlotDevice as MPC3Basic;\n\t\t\tif (_touchpanel == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Failed to construct MPC3 Touchpanel Controller with key {0}, check configuration\", key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (_touchpanel.Registerable)\n\t\t\t{\n\t\t\t\tvar registrationResponse = _touchpanel.Register();\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"touchpanel registration response: {0}\", registrationResponse);\n\t\t\t}\n\n\t\t\t_touchpanel.BaseEvent += Touchpanel_BaseEvent;\n\t\t\t_touchpanel.ButtonStateChange += Touchpanel_ButtonStateChange;\n\t\t\t_touchpanel.PanelStateChange += Touchpanel_PanelStateChange;\n\n\t\t\t_buttons = buttons;\n\t\t\tif (_buttons == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this,\n\t\t\t\t\t\"Button properties are null, failed to setup MPC3 Touch Controller, check configuration\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tAddPostActivationAction(() =>\n\t\t\t{\n\t\t\t\tforeach (var button in _buttons)\n\t\t\t\t{\n\t\t\t\t\tvar buttonKey = button.Key.ToLower();\n\t\t\t\t\tvar buttonConfig = button.Value;\n\n\t\t\t\t\tInitializeButton(buttonKey, buttonConfig);\n\t\t\t\t\tInitializeButtonFeedback(buttonKey, buttonConfig);\n\t\t\t\t}\n\n\t\t\t\tListButtons();\n\t\t\t});\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Enables/disables buttons based on event type configuration \n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"config\"></param>\n  /// <summary>\n  /// InitializeButton method\n  /// </summary>\n\t\tpublic void InitializeButton(string key, KeypadButton config)\n\t\t{\n\t\t\tif (config == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Button '{0}' config is null, unable to initialize\", key);\n\t\t\t\treturn;\n\t\t\t}\n\n            TryParseInt(key, out int buttonNumber);\n\n            var buttonEventTypes = config.EventTypes;\n\t\t\tBoolOutputSig enabledFb = null;\n\t\t\tBoolOutputSig disabledFb = null;\n\n\t\t\tswitch (key)\n\t\t\t{\n\t\t\t\tcase (\"power\"):\n\t\t\t\t\t{\n\t\t\t\t\t\tif (buttonEventTypes == null || buttonEventTypes.Keys == null)\n\t\t\t\t\t\t\t_touchpanel.DisablePowerButton();\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t_touchpanel.EnablePowerButton();\n\n\n\t\t\t\t\t\tenabledFb = _touchpanel.PowerButtonEnabledFeedBack;\n\t\t\t\t\t\tdisabledFb = _touchpanel.PowerButtonDisabledFeedBack;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t//case (\"volumeup\"):\n\t\t\t\t//    {\n\t\t\t\t//        break;\n\t\t\t\t//    }\n\t\t\t\t//case (\"volumedown\"):\n\t\t\t\t//    {\n\t\t\t\t//        break;\n\t\t\t\t//    }\n\t\t\t\t//case (\"volumefeedback\"):\n\t\t\t\t//    {\n\t\t\t\t//        break;\n\t\t\t\t//    }\n\t\t\t\tcase (\"mute\"):\n\t\t\t\t\t{\n\t\t\t\t\t\tif (buttonEventTypes == null || buttonEventTypes.Keys == null)\n\t\t\t\t\t\t\t_touchpanel.DisableMuteButton();\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t_touchpanel.EnableMuteButton();\n\n\n\t\t\t\t\t\tenabledFb = _touchpanel.MuteButtonEnabledFeedBack;\n\t\t\t\t\t\tdisabledFb = _touchpanel.MuteButtonDisabledFeedBack;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (buttonNumber == 0 || buttonNumber > 9)\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tif (buttonEventTypes == null || buttonEventTypes.Keys == null)\n\t\t\t\t\t\t\t_touchpanel.DisableNumericalButton((uint)buttonNumber);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t_touchpanel.EnableNumericalButton((uint)buttonNumber);\n\n\n\t\t\t\t\t\tif (_touchpanel.NumericalButtonEnabledFeedBack != null)\n\t\t\t\t\t\t\tenabledFb = _touchpanel.NumericalButtonEnabledFeedBack[(uint)buttonNumber];\n\n\t\t\t\t\t\tif (_touchpanel.NumericalButtonDisabledFeedBack != null)\n\t\t\t\t\t\t\tdisabledFb = _touchpanel.NumericalButtonDisabledFeedBack[(uint)buttonNumber];\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\n\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"InitializeButton: key-'{0}' enabledFb-'{1}', disabledFb-'{2}'\",\n\t\t\t\tkey, enabledFb ?? (object)\"null\", disabledFb ?? (object)\"null\");\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Links button feedback if configured\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"config\"></param>\n  /// <summary>\n  /// InitializeButtonFeedback method\n  /// </summary>\n\t\tpublic void InitializeButtonFeedback(string key, KeypadButton config)\n\t\t{\n\t\t\t//Debug.LogMessage(LogEventLevel.Debug, this, \"Initializing button '{0}' feedback...\", key);\n\n\t\t\tif (config == null)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Button '{0}' config is null, skipping.\", key);\n\t\t\t\treturn;\n\t\t\t}\n\n            TryParseInt(key, out int buttonNumber);\n\n            // Link up the button feedbacks to the specified device feedback\n            var buttonFeedback = config.Feedback;\n\t\t\tif (buttonFeedback == null || string.IsNullOrEmpty(buttonFeedback.DeviceKey))\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Button '{0}' feedback not configured, skipping.\",\n\t\t\t\t\tkey);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tFeedback deviceFeedback;\n\n\t\t\ttry\n\t\t\t{\n                if (!(DeviceManager.GetDeviceForKey(buttonFeedback.DeviceKey) is Device device))\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Button '{0}' feedback deviceKey '{1}' not found.\",\n                        key, buttonFeedback.DeviceKey);\n                    return;\n                }\n\n                deviceFeedback = device.GetFeedbackProperty(buttonFeedback.FeedbackName);\n\t\t\t\tif (deviceFeedback == null)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Button '{0}' feedbackName property '{1}' not found.\",\n\t\t\t\t\t\tkey, buttonFeedback.FeedbackName);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// TODO [ ] verify if this can replace the current method\n\t\t\t\t//Debug.LogMessage(LogEventLevel.Information, this, \"deviceFeedback.GetType().Name: '{0}'\", deviceFeedback.GetType().Name);\n\t\t\t\t//switch (feedback.GetType().Name.ToLower())\n\t\t\t\t//{\n\t\t\t\t//    case(\"boolfeedback\"):\n\t\t\t\t//    {\n\t\t\t\t//        break;\n\t\t\t\t//    }\n\t\t\t\t//    case(\"intfeedback\"):\n\t\t\t\t//    {\n\t\t\t\t//        break;\n\t\t\t\t//    }\n\t\t\t\t//    case(\"stringfeedback\"):\n\t\t\t\t//    {\n\t\t\t\t//        break;\n\t\t\t\t//    }\n\t\t\t\t//}\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"InitializeButtonFeedback (button '{1}', deviceKey '{2}') Exception Message: {0}\",\n\t\t\t\t\tex.Message, key, buttonFeedback.DeviceKey);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"InitializeButtonFeedback (button '{1}', deviceKey '{2}') Exception StackTrace: {0}\",\n\t\t\t\t\tex.StackTrace, key, buttonFeedback.DeviceKey);\n\t\t\t\tif (ex.InnerException != null) Debug.LogMessage(LogEventLevel.Verbose, this, \"InitializeButtonFeedback (button '{1}', deviceKey '{2}') InnerException: {0}\",\n\t\t\t\t\tex.InnerException, key, buttonFeedback.DeviceKey);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar boolFeedback = deviceFeedback as BoolFeedback;\n\n            switch (key)\n            {\n                case (\"power\"):\n                    {\n                        boolFeedback?.LinkCrestronFeedback(_touchpanel.FeedbackPower);\n                        break;\n                    }\n                case (\"volumeup\"):\n                case (\"volumedown\"):\n                case (\"volumefeedback\"):\n                    {\n                        if (deviceFeedback is IntFeedback intFeedback)\n                        {\n                            var volumeFeedback = intFeedback;\n                            volumeFeedback.LinkInputSig(_touchpanel.VolumeBargraph);\n                        }\n                        break;\n                    }\n                case (\"mute\"):\n                    {\n                        boolFeedback?.LinkCrestronFeedback(_touchpanel.FeedbackMute);\n                        break;\n                    }\n                default:\n                    {\n                        boolFeedback?.LinkCrestronFeedback(_touchpanel.Feedbacks[(uint)buttonNumber]);\n                        break;\n                    }\n            }\n        }\n\n\t\t/// <summary>\n\t\t/// Try parse int helper method\n\t\t/// </summary>\n\t\t/// <param name=\"str\"></param>\n\t\t/// <param name=\"result\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool TryParseInt(string str, out int result)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tresult = int.Parse(str);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcatch\n\t\t\t{\n\t\t\t\tresult = 0;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tprivate void Touchpanel_BaseEvent(GenericBase device, BaseEventArgs args)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"BaseEvent: eventId-'{0}', index-'{1}'\", args.EventId, args.Index);\n\t\t}\n\n\t\tprivate void Touchpanel_ButtonStateChange(GenericBase device, Crestron.SimplSharpPro.DeviceSupport.ButtonEventArgs args)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"ButtonStateChange: buttonNumber-'{0}' buttonName-'{1}', buttonState-'{2}'\", args.Button.Number, args.Button.Name, args.NewButtonState);\n\t\t\tvar type = args.NewButtonState.ToString();\n\n\t\t\tif (_buttons.ContainsKey(args.Button.Number.ToString(CultureInfo.InvariantCulture)))\n\t\t\t{\n\t\t\t\tPress(args.Button.Number.ToString(CultureInfo.InvariantCulture), type);\n\t\t\t}\n\t\t\telse if (_buttons.ContainsKey(args.Button.Name.ToString()))\n\t\t\t{\n\t\t\t\tPress(args.Button.Name.ToString(), type);\n\t\t\t}\n\t\t}\n\n\t\tprivate void Touchpanel_PanelStateChange(GenericBase device, BaseEventArgs args)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"PanelStateChange: eventId-'{0}', index-'{1}'\", args.EventId, args.Index);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Runs the function associated with this button/type. One of the following strings:\n\t\t/// Pressed, Released, Tapped, DoubleTapped, Held, HeldReleased    \n\t\t/// </summary>\n\t\t/// <param name=\"buttonKey\"></param>\n\t\t/// <param name=\"type\"></param>\n  /// <summary>\n  /// Press method\n  /// </summary>\n\t\tpublic void Press(string buttonKey, string type)\n\t\t{\n\t\t\tthis.LogVerbose(\"Press: buttonKey-'{buttonKey}', type-'{type}'\", buttonKey, type);\n\n\t\t\t// TODO: In future, consider modifying this to generate actions at device activation time\n\t\t\t//       to prevent the need to dynamically call the method via reflection on each button press\n\t\t\tif (!_buttons.ContainsKey(buttonKey)) return;\n\n\t\t\tvar button = _buttons[buttonKey];\n\t\t\tif (!button.EventTypes.ContainsKey(type)) return;\n\n\t\t\tforeach (var eventType in button.EventTypes[type]) DeviceJsonApi.DoDeviceAction(eventType);\n\t\t}\n\n\n  /// <summary>\n  /// ListButtons method\n  /// </summary>\n\t\tpublic void ListButtons()\n\t\t{\n\t\t\tthis.LogVerbose(\"MPC3 Controller {0} - Available Buttons\", Key);\n\n\t\t\tforeach (var button in _buttons)\n\t\t\t{\n\t\t\t\tthis.LogVerbose(\"Key: {key}\", button.Key);\n\t\t\t}\n\t\t}\n\t}\n\n /// <summary>\n /// Represents a KeypadButton\n /// </summary>\n\tpublic class KeypadButton\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the EventTypes\n\t\t/// </summary>\n\t\t[JsonProperty(\"eventTypes\")]\n\t\tpublic Dictionary<string, DeviceActionWrapper[]> EventTypes { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Feedback\n\t\t/// </summary>\n\t\t[JsonProperty(\"feedback\")]\n\t\tpublic KeypadButtonFeedback Feedback { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic KeypadButton()\n\t\t{\n\t\t\tEventTypes = new Dictionary<string, DeviceActionWrapper[]>();\n\t\t\tFeedback = new KeypadButtonFeedback();\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a KeypadButtonFeedback\n\t/// </summary>\n\tpublic class KeypadButtonFeedback\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the DeviceKey\n\t\t/// </summary>\n\t\t[JsonProperty(\"deviceKey\")]\n\t\tpublic string DeviceKey { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the FeedbackName\n\t\t/// </summary>\n\t\t[JsonProperty(\"feedbackName\")]\n\t\tpublic string FeedbackName { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Touchpanels/TriListExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Extensions used for more-clear attachment of Actions to user objects on sigs\n\t/// </summary>\n\tpublic static class SigAndTriListExtensions\n\t{\n\t\t/// <summary>\n\t\t/// Attaches Action to Sig's user object and returns the same Sig. This provides no protection\n        /// from null sigs\n\t\t/// </summary>\n\t\t/// <param name=\"sig\">The BoolOutputSig to attach the Action to</param>\n\t\t/// <param name=\"a\">An action to run when sig is pressed and when released</param>\n\t\t/// <returns>The Sig, sig</returns>\n\t\tpublic static BoolOutputSig SetBoolSigAction(this BoolOutputSig sig, Action<bool> a)\n\t\t{\n\t\t\tsig.UserObject = a;\n\t\t\treturn sig;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Attaches Action to Sig's user object and returns the same Sig.\n\t\t/// </summary>\n\t\t/// <param name=\"tl\"></param>\n\t\t/// <param name=\"sigNum\"></param>\n\t\t/// <param name=\"a\"></param>\n\t\t/// <returns></returns>\n  /// <summary>\n  /// SetBoolSigAction method\n  /// </summary>\n\t\tpublic static BoolOutputSig SetBoolSigAction(this BasicTriList tl, uint sigNum, Action<bool> a)\n\t\t{\n\t\t\treturn tl.BooleanOutput[sigNum].SetBoolSigAction(a);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Attaches a void Action to a TriList's output sig's UserObject, to be run on press\n\t\t/// </summary>\n\t\t/// <param name=\"tl\">trilist</param>\n\t\t/// <param name=\"sigNum\">number of the signal</param>\n\t\t/// <param name=\"a\">action to run when the signal is true (pressed)</param>\n\t\t/// <returns></returns>\n\t\tpublic static BoolOutputSig SetSigTrueAction(this BasicTriList tl, uint sigNum, Action a)\n\t\t{\n\t\t\treturn tl.BooleanOutput[sigNum].SetBoolSigAction(b => { if(b) a(); });\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Attaches a void Action to a TriList's output sig's UserObject, to be run on release\n\t\t/// </summary>\n\t\t/// <returns>The sig</returns>\n\t\tpublic static BoolOutputSig SetSigFalseAction(this BasicTriList tl, uint sigNum, Action a)\n\t\t{\n\t\t\treturn tl.BooleanOutput[sigNum].SetBoolSigAction(b => { if (!b) a(); });\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Attaches a void Action to an output sig's UserObject, to be run on release\n\t\t/// </summary>\n\t\t/// <returns>The Sig</returns>\n\t\tpublic static BoolOutputSig SetSigFalseAction(this BoolOutputSig sig, Action a)\n\t\t{\n\t\t\treturn sig.SetBoolSigAction(b => { if (!b) a(); });\n\t\t}\n\n        /// <summary>\n        /// Sets an action to a held sig\n        /// </summary>\n        /// <returns>The sig</returns>\n        public static BoolOutputSig SetSigHeldAction(this BasicTriList tl, uint sigNum, uint heldMs, Action heldAction)\n        {\n            return SetSigHeldAction(tl, sigNum, heldMs, heldAction, null);\n        }\n\n\t\t/// <summary>\n\t\t/// Sets an action to a held sig as well as a released-without-hold action\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic static BoolOutputSig SetSigHeldAction(this BoolOutputSig sig, uint heldMs, Action heldAction, Action holdReleasedAction, Action releaseAction)\n\t\t{\n\t\t\tCTimer heldTimer = null;\n\t\t\tbool wasHeld = false;\n\t\t\treturn sig.SetBoolSigAction(press =>\n\t\t\t{\n\t\t\t\tif (press)\n\t\t\t\t{\n\t\t\t\t\twasHeld = false;\n\t\t\t\t\t// Could insert a pressed action here\n\t\t\t\t\theldTimer = new CTimer(o =>\n\t\t\t\t\t{\n\t\t\t\t\t\t// if still held and there's an action\n\t\t\t\t\t\tif (sig.BoolValue && heldAction != null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twasHeld = true;\n\t\t\t\t\t\t\t// Hold action here\n\t\t\t\t\t\t\theldAction();\n\t\t\t\t\t\t}\n\t\t\t\t\t}, heldMs);\n\t\t\t\t}\n\t\t\t\telse if (!press && !wasHeld) // released, no hold\n\t\t\t\t{\n\t\t\t\t\theldTimer.Stop();\n\t\t\t\t\tif (releaseAction != null)\n\t\t\t\t\t\treleaseAction();\n\t\t\t\t}\n\t\t\t\telse // !press && wasHeld // released after held\n\t\t\t\t{\n\t\t\t\t\theldTimer.Stop();\n\t\t\t\t\tif (holdReleasedAction != null)\n\t\t\t\t\t\tholdReleasedAction();\n\t\t\t\t}\n\t\t\t});\n\n\t\t}\n\n        /// <summary>\n        /// Sets an action to a held sig as well as a released-without-hold action\n        /// </summary>\n        /// <returns>The sig</returns>\n        /// <summary>\n        /// SetSigHeldAction method\n        /// </summary>\n        public static BoolOutputSig SetSigHeldAction(this BasicTriList tl, uint sigNum, uint heldMs, Action heldAction, Action releaseAction)\n        {\n\t\t\treturn tl.BooleanOutput[sigNum].SetSigHeldAction(heldMs, heldAction, null, releaseAction);\n        }\n\n\t\t/// <summary>\n\t\t/// Sets an action to a held sig, an action for the release of hold, as well as a released-without-hold action\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic static BoolOutputSig SetSigHeldAction(this BasicTriList tl, uint sigNum, uint heldMs, Action heldAction,\n\t\t\tAction holdReleasedAction, Action releaseAction)\n\t\t{\n\t\t\treturn tl.BooleanOutput[sigNum].SetSigHeldAction(heldMs, heldAction, holdReleasedAction, releaseAction);\n\t\t}\n\n\t\t/// <summary>\n  \t\t/// SetUShortSigAction method\n  \t\t/// </summary>\n\t\t/// <param name=\"sig\"></param>\n\t\t/// <param name=\"a\"></param>\n\t\t/// <returns>The Sig</returns>\n \t\tpublic static UShortOutputSig SetUShortSigAction(this UShortOutputSig sig, Action<ushort> a)\n\t\t{\n\t\t\tsig.UserObject = a;\n\t\t\treturn sig;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SetUShortSigAction method\n\t\t/// </summary>\n\t\t/// <param name=\"tl\"></param>\n\t\t/// <param name=\"sigNum\"></param>\n\t\t/// <param name=\"a\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static UShortOutputSig SetUShortSigAction(this BasicTriList tl, uint sigNum, Action<ushort> a)\n\t\t{\n\t\t\treturn tl.UShortOutput[sigNum].SetUShortSigAction(a);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SetStringSigAction method\n\t\t/// </summary>\n\t\t/// <param name=\"sig\"></param>\n\t\t/// <param name=\"a\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static StringOutputSig SetStringSigAction(this StringOutputSig sig, Action<string> a)\n\t\t{\n\t\t\tsig.UserObject = a;\n\t\t\treturn sig;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// SetStringSigAction method\n\t\t/// </summary>\n\t\t/// <param name=\"tl\"></param>\n\t\t/// <param name=\"sigNum\"></param>\n\t\t/// <param name=\"a\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static StringOutputSig SetStringSigAction(this BasicTriList tl, uint sigNum, Action<string> a)\n\t\t{\n\t\t\treturn tl.StringOutput[sigNum].SetStringSigAction(a);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ClearSigAction method\n\t\t/// </summary>\n\t\t/// <param name=\"sig\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static Sig ClearSigAction(this Sig sig)\n\t\t{\n\t\t\tsig.UserObject = null;\n\t\t\treturn sig;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ClearBoolSigAction method\n\t\t/// </summary>\n\t\t/// <param name=\"tl\">trilist</param>\n\t\t/// <param name=\"sigNum\">signal number to clear</param>\n\t\t/// <returns></returns>\n\t\tpublic static BoolOutputSig ClearBoolSigAction(this BasicTriList tl, uint sigNum)\n\t\t{\n\t\t\treturn ClearSigAction(tl.BooleanOutput[sigNum]) as BoolOutputSig;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ClearUShortSigAction method\n\t\t/// </summary>\n\t\tpublic static UShortOutputSig ClearUShortSigAction(this BasicTriList tl, uint sigNum)\n\t\t{\n\t\t\treturn ClearSigAction(tl.UShortOutput[sigNum]) as UShortOutputSig;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ClearStringSigAction method\n\t\t/// </summary>\n\t\tpublic static StringOutputSig ClearStringSigAction(this BasicTriList tl, uint sigNum)\n\t\t{\n\t\t\treturn ClearSigAction(tl.StringOutput[sigNum]) as StringOutputSig;\n\t\t}\n\n        /// <summary>\n        /// ClearAllSigActions method\n        /// </summary>\n        public static void ClearAllSigActions(this BasicTriList t1)\n        {\n            foreach (var sig in t1.BooleanOutput)\n            {\n                ClearSigAction(sig);\n            }\n\n            foreach (var sig in t1.UShortOutput)\n            {\n                ClearSigAction(sig);\n            }\n\n            foreach (var sig in t1.StringOutput)\n            {\n                ClearSigAction(sig);\n            }\n        }\n\n        /// <summary>\n        /// SetBool method\n        /// </summary>\n        public static void SetBool(this BasicTriList tl, uint sigNum, bool value)\n        {\n            tl.BooleanInput[sigNum].BoolValue = value;\n        }\n\n\t\t/// <summary>\n\t\t/// Sends an true-false pulse to the sig\n\t\t/// </summary>\n\t\t/// <param name=\"tl\"></param>\n\t\t/// <param name=\"sigNum\"></param>\n\t\tpublic static void PulseBool(this BasicTriList tl, uint sigNum)\n\t\t{\n\t\t\ttl.BooleanInput[sigNum].Pulse();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sends a timed pulse to the sig\n\t\t/// </summary>\n\t\t/// <param name=\"tl\"></param>\n\t\t/// <param name=\"sigNum\"></param>\n\t\t/// <param name=\"ms\"></param>\n\t\tpublic static void PulseBool(this BasicTriList tl, uint sigNum, int ms)\n\t\t{\n\t\t\ttl.BooleanInput[sigNum].Pulse(ms);\n\t\t}\n\n        /// <summary>\n        /// Helper method to set the value of a ushort Sig on TriList\n        /// </summary>\n        public static void SetUshort(this BasicTriList tl, uint sigNum, ushort value)\n        {\n            tl.UShortInput[sigNum].UShortValue = value;\n        }\n\n        /// <summary>\n        /// Helper method to set the value of a string Sig on TriList\n        /// </summary>\n        public static void SetString(this BasicTriList tl, uint sigNum, string value)\n        {\n            tl.StringInput[sigNum].StringValue = value;\n        }\n\n\t\t/// <summary>\n\t\t/// Helper method to set the value of a string Sig on TriList with encoding\n\t\t/// </summary>\n\t\t/// <param name=\"tl\">trilist</param>\n\t\t/// <param name=\"sigNum\">signal number to set</param>\n\t\t/// <param name=\"value\">string value to set</param>\n\t\t/// <param name=\"encoding\">string encoding to use</param>\n\t    public static void SetString(this BasicTriList tl, uint sigNum, string value, eStringEncoding encoding)\n\t    {\n\t        tl.StringInput[sigNum].StringEncoding = encoding;\n\t        tl.StringInput[sigNum].StringValue = value;\n\t    }\n\n\t\t/// <summary>\n\t\t/// Returns bool value of trilist sig\n\t\t/// </summary>\n\t\t/// <param name=\"tl\"></param>\n\t\t/// <param name=\"sigNum\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static bool GetBool(this BasicTriList tl, uint sigNum)\n\t\t{\n\t\t\treturn tl.BooleanOutput[sigNum].BoolValue;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns ushort value of trilist sig\n\t\t/// </summary>\n\t\t/// <param name=\"tl\"></param>\n\t\t/// <param name=\"sigNum\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static ushort GetUshort(this BasicTriList tl, uint sigNum)\n\t\t{\n\t\t\treturn tl.UShortOutput[sigNum].UShortValue;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns string value of trilist sig.\n\t\t/// </summary>\n\t\t/// <param name=\"tl\"></param>\n\t\t/// <param name=\"sigNum\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static string GetString(this BasicTriList tl, uint sigNum)\n\t\t{\n\t\t\treturn tl.StringOutput[sigNum].StringValue;\n\t\t}\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/TriListBridges/HandlerBridge.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\n\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Abstract base class for TriList handler bridges\n\t/// </summary>\n\tpublic abstract class HandlerBridge\n\t{\n  /// <summary>\n  /// Gets or sets the IsAttached\n  /// </summary>\n\t\tpublic bool IsAttached { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Attaches the handler to the panel's user objects\n\t\t/// </summary>\n\t\tpublic abstract void AttachToTriListOutputs(bool sendUpdate);\n\n\t\t/// <summary>\n\t\t/// Removes the handler from the panel's user objects\n\t\t/// </summary>\n\t\tpublic abstract void DetachFromTriListOutputs();\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.UI\n{\n    /// <summary>\n    /// Base class for Touchpanel devices\n    /// </summary>\n    public abstract class TouchpanelBase : EssentialsDevice, IHasBasicTriListWithSmartObject\n    {\n        /// <summary>\n        /// Gets or sets the configuration for the Crestron touchpanel.\n        /// </summary>\n        protected CrestronTouchpanelPropertiesConfig _config;\n        /// <summary>\n        /// Gets or sets the Panel\n        /// </summary>\n        public BasicTriListWithSmartObject Panel { get; private set; }\n\n        /// <summary>\n        /// Constructor for use with device Factory. A touch panel device will be created based on the provided IP-ID and the\n        /// type of the panel. The SGD File path can be specified using the config property, or a default one located in the program directory if none\n        /// is provided.\n        /// </summary>\n        /// <param name=\"key\">Essentials Device Key</param>\n        /// <param name=\"name\">Essentials Device Name</param>    \n        /// <param name=\"panel\">Crestron Touchpanel Device</param>    \n        /// <param name=\"config\">Touchpanel Configuration</param>        \n        protected TouchpanelBase(string key, string name, BasicTriListWithSmartObject panel, CrestronTouchpanelPropertiesConfig config)\n            : base(key, name)\n        {\n\n            if (panel == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Panel is not valid. Touchpanel class WILL NOT work correctly\");\n                return;\n            }\n\n            Panel = panel;\n\n            Panel.SigChange += Panel_SigChange;\n\n            if (Panel is TswFt5ButtonSystem)\n            {\n                var tsw = Panel as TswFt5ButtonSystem;\n                tsw.ExtenderSystemReservedSigs.Use();\n                tsw.ExtenderSystemReservedSigs.DeviceExtenderSigChange\n                    += ExtenderSystemReservedSigs_DeviceExtenderSigChange;\n\n                tsw.ButtonStateChange += Tsw_ButtonStateChange;\n            }\n\n            _config = config;\n\n            AddPreActivationAction(() =>\n            {\n                // Give up cleanly if SGD is not present.\n                var sgdName = Global.FilePathPrefix + \"sgd\" + Global.DirectorySeparator + _config.SgdFile;\n                if (!File.Exists(sgdName))\n                {\n                    this.LogInformation(\"Smart object file '{0}' not present in User folder. Looking for embedded file\", sgdName);\n\n                    sgdName = Global.ApplicationDirectoryPathPrefix + Global.DirectorySeparator + \"SGD\" + Global.DirectorySeparator + _config.SgdFile;\n\n                    if (!File.Exists(sgdName))\n                    {\n                        this.LogWarning(\"Unable to find SGD file '{0}' in User sgd or application SGD folder. Exiting touchpanel load.\", sgdName);\n                        return;\n                    }\n                }\n\n                Panel.LoadSmartObjects(sgdName);\n            });\n\n            AddPostActivationAction(() =>\n            {\n                // Check for IEssentialsRoomCombiner in DeviceManager and if found, subscribe to its event\n\n                if (DeviceManager.AllDevices.FirstOrDefault((d) => d is IEssentialsRoomCombiner) is IEssentialsRoomCombiner roomCombiner)\n                {\n                    // Subscribe to the even\n                    roomCombiner.RoomCombinationScenarioChanged += new EventHandler<EventArgs>(RoomCombiner_RoomCombinationScenarioChanged);\n\n                    // Connect to the initial roomKey\n                    if (roomCombiner.CurrentScenario != null)\n                    {\n                        // Use the current scenario\n                        DetermineRoomKeyFromScenario(roomCombiner.CurrentScenario);\n                    }\n                    else\n                    {\n                        // Current Scenario not yet set.  Use default \n                        SetupPanelDrivers(_config.DefaultRoomKey);\n                    }\n                }\n                else\n                {\n                    // No room combiner, use the default key\n                    SetupPanelDrivers(_config.DefaultRoomKey);\n                }\n\n                var panelRegistrationResponse = Panel.Register();\n\n                if (panelRegistrationResponse != eDeviceRegistrationUnRegistrationResponse.Success)\n                    this.LogInformation(\"WARNING: Registration failed. Continuing, but panel may not function: {0}\", Panel.RegistrationFailureReason);\n            });\n        }\n\n        /// <summary>\n        /// Setup Panel operation\n        /// </summary>\n        /// <param name=\"roomKey\">Room Key for this panel</param>\n        protected abstract void SetupPanelDrivers(string roomKey);\n\n        /// <summary>\n        /// Event handler for System Extender Events\n        /// </summary>\n        /// <param name=\"currentDeviceExtender\"></param>\n        /// <param name=\"args\"></param>\n        protected abstract void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args);\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        protected virtual void RoomCombiner_RoomCombinationScenarioChanged(object sender, EventArgs e)\n        {\n            var roomCombiner = sender as IEssentialsRoomCombiner;\n\n            DetermineRoomKeyFromScenario(roomCombiner.CurrentScenario);\n        }\n\n        /// <summary>\n        /// Determines the room key to use based on the scenario\n        /// </summary>\n        /// <param name=\"scenario\"></param>\n        protected virtual void DetermineRoomKeyFromScenario(IRoomCombinationScenario scenario)\n        {\n            string newRoomKey = null;\n\n            if (scenario.UiMap.ContainsKey(Key))\n            {\n                newRoomKey = scenario.UiMap[Key];\n            }\n            else if (scenario.UiMap.ContainsKey(_config.DefaultRoomKey))\n            {\n                newRoomKey = scenario.UiMap[_config.DefaultRoomKey];\n            }\n\n            SetupPanelDrivers(newRoomKey);\n        }\n\n        private void Panel_SigChange(object currentDevice, SigEventArgs args)\n        {\n            this.LogVerbose(\"Sig change: {0} {1}={2}\", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);\n            var uo = args.Sig.UserObject;\n            if (uo is Action<bool>)\n                (uo as Action<bool>)(args.Sig.BoolValue);\n            else if (uo is Action<ushort>)\n                (uo as Action<ushort>)(args.Sig.UShortValue);\n            else if (uo is Action<string>)\n                (uo as Action<string>)(args.Sig.StringValue);\n        }\n\n        private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args)\n        {\n            var uo = args.Button.UserObject;\n            if (uo is Action<bool>)\n                (uo as Action<bool>)(args.Button.State == eButtonState.Pressed);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/UI PageManagers/BlurayPageManager.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.PageManagers\n{\n /// <summary>\n /// Represents a DiscPlayerMediumPageManager\n /// </summary>\n\tpublic class DiscPlayerMediumPageManager : MediumLeftSwitchablePageManager\n\t{\n\t\tIDiscPlayerControls Player;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"player\">player controls object</param>\n\t\t/// <param name=\"trilist\">trilist object</param>\n\t\tpublic DiscPlayerMediumPageManager(IDiscPlayerControls player, BasicTriListWithSmartObject trilist)\n\t\t\t: base(player.DisplayUiType)\n\t\t{\n\t\t\tPlayer = player;\n\t\t\tTriList = trilist;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Show method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Show()\n\t\t{\n\t\t\tuint offset = GetOffsetJoin();\n\t\t\tBackingPageJoin = offset + 1;\n\t\t\tAllLeftSubpages = new uint[] { 7, 8 };\n\n\t\t\tif (LeftSubpageJoin == 0)\n\t\t\t\tLeftSubpageJoin = offset + 8; // default to transport\n\t\t\tTriList.BooleanInput[BackingPageJoin].BoolValue = true;\n\t\t\tTriList.BooleanInput[LeftSubpageJoin].BoolValue = true;\n\n\t\t\t// Attach buttons to interlock\n\t\t\tforeach(var p in AllLeftSubpages)\n\t\t\t{\n\t\t\t\tvar p2 = p; // scope\n\t\t\t\tTriList.SetSigFalseAction(10000 + p2, () => InterlockLeftSubpage(p2));\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hide method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Hide()\n\t\t{\n\t\t\tTriList.BooleanInput[BackingPageJoin].BoolValue = false;\n\t\t\tTriList.BooleanInput[LeftSubpageJoin].BoolValue = false;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/UI PageManagers/PageManager.cs",
    "content": "﻿using System.Collections.Generic;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.PageManagers\n{\n\t/// <summary>\n\t/// The PageManager classes are used to bridge a device to subpage\n\t/// visibility.\n\t/// </summary>\n\tpublic abstract class PageManager\n\t{\n\t\t/// <summary>\n\t\t/// ActiveJoins list\n\t\t/// </summary>\n\t\tprotected List<uint> ActiveJoins = new List<uint>();\n\n\t\t/// <summary>\n\t\t/// Show method\n\t\t/// </summary>\n\t\tpublic abstract void Show();\n\n\t\t/// <summary>\n\t\t/// Hide method\n\t\t/// </summary>\n\t\tpublic abstract void Hide();\n\n\t\t/// <summary>\n\t\t/// For device types 1-49, returns the offset join for subpage management 10100 - 14900\n\t\t/// </summary>\n\t\t/// <param name=\"deviceType\">1 through 49, as defined in some constants somewhere!</param>\n\t\t/// <returns></returns>\n\t\tpublic uint GetOffsetJoin(uint deviceType)\n\t\t{\n\t\t\treturn 10000 + (deviceType * 100);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// A simple class that hides and shows the default subpage for a given source type\n\t/// </summary>\n\tpublic class DefaultPageManager : PageManager\n\t{\n\t\tBasicTriList TriList;\n\t\tuint BackingPageJoin;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"device\">device</param>\n\t\t/// <param name=\"trilist\">trilist object</param>\n\t\tpublic DefaultPageManager(IUiDisplayInfo device, BasicTriList trilist)\n\t\t{\n\t\t\tTriList = trilist;\n\t\t\tBackingPageJoin = GetOffsetJoin(device.DisplayUiType) + 1;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"join\">back page join</param>\n\t\t/// <param name=\"trilist\">trilist object</param>\n\t\tpublic DefaultPageManager(uint join, BasicTriList trilist)\n\t\t{\n\t\t\tTriList = trilist;\n\t\t\tBackingPageJoin = join;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Show method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Show()\n\t\t{\n\t\t\tTriList.BooleanInput[BackingPageJoin].BoolValue = true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hide method\n\t\t/// </summary>\n\t\tpublic override void Hide()\n\t\t{\n\t\t\tTriList.BooleanInput[BackingPageJoin].BoolValue = false;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// A page manager for a page with backing panel and a switchable side panel\n\t/// </summary>\n\tpublic abstract class MediumLeftSwitchablePageManager : PageManager\n\t{\n\t\t/// <summary>\n\t\t/// TriList object\n\t\t/// </summary>\n\t\tprotected BasicTriListWithSmartObject TriList;\n\n\t\t/// <summary>\n\t\t/// Left subpage join\n\t\t/// </summary>\n\t\tprotected uint LeftSubpageJoin;\n\n\t\t/// <summary>\n\t\t/// Backing page join\n\t\t/// </summary>\n\t\tprotected uint BackingPageJoin;\n\n\t\t/// <summary>\n\t\t/// All left subpages\n\t\t/// </summary>\n\t\tprotected uint[] AllLeftSubpages;\n\n\t\t/// <summary>\n\t\t/// Display UI Type\n\t\t/// </summary>\n\t\tprotected uint DisplayUiType;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"displayUiType\">UI type</param>\n\t\tprotected MediumLeftSwitchablePageManager(uint displayUiType)\n\t\t{\n\t\t\tDisplayUiType = displayUiType;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Interlock left subpage\n\t\t/// </summary>\n\t\t/// <param name=\"join\"></param>\n\t\tprotected void InterlockLeftSubpage(uint join)\n\t\t{\n\t\t\tjoin = join + GetOffsetJoin();\n\t\t\tClearLeftInterlock();\n\t\t\tTriList.BooleanInput[join].BoolValue = true;\n\t\t\tLeftSubpageJoin = join;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Clear left interlock\n\t\t/// </summary>\n\t\tprotected void ClearLeftInterlock()\n\t\t{\n\t\t\tforeach (var p in AllLeftSubpages)\n\t\t\t\tTriList.BooleanInput[GetOffsetJoin() + p].BoolValue = false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Get offset join\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tprotected uint GetOffsetJoin()\n\t\t{\n\t\t\treturn GetOffsetJoin(DisplayUiType);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/UI PageManagers/SetTopBoxThreePanelPageManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Presets;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.PageManagers\n{\n\t/// <summary>\n\t/// Represents a ThreePanelPlusOnePageManager\n\t/// </summary>\n\tpublic class ThreePanelPlusOnePageManager : PageManager\n\t{\n\t\t/// <summary>\n\t\t/// The trilist\n\t\t/// </summary>\n\t\tprotected BasicTriListWithSmartObject TriList;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets the Position5TabsId\n\t\t/// </summary>\n\t\tpublic uint Position5TabsId { get; set; }\n\n\t\t/// <summary>\n\t\t/// Show the tabs on the third panel\n\t\t/// </summary>\n\t\tprotected bool ShowPosition5Tabs;\n\n\t\t/// <summary>\n\t\t/// Joins that are always visible when this manager is visible\n\t\t/// </summary>\n\t\tprotected uint[] FixedVisibilityJoins;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the current visible item in position 5\n\t\t/// </summary>\n\t\tprotected uint CurrentVisiblePosition5Item;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"trilist\"></param>\n\t\tpublic ThreePanelPlusOnePageManager(BasicTriListWithSmartObject trilist)\n\t\t{\n\t\t\tTriList = trilist;\n            CurrentVisiblePosition5Item = 1;\n\t\t}\n\t\n\t\t/// <summary>\n\t\t/// The joins for the switchable panel in position 5\n\t\t/// </summary>\n\t\tDictionary<uint, uint> Position5SubpageJoins = new Dictionary<uint, uint>\n\t        {\n\t            { 1, 10053 },\n\t            { 2, 10054 }\n\t        };\n\n\t\t/// <summary>\n\t\t/// \n\t\t/// </summary>\n\t\tpublic override void Show()\n\t\t{\n\t\t\t// Project the joins into corresponding sigs.\n\t\t\tvar fixedSigs = FixedVisibilityJoins.Select(u => TriList.BooleanInput[u]).ToList();\n            foreach (var sig in fixedSigs)\n                sig.BoolValue = true;\n\t\t\t\n\t\t\tif (ShowPosition5Tabs)\n\t\t\t{\n\t\t\t\t// Show selected tab\n\t\t\t\tTriList.BooleanInput[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = true;\n\t\t\t\t// hook up tab object\n\t\t\t\tvar tabSo = TriList.SmartObjects[Position5TabsId];\n                tabSo.BooleanOutput[\"Tab Button 1 Press\"].UserObject = new Action<bool>(b => { if (!b) ShowTab(1); });\n                tabSo.BooleanOutput[\"Tab Button 2 Press\"].UserObject = new Action<bool>(b => { if (!b) ShowTab(2); });\n                tabSo.SigChange -= tabSo_SigChange;\n                tabSo.SigChange += tabSo_SigChange;\n            }\n\t\t}\n\n        void tabSo_SigChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.SmartObjectEventArgs args)\n        {\n            var uo = args.Sig.UserObject;\n            if(uo is Action<bool>)\n                (uo as Action<bool>)(args.Sig.BoolValue);\n        }\n\n\t\t/// <summary>\n\t\t/// Hide method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Hide()\n\t\t{\n            var fixedSigs = FixedVisibilityJoins.Select(u => TriList.BooleanInput[u]).ToList();\n            foreach (var sig in fixedSigs)\n                sig.BoolValue = false;\n\t\t\tif (ShowPosition5Tabs)\n\t\t\t{\n                TriList.BooleanInput[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = false;\n\n                //var tabSo = TriList.SmartObjects[Position5TabsId];\n                //tabSo.BooleanOutput[\"Tab Button 1 Press\"].UserObject = null;\n                //tabSo.BooleanOutput[\"Tab Button 2 Press\"].UserObject = null;\n\t\t\t}\n\t\t}\n\n\t\tvoid ShowTab(uint number)\n\t\t{\n\t\t\t// Ignore re-presses\n\t\t\tif (CurrentVisiblePosition5Item == number) return;\n\t\t\t// Swap subpage\n\t\t\tvar bi = TriList.BooleanInput;\n\t\t\tif (CurrentVisiblePosition5Item > 0)\n\t\t\t\tbi[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = false;\n\t\t\tCurrentVisiblePosition5Item = number;\n\t\t\tbi[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = true;\n\t\t}\n\t}\n\n\n\n\t/// <summary>\n\t/// Represents a SetTopBoxThreePanelPageManager\n\t/// </summary>\n\tpublic class SetTopBoxThreePanelPageManager : ThreePanelPlusOnePageManager\n\t{\n\t\tISetTopBoxControls SetTopBox;\n\t\tDevicePresetsView PresetsView;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the DpadSmartObjectId\n\t\t/// </summary>\n\t\tpublic uint DpadSmartObjectId { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the NumberPadSmartObjectId\n\t\t/// </summary>\n\t\tpublic uint NumberPadSmartObjectId { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the PresetsSmartObjectId\n\t\t/// </summary>\n\t\tpublic uint PresetsSmartObjectId { get; set; }\n\n\t\t/// <summary>\n\t\t/// A page manager for set top box that shows some combination of four different panels,\n\t\t/// in three slots on the page.\n\t\t/// </summary>\n\t\t/// <param name=\"stb\"></param>\n\t\t/// <param name=\"trilist\"></param>\n\t\tpublic SetTopBoxThreePanelPageManager(ISetTopBoxControls stb, BasicTriListWithSmartObject trilist)\n\t\t\t: base(trilist)\n\t\t{\n\t\t\tSetTopBox = stb;\n\t\t\tTriList = trilist;\n\n\t\t\tDpadSmartObjectId = 10011;\n\t\t\tNumberPadSmartObjectId = 10014;\n\t\t\tPresetsSmartObjectId = 10012;\n\t\t\tPosition5TabsId = 10081;\n\n\t\t\tbool dpad = stb.HasDpad;\n\t\t\tbool preset = stb.HasPresets;\n\t\t\tbool dvr = stb.HasDvr;\n\t\t\tbool numbers = stb.HasNumeric;\n\n            if (dpad && !preset && !dvr && !numbers) FixedVisibilityJoins = new uint[] { 10031, 10091 };\n            else if (!dpad && preset && !dvr && !numbers) FixedVisibilityJoins = new uint[] { 10032, 10091 };\n            else if (!dpad && !preset && dvr && !numbers) FixedVisibilityJoins = new uint[] { 10033, 10091 };\n            else if (!dpad && !preset && !dvr && numbers) FixedVisibilityJoins = new uint[] { 10034, 10091 };\n\n            else if (dpad && preset && !dvr && !numbers) FixedVisibilityJoins = new uint[] { 10042, 10021, 10092 };\n            else if (dpad && !preset && dvr && !numbers) FixedVisibilityJoins = new uint[] { 10043, 10021, 10092 };\n            else if (dpad && !preset && !dvr && numbers) FixedVisibilityJoins = new uint[] { 10044, 10021, 10092 };\n            else if (!dpad && preset && dvr && !numbers) FixedVisibilityJoins = new uint[] { 10043, 10022, 10092 };\n            else if (!dpad && preset && !dvr && numbers) FixedVisibilityJoins = new uint[] { 10044, 10022, 10092 };\n            else if (!dpad && !preset && dvr && numbers) FixedVisibilityJoins = new uint[] { 10044, 10023, 10092 };\n\n            else if (dpad && preset && dvr && !numbers) FixedVisibilityJoins = new uint[] { 10053, 10032, 10011, 10093 };\n            else if (dpad && preset && !dvr && numbers) FixedVisibilityJoins = new uint[] { 10054, 10032, 10011, 10093 };\n            else if (dpad && !preset && dvr && numbers) FixedVisibilityJoins = new uint[] { 10054, 10033, 10011, 10093 };\n            else if (!dpad && preset && dvr && numbers) FixedVisibilityJoins = new uint[] { 10054, 10033, 10012, 10093 };\n\n            else if (dpad && preset && dvr && numbers)\n            {\n                FixedVisibilityJoins = new uint[] { 10081, 10032, 10011, 10093 }; // special case\n                ShowPosition5Tabs = true;\n            }\n            // Bad config case\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, stb, \"WARNING: Not configured to show any UI elements\");\n                FixedVisibilityJoins = new uint[] { 10091 };\n            }\n\n\t\t\t// Build presets\n            if (stb.HasPresets && stb.TvPresets != null)\n\t\t\t{\n\t\t\t\tPresetsView = new DevicePresetsView(trilist, stb.TvPresets);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Show method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Show()\n\t\t{\n\t\t\tif(PresetsView != null)\n\t\t\t\tPresetsView.Attach();\n\t\t\tbase.Show();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hide method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Hide()\n\t\t{\n            if (PresetsView != null)\n    \t\t\tPresetsView.Detach();\n\t\t\tbase.Hide();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/UI PageManagers/SetTopBoxTwoPanelPageManager.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Presets;\n\nnamespace PepperDash.Essentials.Core.PageManagers\n{\n\t/// <summary>\n\t/// Represents a SetTopBoxMediumPageManager\n\t/// </summary>\n\tpublic class SetTopBoxMediumPageManager : MediumLeftSwitchablePageManager\n\t{\n\t\tISetTopBoxControls SetTopBox;\n\t\tDevicePresetsView PresetsView;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"stb\">set top box controls device</param>\n\t\t/// <param name=\"trilist\">trilist device</param>\n\t\tpublic SetTopBoxMediumPageManager(ISetTopBoxControls stb, BasicTriListWithSmartObject trilist)\n\t\t\t: base(stb.DisplayUiType)\n\t\t{\n\t\t\tSetTopBox = stb;\n\t\t\tTriList = trilist;\n\t\t\tif(stb.TvPresets != null)\n\t\t\t\tPresetsView = new DevicePresetsView(trilist, stb.TvPresets);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Show method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Show()\n\t\t{\n\t\t\tif(PresetsView != null)\n\t\t\t\tPresetsView.Attach();\n\t\t\tuint offset = GetOffsetJoin();\n\t\t\tif (SetTopBox.HasDvr) // Show backing page with DVR controls\n\t\t\t{\n\t\t\t\tBackingPageJoin = offset + 1;\n\t\t\t\tAllLeftSubpages = new uint[] { 6, 7, 8 };\n\t\t\t}\n\t\t\telse // Show the backing page with no DVR controls\n\t\t\t{\n\t\t\t\tBackingPageJoin = offset + 2;\n\t\t\t\tAllLeftSubpages = new uint[] { 6, 7 };\n\t\t\t}\n\n\t\t\tif (LeftSubpageJoin == 0)\n\t\t\t\tLeftSubpageJoin = offset + 6; // default to presets\n\t\t\tTriList.BooleanInput[BackingPageJoin].BoolValue = true;\n\t\t\tTriList.BooleanInput[LeftSubpageJoin].BoolValue = true;\n\n\t\t\t// Attach buttons to interlock\n\t\t\tforeach(var p in AllLeftSubpages)\n\t\t\t{\n\t\t\t\tvar p2 = p; // scope\n\t\t\t\tTriList.SetSigFalseAction(10000 + p2, () => InterlockLeftSubpage(p2));\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hide method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Hide()\n\t\t{\n\t\t\tTriList.BooleanInput[BackingPageJoin].BoolValue = false;\n\t\t\tTriList.BooleanInput[LeftSubpageJoin].BoolValue = false;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/UI PageManagers/SinglePageManager.cs",
    "content": "﻿using System.Collections.Generic;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Core.PageManagers\n{\n\t/// <summary>\n\t/// Represents a SinglePageManager\n\t/// </summary>\n\tpublic class SinglePageManager : PageManager\n\t{\n\t\tBasicTriList TriList;\n\t\tuint BackingPageJoin;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"pageJoin\">join for thepage</param>\n\t\t/// <param name=\"trilist\">trilist</param>\n\t\tpublic SinglePageManager(uint pageJoin, BasicTriList trilist)\n\t\t{\n\t\t\tTriList = trilist;\n\t\t\tBackingPageJoin = pageJoin;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Show method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void Show()\n\t\t{\n\t\t\tTriList.BooleanInput[BackingPageJoin].BoolValue = true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hide method\n\t\t/// </summary>\n\t\tpublic override void Hide()\n\t\t{\n\t\t\tTriList.BooleanInput[BackingPageJoin].BoolValue = false;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Utilities/ActionSequence.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.CrestronThread;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\n\nusing Newtonsoft.Json;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Utilities\n{\n    /// <summary>\n    /// A device that executes a sequence of actions with optional delays between actions\n    /// </summary>\n    [Description(\"A device that executes a sequence of actions with optional delays between actions\")]\n    public class ActionSequence : EssentialsDevice\n    {\n        private ActionSequencePropertiesConfig _propertiesConfig;\n\n        private CrestronQueue<SequencedDeviceActionWrapper> _actionQueue;\n\n        private Thread _worker;\n\n        private bool _allowActionsToExecute;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"config\"></param>\n        public ActionSequence(string key, DeviceConfig config)\n            : base(key, config.Name)\n        {\n            var props = config.Properties.ToObject<ActionSequencePropertiesConfig>();\n            _propertiesConfig = props;\n\n            if (_propertiesConfig != null)\n            {\n                if (_propertiesConfig.ActionSequence.Count > 0)\n                {\n                    _actionQueue = new CrestronQueue<SequencedDeviceActionWrapper>(_propertiesConfig.ActionSequence.Count);\n                }\n            }\n        }\n\n        /// <summary>\n        /// StartSequence method\n        /// </summary>\n        public void StartSequence()\n        {\n            if (_worker !=null && _worker.ThreadState == Thread.eThreadStates.ThreadRunning)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Thread already running.  Cannot Start Sequence\");\n                return;\n            }\n\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Starting Action Sequence\");\n            _allowActionsToExecute = true;\n            AddActionsToQueue();\n            _worker = new Thread(ProcessActions, null, Thread.eThreadStartOptions.Running);\n        }\n\n        /// <summary>\n        /// StopSequence method\n        /// </summary>\n        public void StopSequence()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Action Sequence\");\n            _allowActionsToExecute = false;\n            _worker.Abort();\n        }\n\n        /// <summary>\n        /// Populates the queue from the configuration information\n        /// </summary>\n        private void AddActionsToQueue()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Adding {0} actions to queue\", _propertiesConfig.ActionSequence.Count);\n\n            for (int i = 0; i < _propertiesConfig.ActionSequence.Count; i++)\n            {\n                _actionQueue.Enqueue(_propertiesConfig.ActionSequence[i]);\n            }\n        }\n\n        private object ProcessActions(object obj)\n        {\n            while (_allowActionsToExecute && _actionQueue.Count > 0)\n            {\n                SequencedDeviceActionWrapper action = null;\n\n                action = _actionQueue.Dequeue();\n                if (action == null)\n                    break;\n\n                // Delay before executing\n                if (action.DelayMs > 0)\n                    Thread.Sleep(action.DelayMs);\n\n                ExecuteAction(action);\n            }\n\n            return null;\n        }\n\n        private void ExecuteAction(DeviceActionWrapper action)\n        {\n            if (action == null)\n                return;\n\n            try\n            {\n                DeviceJsonApi.DoDeviceAction(action);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Error Executing Action: {0}\", e);\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a ActionSequencePropertiesConfig\n    /// </summary>\n    public class ActionSequencePropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the ActionSequence\n        /// </summary>\n        [JsonProperty(\"actionSequence\")]\n        public List<SequencedDeviceActionWrapper> ActionSequence { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public ActionSequencePropertiesConfig()\n        {\n            ActionSequence = new List<SequencedDeviceActionWrapper>();\n        }\n    }\n\n    /// <summary>\n    /// Represents a SequencedDeviceActionWrapper\n    /// </summary>\n    public class SequencedDeviceActionWrapper : DeviceActionWrapper\n    {\n        /// <summary>\n        /// Gets or sets the DelayMs\n        /// </summary>\n        [JsonProperty(\"delayMs\")]\n        public int DelayMs { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a ActionSequenceFactory\n    /// </summary>\n    public class ActionSequenceFactory : EssentialsDeviceFactory<ActionSequence>\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public ActionSequenceFactory()\n        {\n            TypeNames = new List<string>() { \"actionsequence\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <param name=\"dc\">device config</param>\n        /// <returns></returns>\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new ActionSequence Device\");\n\n            return new ActionSequence(dc.Key, dc);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/VideoStatus/VideoStatusOutputs.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DM;\n\nnamespace PepperDash.Essentials.Core\n{\n\t/// <summary>\n\t/// Use this class to pass in values to RoutingInputPorts.  Unused properties will have default \n\t/// funcs assigned to them.\n\t/// </summary>\n\tpublic class VideoStatusFuncsWrapper\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the HasVideoStatusFunc\n\t\t/// </summary>\n\t\tpublic Func<bool> HasVideoStatusFunc { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the HdcpActiveFeedbackFunc\n\t\t/// </summary>\n\t\tpublic Func<bool> HdcpActiveFeedbackFunc { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the HdcpStateFeedbackFunc\n\t\t/// </summary>\n\t\tpublic Func<string> HdcpStateFeedbackFunc { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the VideoResolutionFeedbackFunc\n\t\t/// </summary>\n\t\tpublic Func<string> VideoResolutionFeedbackFunc { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the VideoSyncFeedbackFunc\n\t\t/// </summary>\n\t\tpublic Func<bool> VideoSyncFeedbackFunc { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic VideoStatusFuncsWrapper()\n\t\t{\n\t\t\tHasVideoStatusFunc = () => true;\n\t\t\tHdcpActiveFeedbackFunc = () => false;\n\t\t\tHdcpStateFeedbackFunc = () => \"\";\n\t\t\tVideoResolutionFeedbackFunc = () => \"n/a\";\n\t\t\tVideoSyncFeedbackFunc = () => false;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a VideoStatusOutputs\n\t/// </summary>\n\tpublic class VideoStatusOutputs\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the HasVideoStatusFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback HasVideoStatusFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the HdcpActiveFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback HdcpActiveFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the HdcpStateFeedback\n\t\t/// </summary>\n\t\tpublic StringFeedback HdcpStateFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the VideoResolutionFeedback\n\t\t/// </summary>\n\t\tpublic StringFeedback VideoResolutionFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the VideoSyncFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback VideoSyncFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the NoStatus\n\t\t/// </summary>\n\t\tpublic static VideoStatusOutputs NoStatus { get { return _Default; } }\n\n\t\tstatic VideoStatusOutputs _Default = new VideoStatusOutputs(new VideoStatusFuncsWrapper\n\t\t\t{\n\t\t\t\tHasVideoStatusFunc = () => false\n\t\t\t});\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"funcs\"></param>\n\t\tpublic VideoStatusOutputs(VideoStatusFuncsWrapper funcs)\n\t\t{\n\t\t\tHasVideoStatusFeedback = new BoolFeedback(\"HasVideoStatusFeedback\", funcs.HasVideoStatusFunc);\n\t\t\tHdcpActiveFeedback = new BoolFeedback(\"HdcpActiveFeedback\", funcs.HdcpActiveFeedbackFunc);\n\t\t\tHdcpStateFeedback = new StringFeedback(\"HdcpStateFeedback\", funcs.HdcpStateFeedbackFunc);\n\t\t\tVideoResolutionFeedback = new StringFeedback(\"VideoResolutionFeedback\", \n\t\t\t\tfuncs.VideoResolutionFeedbackFunc);\n\t\t\tVideoSyncFeedback = new BoolFeedback(\"VideoSyncFeedback\", funcs.VideoSyncFeedbackFunc);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// FireAll method\n\t\t/// </summary>\n\t\tpublic void FireAll()\n\t\t{\n\t\t\tHasVideoStatusFeedback.FireUpdate();\n\t\t\tHdcpActiveFeedback.FireUpdate();\n\t\t\tHdcpActiveFeedback.FireUpdate();\n\t\t\tVideoResolutionFeedback.FireUpdate();\n\t\t\tVideoSyncFeedback.FireUpdate();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ToList method\n\t\t/// </summary>\n\t\tpublic List<Feedback> ToList()\n\t\t{\n\t\t\treturn new List<Feedback>\n\t\t\t{\n\t\t\t\tHasVideoStatusFeedback,\n\t\t\t\tHdcpActiveFeedback,\n\t\t\t\tHdcpStateFeedback,\n\t\t\t\tVideoResolutionFeedback,\n\t\t\t\tVideoSyncFeedback\n\t\t\t};\n\t\t}\n\t}\n\n\t// /// <summary>\n\t// /// Wraps up the common video statuses exposed on a video input port\n\t// /// </summary>\n\t//public class BasicVideoStatus : IBasicVideoStatus\n\t//{\n\t//    public event VideoStatusChangeHandler VideoStatusChange;\n\n\t//    public bool HasVideoStatus { get; private set; }\n\n\t//    public bool HdcpActive\n\t//    {\n\t//        get { return HdcpActiveFunc != null ? HdcpActiveFunc() : false; }\n\t//    }\n\n\t//    public string HdcpState\n\t//    {\n\t//        get { return HdcpStateFunc != null? HdcpStateFunc() : \"\"; }\n\t//    }\n\n\t//    public string VideoResolution\n\t//    {\n\t//        get { return VideoResolutionFunc != null ? VideoResolutionFunc() : \"\"; }\n\t//    }\n\n\t//    public bool VideoSync\n\t//    {\n\t//        get { return VideoSyncFunc != null ? VideoSyncFunc() : false; }\n\t//    }\n\n\t//    Func<bool> HdcpActiveFunc;\n\t//    Func<string> HdcpStateFunc;\n\t//    Func<string> VideoResolutionFunc;\n\t//    Func<bool> VideoSyncFunc;\n\n\t//    public BasicVideoStatus(bool hasVideoStatus, Func<bool> hdcpActiveFunc,\n\t//        Func<string> hdcpStateFunc, Func<string> videoResolutionFunc, Func<bool> videoSyncFunc)\n\t//    {\n\t//        HasVideoStatus = hasVideoStatus;\n\t//        HdcpActiveFunc = hdcpActiveFunc;\n\t//        HdcpStateFunc = hdcpStateFunc;\n\t//        VideoResolutionFunc = videoResolutionFunc;\n\t//        VideoSyncFunc = videoSyncFunc;\n\t//    }\n\t//}\n\n\t//public enum eVideoStatusChangeType\n\t//{\n\t//    HdcpActive,\n\t//    HdcpState,\n\t//    VideoResolution,\n\t//    VideoSync\n\t//}\n\n\t//public interface IBasicVideoStatus\n\t//{\n\t//    event VideoStatusChangeHandler VideoStatusChange;\n\t//    bool HdcpActive { get; }\n\t//    string HdcpState { get; }\n\t//    string VideoResolution { get; }\n\t//    bool VideoSync { get; }\n\t//}\n\n\t//public delegate void VideoStatusChangeHandler(IBasicVideoStatus device, eVideoStatusChangeType type);\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/EssentialsWebApi.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.WebScripting;\nusing PepperDash.Core;\nusing PepperDash.Core.Web;\nusing PepperDash.Essentials.Core.Web.RequestHandlers;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Web\n{\n    /// <summary>\n    /// Represents a EssentialsWebApi\n    /// </summary>\n    public class EssentialsWebApi : EssentialsDevice\n    {\n        private readonly WebApiServer _server;\n\n        private readonly WebApiServer _debugServer;\n\n\n\n\n        ///<example>\n        /// http(s)://{ipaddress}/cws/{basePath}\n        /// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath}\n        /// </example>\n        private readonly string _defaultBasePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance\n            ? string.Format(\"/app{0:00}/api\", InitialParametersClass.ApplicationNumber)\n            : \"/api\";\n\n        private const int DebugTrace = 0;\n        private const int DebugInfo = 1;\n        private const int DebugVerbose = 2;\n\n        /// <summary>\n        /// Gets or sets the BasePath\n        /// </summary>\n        public string BasePath { get; private set; }\n\n        /// <summary>\n        /// Tracks if CWS is registered\n        /// </summary>\n        public bool IsRegistered\n        {\n            get { return _server.IsRegistered; }\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"name\"></param>\t\t\n        public EssentialsWebApi(string key, string name)\n            : this(key, name, null)\n        {\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"name\"></param>\n        /// <param name=\"config\"></param>\n        public EssentialsWebApi(string key, string name, EssentialsWebApiPropertiesConfig config)\n            : base(key, name)\n        {\n            Key = key;\n\n            if (config == null)\n                BasePath = _defaultBasePath;\n            else\n                BasePath = string.IsNullOrEmpty(config.BasePath) ? _defaultBasePath : config.BasePath;\n\n            _server = new WebApiServer(Key, Name, BasePath);\n\n            _debugServer = new WebApiServer($\"{key}-debug-app\", $\"{name} Debug App\", \"/debug\");\n            _debugServer.SetFallbackHandler(new ServeDebugAppRequestHandler());\n\n            SetupRoutes();\n        }\n\n        private void SetupRoutes()\n        {\n            var routes = new List<HttpCwsRoute>\n            {\n                new HttpCwsRoute(\"login\")\n                {\n                    Name = \"Root\",\n                    RouteHandler = new LoginRequestHandler()\n                },\n                new HttpCwsRoute(\"versions\")\n                {\n                    Name = \"ReportVersions\",\n                    RouteHandler = new ReportVersionsRequestHandler()\n                },\n                new HttpCwsRoute(\"appdebug\")\n                {\n                    Name = \"AppDebug\",\n                    RouteHandler = new AppDebugRequestHandler()\n                },\n                new HttpCwsRoute(\"devices\")\n                {\n                    Name = \"DevList\",\n                    RouteHandler = new DevListRequestHandler()\n                },\n                new HttpCwsRoute(\"deviceCommands/{deviceKey}\")\n                {\n                    Name = \"DevJson\",\n                    RouteHandler = new DevJsonRequestHandler()\n                },\n                new HttpCwsRoute(\"deviceProperties/{deviceKey}\")\n                {\n                    Name = \"DevProps\",\n                    RouteHandler = new DevPropsRequestHandler()\n                },\n                new HttpCwsRoute(\"deviceMethods/{deviceKey}\")\n                {\n                    Name = \"DevMethods\",\n                    RouteHandler = new DevMethodsRequestHandler()\n                },\n                new HttpCwsRoute(\"deviceFeedbacks/{deviceKey}\")\n                {\n                    Name = \"GetFeedbacksForDeviceKey\",\n                    RouteHandler = new GetFeedbacksForDeviceRequestHandler()\n                },\n                new HttpCwsRoute(\"deviceStreamDebug\")\n                {\n                    Name = \"SetDeviceStreamDebug\",\n                    RouteHandler = new SetDeviceStreamDebugRequestHandler()\n                },\n                new HttpCwsRoute(\"disableAllStreamDebug\")\n                {\n                    Name = \"DisableAllStreamDebug\",\n                    RouteHandler = new DisableAllStreamDebugRequestHandler()\n                },\n                new HttpCwsRoute(\"config\")\n                {\n                    Name = \"ShowConfig\",\n                    RouteHandler = new ShowConfigRequestHandler()\n                },\n                new HttpCwsRoute(\"types\")\n                {\n                    Name = \"GetTypes\",\n                    RouteHandler = new GetTypesRequestHandler()\n                },\n                new HttpCwsRoute(\"types/{filter}\")\n                {\n                    Name = \"GetTypesByFilter\",\n                    RouteHandler = new GetTypesByFilterRequestHandler()\n                },\n                new HttpCwsRoute(\"joinMap/{bridgeKey}\")\n                {\n                    Name = \"GetJoinMapsForBridgeKey\",\n                    RouteHandler = new GetJoinMapForBridgeKeyRequestHandler()\n                },\n                new HttpCwsRoute(\"joinMap/{bridgeKey}/{deviceKey}\")\n                {\n                    Name = \"GetJoinMapsForDeviceKey\",\n                    RouteHandler = new GetJoinMapForDeviceKeyRequestHandler()\n                },\n                new HttpCwsRoute(\"debugSession\")\n                {\n                    Name = \"DebugSession\",\n                    RouteHandler = new DebugSessionRequestHandler()\n                },\n                new HttpCwsRoute(\"doNotLoadConfigOnNextBoot\")\n                {\n                    Name = \"DoNotLoadConfigOnNextBoot\",\n                    RouteHandler = new DoNotLoadConfigOnNextBootRequestHandler()\n                },\n                new HttpCwsRoute(\"restartProgram\")\n                {\n                    Name = \"Restart Program\",\n                    RouteHandler = new RestartProgramRequestHandler()\n                },\n                new HttpCwsRoute(\"loadConfig\")\n                {\n                    Name = \"Load Config\",\n                    RouteHandler = new LoadConfigRequestHandler()\n                },\n                new HttpCwsRoute(\"tielines\")\n                {\n                    Name = \"Get TieLines\",\n                    RouteHandler = new GetTieLinesRequestHandler()\n                },\n                new HttpCwsRoute(\"device/{deviceKey}/routingPorts\")\n                {\n                    Name = \"Get Routing Ports for a device\",\n                    RouteHandler = new GetRoutingPortsHandler()\n                },\n                new HttpCwsRoute(\"routingDevicesAndTieLines\")\n                {\n                    Name = \"Get Routing Devices and TieLines\",\n                    RouteHandler = new GetRoutingDevicesAndTieLinesHandler()\n                },\n            };\n\n            AddRoute(routes);\n        }\n\n        /// <summary>\n        /// Add a single route to the API. MUST be done during the activation phase\n        /// </summary>\n        /// <param name=\"route\"></param>\n        /// <summary>\n        /// AddRoute method\n        /// </summary>\n        public void AddRoute(HttpCwsRoute route)\n        {\n            _server.AddRoute(route);\n        }\n\n        /// <summary>\n        /// Add a collection of routes to the API. MUST be done during the activation phase\n        /// </summary>\n        /// <param name=\"routes\"></param>\n        public void AddRoute(List<HttpCwsRoute> routes)\n        {\n            foreach (var route in routes)\n            {\n                AddRoute(route);\n            }\n        }\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        /// <inheritdoc />\n        public override void Initialize()\n        {\n            AddRoute(new HttpCwsRoute(\"apiPaths\")\n            {\n                Name = \"GetPaths\",\n                RouteHandler = new GetRoutesHandler(_server.GetRouteCollection(), BasePath)\n            });\n\n            // If running on an appliance\n            if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)\n            {\n                /*\n\t\t\t\t\tWEBSERVER [ON | OFF | TIMEOUT <VALUE IN SECONDS> | MAXSESSIONSPERUSER <Number of sessions>]\n\t\t\t\t*/\n                var response = string.Empty;\n                CrestronConsole.SendControlSystemCommand(\"webserver\", ref response);\n                if (response.Contains(\"OFF\")) return;\n\n                var is4Series = eCrestronSeries.Series4 == (Global.ProcessorSeries & eCrestronSeries.Series4);\n                Debug.LogMessage(LogEventLevel.Verbose, \"Starting Essentials Web API on {0} Appliance\", is4Series ? \"4-series\" : \"3-series\");\n\n                _server.Start();\n                _debugServer.Start();\n\n                GetPaths();\n\n                return;\n            }\n\n            // Automatically start CWS when running on a server (Linux OS, Virtual Control)\n            Debug.LogMessage(LogEventLevel.Verbose, \"Starting Essentials Web API on Virtual Control Server\");\n\n            _server.Start();\n            _debugServer.Start();\n\n            GetPaths();\n        }\n\n        /// <summary>\n        /// Print the available paths\n        /// </summary>\n        /// <example>\n        /// http(s)://{ipaddress}/cws/{basePath}\n        /// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath}\n        /// </example>\n        /// <summary>\n        /// GetPaths method\n        /// </summary>\n        public void GetPaths()\n        {\n            Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));\n\n            var currentIp = CrestronEthernetHelper.GetEthernetParameter(\n                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);\n\n            var hostname = CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);\n\n            var path = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server\n                ? $\"https://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws{BasePath}\"\n                : $\"https://{currentIp}/cws{BasePath}\";\n\n            Debug.LogMessage(LogEventLevel.Information, this, \"Server:{path:l}\", path);\n\n            var routeCollection = _server.GetRouteCollection();\n            if (routeCollection == null)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Server route collection is null\");\n                return;\n            }\n            Debug.LogMessage(LogEventLevel.Information, this, \"Configured Routes:\");\n            foreach (var route in routeCollection)\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"{routeName:l}: {routePath:l}/{routeUrl:l}\", route.Name, path, route.Url);\n            }\n            Debug.LogInformation(this, \"Web API initialized and ready to accept requests\");\n\n            Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));\n\n            var debugAppUrl = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server\n   ? $\"https://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws/debug\"\n   : $\"https://{currentIp}/cws/debug\";\n\n            Debug.LogMessage(LogEventLevel.Information, this, \"Developer Tools Web App available at: {debugAppUrl:l}\", debugAppUrl);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/EssentialsWebApiFactory.cs",
    "content": "﻿using System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Web\n{\n /// <summary>\n /// Represents a EssentialsWebApiFactory\n /// </summary>\n\tpublic class EssentialsWebApiFactory : EssentialsDeviceFactory<EssentialsWebApi>\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic EssentialsWebApiFactory()\n\t\t{\n\t\t\tTypeNames = new List<string> { \"EssentialsWebApi\" };\n\t\t}\n\n\t\t/// <summary>\n\t\t/// BuildDevice method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override EssentialsDevice BuildDevice(DeviceConfig dc)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Essentials Web API Server\");\n\n\t\t\tvar props = dc.Properties.ToObject<EssentialsWebApiPropertiesConfig>();\n\t\t\tif (props != null) return new EssentialsWebApi(dc.Key, dc.Name, props);\n\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"Factory failed to create new Essentials Web API Server\");\n\t\t\treturn null;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/EssentialsWebApiHelpers.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp.WebScripting;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Core.Web\n{\n\t/// <summary>\n\t/// EssentialsWebApiHelpers class\n\t/// </summary>\n\tpublic static class EssentialsWebApiHelpers\n\t{\n\t\t/// <summary>\n\t\t/// GetRequestBody method\n\t\t/// </summary>\n\t\tpublic static string GetRequestBody(this HttpCwsRequest request)\n\t\t{\n\t\t\tvar bytes = new byte[request.ContentLength];\n\n\t\t\trequest.InputStream.Read(bytes, 0, request.ContentLength);\n\n\t\t\treturn Encoding.UTF8.GetString(bytes, 0, bytes.Length);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// MapToAssemblyObject method\n\t\t/// </summary>\n\t\tpublic static object MapToAssemblyObject(LoadedAssembly assembly)\n\t\t{\n\t\t\treturn new\n\t\t\t{\n                assembly.Name,\n                assembly.Version\n\t\t\t};\n\t\t}\n\n\n\n\t\t/// <summary>\n\t\t/// MapToDeviceListObject method\n\t\t/// </summary>\n\t\tpublic static object MapToDeviceListObject(IKeyed device)\n\t\t{\n\t\t\tvar interfaces = device.GetType()\n\t\t\t\t.GetInterfaces()\n\t\t\t\t.Select(i => i.Name)\n\t\t\t\t.ToList();\n\n\t\t\treturn new\n\t\t\t{\n                device.Key,\n\t\t\t\tName = (device is IKeyName)\n\t\t\t\t\t? (device as IKeyName).Name\n\t\t\t\t\t: \"---\",\n\t\t\t\tInterfaces = interfaces\n\t\t\t};\n\t\t}\n\n\t\t/// <summary>\n\t\t/// MapJoinToObject method\n\t\t/// </summary>\n\t\tpublic static object MapJoinToObject(string key, JoinMapBaseAdvanced join)\n\t\t{\n\t\t\tvar kp = new KeyValuePair<string, JoinMapBaseAdvanced>(key, join);\n\n\t\t\treturn MapJoinToObject(kp);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// MapJoinToObject method\n\t\t/// </summary>\n\t\tpublic static object MapJoinToObject(KeyValuePair<string, JoinMapBaseAdvanced> join)\n\t\t{\n\t\t\treturn new\n\t\t\t{\n\t\t\t\tDeviceKey = join.Key,\n\t\t\t\tJoins = join.Value.Joins.Select(j => MapJoinDataCompleteToObject(j))\n\t\t\t};\n\t\t}\n\n\t\t/// <summary>\n\t\t/// MapJoinDataCompleteToObject method\n\t\t/// </summary>\n\t\tpublic static object MapJoinDataCompleteToObject(KeyValuePair<string, JoinDataComplete> joinData)\n\t\t{\n\t\t\treturn new\n\t\t\t{\n\t\t\t\tSignal = joinData.Key,\n\t\t\t\tDescription = joinData.Value.Metadata.Description,\n\t\t\t\tJoinNumber = joinData.Value.JoinNumber,\n\t\t\t\tJoinSpan = joinData.Value.JoinSpan,\n\t\t\t\tJoinType = joinData.Value.Metadata.JoinType.ToString(),\n\t\t\t\tJoinCapabilities = joinData.Value.Metadata.JoinCapabilities.ToString()\n\t\t\t};\n\t\t}\n\n\t\t/// <summary>\n\t\t/// MapDeviceTypeToObject method\n\t\t/// </summary>\n\t\tpublic static object MapDeviceTypeToObject(string key, DeviceFactoryWrapper device)\n\t\t{\n\t\t\tvar kp = new KeyValuePair<string, DeviceFactoryWrapper>(key, device);\n\n\t\t\treturn MapDeviceTypeToObject(kp);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// MapDeviceTypeToObject method\n\t\t/// </summary>\n\t\tpublic static object MapDeviceTypeToObject(KeyValuePair<string, DeviceFactoryWrapper> device)\n\t\t{\n\t\t\treturn new\n\t\t\t{\n\t\t\t\tType = device.Key,\n\t\t\t\tDescription = device.Value.Description,\n\t\t\t\tCType = device.Value.Type == null ? \"---\": device.Value.Type.ToString()\n\t\t\t};\n\t\t}\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/EssentialsWebApiPropertiesConfig.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Core.Web\n{\n\t/// <summary>\n\t/// Represents a EssentialsWebApiPropertiesConfig\n\t/// </summary>\n\tpublic class EssentialsWebApiPropertiesConfig\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the BasePath\n\t\t/// </summary>\n\t\t[JsonProperty(\"basePath\")]\n\t\tpublic string BasePath { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/AppDebugRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\nusing System;\nusing Serilog.Events;\nusing Newtonsoft.Json.Converters;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a AppDebugRequestHandler\n /// </summary>\n\tpublic class AppDebugRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\t\t\n\t\tpublic AppDebugRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar appDebug = new AppDebug { MinimumLevel = Debug.WebsocketMinimumLogLevel };\n\n\t\t\tvar body = JsonConvert.SerializeObject(appDebug, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.Write(body, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePost(HttpCwsContext context)\n\t\t{\n\t\t\tif (context.Request.ContentLength < 0)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar data = context.Request.GetRequestBody();\n\t\t\tif (string.IsNullOrEmpty(data))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar appDebug = new AppDebug();\n\t\t\tvar requestBody = JsonConvert.DeserializeObject<AppDebug>(data);\n\n\t\t\tDebug.SetWebSocketMinimumDebugLevel(requestBody.MinimumLevel);\n\n\t\t\tappDebug.MinimumLevel = Debug.WebsocketMinimumLogLevel;\n\t\t\tvar responseBody = JsonConvert.SerializeObject(appDebug, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.Write(responseBody, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a AppDebug\n\t/// </summary>\n\tpublic class AppDebug\n\t{\n        /// <summary>\n        /// Gets or sets the MinimumLevel\n        /// </summary>\n\t\t[JsonProperty(\"minimumLevel\", NullValueHandling = NullValueHandling.Ignore)]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public LogEventLevel MinimumLevel { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Crestron.SimplSharp.WebScripting;\nusing Crestron.SimplSharpPro.EthernetCommunication;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\nusing Serilog.Events;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n    /// <summary>\n    /// Represents a DebugSessionRequestHandler\n    /// </summary>\n    public class DebugSessionRequestHandler : WebApiBaseRequestHandler\n    {    \n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public DebugSessionRequestHandler()\n            : base(true)\n        {\n        }\n\n        /// <summary>\n        /// Gets details for a debug session\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContext context)\n        {\n            var routeData = context.Request.RouteData;\n            if (routeData == null)\n            {\n                context.Response.StatusCode = 400;\n                context.Response.StatusDescription = \"Bad Request\";\n                context.Response.End();\n\n                return;\n            }\n\n            try\n            {\n                var ip = CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);\n\n                var port = 0;\n\n                if (!Debug.WebsocketSink.IsRunning)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Starting WS Server\");\n                    // Generate a random port within a specified range\n                    port = new Random().Next(65435, 65535);\n                    // Start the WS Server\n                    Debug.WebsocketSink.StartServerAndSetPort(port);\n                    Debug.SetWebSocketMinimumDebugLevel(Serilog.Events.LogEventLevel.Verbose);\n                }\n\n                var url = Debug.WebsocketSink.Url;\n\n                object data = new\n                {\n                    url = Debug.WebsocketSink.Url\n                };\n\n                Debug.LogMessage(LogEventLevel.Information, \"Debug Session URL: {0}\", url);\n\n                // Return the port number with the full url of the WS Server\n                var res = JsonConvert.SerializeObject(data);\n\n                context.Response.ContentType = \"application/json\";\n                context.Response.ContentEncoding = Encoding.UTF8;\n                context.Response.StatusCode = 200;\n                context.Response.StatusDescription = \"OK\";\n                context.Response.Write(res, false);\n                context.Response.End();\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(LogEventLevel.Information, \"Error: {0}\", e);\n            }\n        }\n\n        /// <summary>\n        /// Stops a debug session\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected override void HandlePost(HttpCwsContext context)\n        {\n            Debug.WebsocketSink.StopServer();\n\n            context.Response.StatusCode = 200;\n            context.Response.StatusDescription = \"OK\";\n            context.Response.End();\n\n            Debug.LogMessage(LogEventLevel.Information, \"Websocket Debug Session Stopped\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/DefaultRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a DefaultRequestHandler\n /// </summary>\n\tpublic class DefaultRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\t\t\n\t\tpublic DefaultRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles CONNECT method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleConnect(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles DELETE method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleDelete(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles HEAD method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleHead(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles OPTIONS method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleOptions(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles PATCH method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePatch(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePost(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles PUT method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePut(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles TRACE method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleTrace(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 418;\n\t\t\tcontext.Response.StatusDescription = \"I'm a teapot\";\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/DevJsonRequestHandler.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a DevJsonRequestHandler\n /// </summary>\n\tpublic class DevJsonRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic DevJsonRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePost(HttpCwsContext context)\n\t\t{\n            var routeData = context.Request.RouteData;\n\n            if(routeData == null)\n            {\n                context.Response.StatusCode = 400;\n                context.Response.StatusDescription = \"Bad Request\";\n                context.Response.End();\n\n                return;\n            }\n\n            if(!routeData.Values.TryGetValue(\"deviceKey\", out var deviceKey))\n            {\n                context.Response.StatusCode = 400;\n                context.Response.StatusDescription = \"Bad Request\";\n                context.Response.End();\n\n                return;\n            }\n\n\t\t\tif (context.Request.ContentLength < 0)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request: no body\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar data = context.Request.GetRequestBody();\n\n\t\t\tif (string.IsNullOrEmpty(data))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request: no body\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\ttry\n\t\t\t{                \n                var daw = new DeviceActionWrapper { DeviceKey = (string) deviceKey};\n\n                JsonConvert.PopulateObject(data, daw);\n\n                Debug.LogMessage(LogEventLevel.Verbose, \"Device Action Wrapper: {@wrapper}\", null, daw);\n\n\t\t\t\tDeviceJsonApi.DoDeviceAction(daw);\n\n\t\t\t\tcontext.Response.StatusCode = 200;\n\t\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\t\tcontext.Response.End();\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(ex, \"Error handling device command: {Exception}\");\t\t\t\t\n\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n                context.Response.Write(JsonConvert.SerializeObject(new { error = ex.Message }), false);\n\t\t\t\tcontext.Response.End();\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/DevListRequestHandler.cs",
    "content": "﻿using System.Linq;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a DevListRequestHandler\n /// </summary>\n\tpublic class DevListRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic DevListRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar allDevices = DeviceManager.AllDevices;\n\t\t\tif (allDevices == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n\t\t\t\tcontext.Response.StatusDescription = \"Not Found\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tallDevices.Sort((a, b) => string.Compare(a.Key, b.Key, System.StringComparison.Ordinal));\n\n\t\t\tvar deviceList = allDevices.Select(d => EssentialsWebApiHelpers.MapToDeviceListObject(d)).ToList();\n\n\t\t\tvar js = JsonConvert.SerializeObject(deviceList, Formatting.Indented);\t\t\t\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = System.Text.Encoding.UTF8;\n\t\t\tcontext.Response.Write(js, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/DevMethodsRequestHandler.cs",
    "content": "﻿using System.Text;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a DevMethodsRequestHandler\n /// </summary>\n\tpublic class DevMethodsRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic DevMethodsRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n            var routeData = context.Request.RouteData;\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, \"Getting DevMethods: {@routeData}\", routeData);\n            if (routeData == null)\n            {\n                context.Response.StatusCode = 400;\n                context.Response.StatusDescription = \"Bad Request\";\n                context.Response.End();\n\n                return;\n            }\n\n            object deviceObj;\n\t\t\tif (!routeData.Values.TryGetValue(\"deviceKey\", out deviceObj))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar device = DeviceManager.GetDeviceForKey(deviceObj.ToString());\n\n\t\t\tif (device == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n                context.Response.StatusDescription = \"Device Not Found\";\n                context.Response.End();\n\n                return;\n\t\t\t}\n\n            var deviceMethods = DeviceJsonApi.GetMethods(device.Key);\n\t\t\tif (deviceMethods == null || deviceMethods.ToLower().Contains(\"no device\"))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n\t\t\t\tcontext.Response.StatusDescription = \"Not Found\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\t\t\t\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = Encoding.UTF8;\n\t\t\tcontext.Response.Write(deviceMethods, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/DevPropsRequestHandler.cs",
    "content": "﻿using System.Text;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a DevPropsRequestHandler\n /// </summary>\n\tpublic class DevPropsRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic DevPropsRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n        /// <summary>\n        /// Handles GET method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected override void HandleGet(HttpCwsContext context)\n        {\n            var routeData = context.Request.RouteData;\n            if (routeData == null)\n            {\n                context.Response.StatusCode = 400;\n                context.Response.StatusDescription = \"Bad Request\";\n                context.Response.End();\n\n                return;\n            }\n\n            object deviceObj;\n            if (!routeData.Values.TryGetValue(\"deviceKey\", out deviceObj))\n            {\n                context.Response.StatusCode = 400;\n                context.Response.StatusDescription = \"Bad Request\";\n                context.Response.End();\n\n                return;\n            }\n\n            var device = DeviceManager.GetDeviceForKey(deviceObj.ToString());\n\n            if (device == null)\n            {\n                context.Response.StatusCode = 404;\n                context.Response.StatusDescription = \"Device Not Found\";\n                context.Response.End();\n\n                return;\n            }\n\n            var deviceProperties = DeviceJsonApi.GetProperties(device.Key);\n            if (deviceProperties == null || deviceProperties.ToLower().Contains(\"no device\"))\n            {\n                context.Response.StatusCode = 404;\n                context.Response.StatusDescription = \"Not Found\";\n                context.Response.End();\n\n                return;\n            }\n\n            context.Response.StatusCode = 200;\n            context.Response.StatusDescription = \"OK\";\n            context.Response.ContentType = \"application/json\";\n            context.Response.ContentEncoding = Encoding.UTF8;\n            context.Response.Write(deviceProperties, false);\n            context.Response.End();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/DisableAllStreamDebugRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a DisableAllStreamDebugRequestHandler\n /// </summary>\n\tpublic class DisableAllStreamDebugRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic DisableAllStreamDebugRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePost(HttpCwsContext context)\n\t\t{\n\t\t\tDeviceManager.DisableAllDeviceStreamDebugging();\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/DoNotLoadConfigOnNextBootRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n\t/// <summary>\n\t/// Represents a DoNotLoadConfigOnNextBootRequestHandler\n\t/// </summary>\n\tpublic class DoNotLoadConfigOnNextBootRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\t\t\n\t\tpublic DoNotLoadConfigOnNextBootRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar data = new Data\n\t\t\t{\n\t\t\t\tDoNotLoadConfigOnNextBoot = Debug.DoNotLoadConfigOnNextBoot\n            };\n\n\t\t\tvar body = JsonConvert.SerializeObject(data, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.Write(body, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePost(HttpCwsContext context)\n\t\t{\n\t\t\tif (context.Request.ContentLength < 0)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar data = context.Request.GetRequestBody();\n\t\t\tif (string.IsNullOrEmpty(data))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar d = new Data();\n\t\t\tvar requestBody = JsonConvert.DeserializeAnonymousType(data, d);\n\n\t\t\tDebug.SetDoNotLoadConfigOnNextBoot(requestBody.DoNotLoadConfigOnNextBoot);\n\n\t\t\tvar responseBody = JsonConvert.SerializeObject(d, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.Write(responseBody, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a Data\n\t/// </summary>\n\tpublic class Data\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the DoNotLoadConfigOnNextBoot\n\t\t/// </summary>\n\t\t[JsonProperty(\"doNotLoadConfigOnNextBoot\", NullValueHandling = NullValueHandling.Ignore)]\n\t\tpublic bool DoNotLoadConfigOnNextBoot { get; set; }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetFeedbacksForDeviceRequestHandler.cs",
    "content": "﻿using System.Linq;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a GetFeedbacksForDeviceRequestHandler\n /// </summary>\n\tpublic class GetFeedbacksForDeviceRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic GetFeedbacksForDeviceRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar routeData = context.Request.RouteData;\n\t\t\tif (routeData == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tobject deviceObj;\n\t\t\tif (!routeData.Values.TryGetValue(\"deviceKey\", out deviceObj))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\n\t\t\tvar device = DeviceManager.GetDeviceForKey(deviceObj.ToString()) as IHasFeedback;\n\t\t\tif (device == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n\t\t\t\tcontext.Response.StatusDescription = \"Not Found\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar boolFeedback =\n\t\t\t\tfrom feedback in device.Feedbacks.OfType<BoolFeedback>()\n\t\t\t\twhere !string.IsNullOrEmpty(feedback.Key)\n\t\t\t\tselect new\n\t\t\t\t{\n\t\t\t\t\tFeedbackKey = feedback.Key,\n\t\t\t\t\tValue = feedback.BoolValue\n\t\t\t\t};\n\n\t\t\tvar intFeedback =\n\t\t\t\tfrom feedback in device.Feedbacks.OfType<IntFeedback>()\n\t\t\t\twhere !string.IsNullOrEmpty(feedback.Key)\n\t\t\t\tselect new\n\t\t\t\t{\n\t\t\t\t\tFeedbackKey = feedback.Key,\n\t\t\t\t\tValue = feedback.IntValue\n\t\t\t\t};\n\n\t\t\tvar stringFeedback = \n\t\t\t\tfrom feedback in device.Feedbacks.OfType<StringFeedback>()\n\t\t\t\twhere !string.IsNullOrEmpty(feedback.Key)\n\t\t\t\tselect new\n\t\t\t\t{\n\t\t\t\t\tFeedbackKey = feedback.Key,\n\t\t\t\t\tValue = feedback.StringValue ?? string.Empty\n\t\t\t\t};\n\n\t\t\tvar responseObj = new\n\t\t\t{\n\t\t\t\tBoolValues = boolFeedback,\n\t\t\t\tIntValues = intFeedback,\n\t\t\t\tSerialValues = stringFeedback\n\t\t\t};\n\n\t\t\tvar js = JsonConvert.SerializeObject(responseObj, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = System.Text.Encoding.UTF8;\n\t\t\tcontext.Response.Write(js, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetJoinMapForBridgeKeyRequestHandler.cs",
    "content": "﻿using System.Linq;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\nusing PepperDash.Essentials.Core.Bridges;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a GetJoinMapForBridgeKeyRequestHandler\n /// </summary>\n\tpublic class GetJoinMapForBridgeKeyRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic GetJoinMapForBridgeKeyRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar routeData = context.Request.RouteData;\n\t\t\tif (routeData == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tobject bridgeObj;\t\t\t\n\t\t\tif (!routeData.Values.TryGetValue(\"bridgeKey\", out bridgeObj))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar bridge = DeviceManager.GetDeviceForKey(bridgeObj.ToString()) as EiscApiAdvanced;\n\t\t\tif (bridge == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar joinMap = bridge.JoinMaps.Select(j => EssentialsWebApiHelpers.MapJoinToObject(j)).ToList();\t\t\t\n\t\t\tif (joinMap == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n\t\t\t\tcontext.Response.StatusDescription = \"Not Found\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar js = JsonConvert.SerializeObject(joinMap, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = System.Text.Encoding.UTF8;\n\t\t\tcontext.Response.Write(js, false);\n\t\t\tcontext.Response.End();\n\t\t}\t\t\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetJoinMapForDeviceKeyRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\nusing PepperDash.Essentials.Core.Bridges;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a GetJoinMapForDeviceKeyRequestHandler\n /// </summary>\n\tpublic class GetJoinMapForDeviceKeyRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic GetJoinMapForDeviceKeyRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar routeData = context.Request.RouteData;\n\t\t\tif (routeData == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tobject bridgeObj;\n\t\t\tif (!routeData.Values.TryGetValue(\"bridgeKey\", out bridgeObj))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tobject deviceObj;\n\t\t\tif (!routeData.Values.TryGetValue(\"deviceKey\", out deviceObj))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar bridge = DeviceManager.GetDeviceForKey(bridgeObj.ToString()) as EiscApiAdvanced;\n\t\t\tif (bridge == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n\t\t\t\tcontext.Response.StatusDescription = \"Not Found\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tJoinMapBaseAdvanced deviceJoinMap;\n\t\t\tif (!bridge.JoinMaps.TryGetValue(deviceObj.ToString(), out deviceJoinMap))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 500;\n\t\t\t\tcontext.Response.StatusDescription = \"Internal Server Error\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar joinMap = EssentialsWebApiHelpers.MapJoinToObject(deviceObj.ToString(), deviceJoinMap);\n\t\t\tvar js = JsonConvert.SerializeObject(joinMap, Formatting.Indented, new JsonSerializerSettings\n\t\t\t{\n\t\t\t\tReferenceLoopHandling = ReferenceLoopHandling.Ignore,\n\t\t\t\tNullValueHandling = NullValueHandling.Ignore,\n\t\t\t\tMissingMemberHandling = MissingMemberHandling.Ignore,\n\t\t\t\tDefaultValueHandling = DefaultValueHandling.Ignore,\n\t\t\t\tTypeNameHandling = TypeNameHandling.None\n\t\t\t});\n\t\t\t\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = System.Text.Encoding.UTF8;\n\t\t\tcontext.Response.Write(js, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetRoutesHandler.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n    /// <summary>\n    /// Represents a GetRoutesHandler\n    /// </summary>\n    public class GetRoutesHandler:WebApiBaseRequestHandler\n    {\n        private HttpCwsRouteCollection routeCollection;\n        private string basePath;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"routeCollection\"></param>\n        /// <param name=\"basePath\"></param>\n        public GetRoutesHandler(HttpCwsRouteCollection routeCollection, string basePath) {\n            this.routeCollection = routeCollection;\n            this.basePath = basePath;\n        }\n\n        /// <summary>\n        /// Handles GET method requests\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected override void HandleGet(HttpCwsContext context)\n        {\n            var currentIp = CrestronEthernetHelper.GetEthernetParameter(\n                CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);\n\n            var hostname = CrestronEthernetHelper.GetEthernetParameter(\n                    CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);\n\n            var path = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server\n                ? $\"https://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws{basePath}\"\n                : $\"https://{currentIp}/cws{basePath}\";\n\n            var response = JsonConvert.SerializeObject(new RoutesResponseObject()\n            {\n                Url = path,\n                Routes = routeCollection\n            });\n\n            context.Response.StatusCode = 200;\n            context.Response.ContentType = \"application/json\";\n            context.Response.Headers.Add(\"Content-Type\", \"application/json\");\n            context.Response.Write(response, false);\n            context.Response.End();\n        }\n    }\n\n    /// <summary>\n    /// Represents a RoutesResponseObject\n    /// </summary>\n    public class  RoutesResponseObject \n    {\n        /// <summary>\n        /// Gets or sets the Url\n        /// </summary>\n        [JsonProperty(\"url\")]\n        public string Url { set; get; }\n\n        /// <summary>\n        /// Gets or sets the Routes\n        /// </summary>\n        [JsonProperty(\"routes\")]\n        public HttpCwsRouteCollection Routes { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetRoutingDevicesAndTieLinesHandler.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n  /// <summary>\n  /// Handles HTTP requests to retrieve routing devices and tielines information\n  /// </summary>\n  public class GetRoutingDevicesAndTieLinesHandler : WebApiBaseRequestHandler\n  {\n    public GetRoutingDevicesAndTieLinesHandler() : base(true) { }\n\n    protected override void HandleGet(HttpCwsContext context)\n    {\n      var devices = new List<RoutingDeviceInfo>();\n\n      // Get all devices from DeviceManager\n      foreach (var device in DeviceManager.AllDevices)\n      {\n        var deviceInfo = new RoutingDeviceInfo\n        {\n          Key = device.Key,\n          Name = (device as IKeyName)?.Name ?? device.Key\n        };\n\n        // Check if device implements IRoutingInputs\n        if (device is IRoutingInputs inputDevice)\n        {\n          deviceInfo.HasInputs = true;\n          deviceInfo.InputPorts = inputDevice.InputPorts.Select(p => new PortInfo\n          {\n            Key = p.Key,\n            SignalType = p.Type.ToString(),\n            ConnectionType = p.ConnectionType.ToString(),\n            IsInternal = p.IsInternal\n          }).ToList();\n        }\n\n        // Check if device implements IRoutingOutputs\n        if (device is IRoutingOutputs outputDevice)\n        {\n          deviceInfo.HasOutputs = true;\n          deviceInfo.OutputPorts = outputDevice.OutputPorts.Select(p => new PortInfo\n          {\n            Key = p.Key,\n            SignalType = p.Type.ToString(),\n            ConnectionType = p.ConnectionType.ToString(),\n            IsInternal = p.IsInternal\n          }).ToList();\n        }\n\n        // Check if device implements IRoutingInputsOutputs\n        if (device is IRoutingInputsOutputs)\n        {\n          deviceInfo.HasInputsAndOutputs = true;\n        }\n\n        // Only include devices that have routing capabilities\n        if (deviceInfo.HasInputs || deviceInfo.HasOutputs)\n        {\n          devices.Add(deviceInfo);\n        }\n      }\n\n      // Get all tielines\n      var tielines = TieLineCollection.Default.Select(tl => new TieLineInfo\n      {\n        SourceDeviceKey = tl.SourcePort.ParentDevice.Key,\n        SourcePortKey = tl.SourcePort.Key,\n        DestinationDeviceKey = tl.DestinationPort.ParentDevice.Key,\n        DestinationPortKey = tl.DestinationPort.Key,\n        SignalType = tl.Type.ToString(),\n        IsInternal = tl.IsInternal\n      }).ToList();\n\n      var response = new RoutingSystemInfo\n      {\n        Devices = devices,\n        TieLines = tielines\n      };\n\n      var jsonResponse = JsonConvert.SerializeObject(response, Formatting.Indented);\n\n      context.Response.StatusCode = 200;\n      context.Response.StatusDescription = \"OK\";\n      context.Response.ContentType = \"application/json\";\n      context.Response.ContentEncoding = Encoding.UTF8;\n      context.Response.Write(jsonResponse, false);\n      context.Response.End();\n    }\n  }\n\n  /// <summary>\n  /// Represents the complete routing system information including devices and tielines\n  /// </summary>\n  public class RoutingSystemInfo\n  {\n    [JsonProperty(\"devices\")]\n    public List<RoutingDeviceInfo> Devices { get; set; }\n\n    [JsonProperty(\"tieLines\")]\n    public List<TieLineInfo> TieLines { get; set; }\n  }\n\n  /// <summary>\n  /// Represents a routing device with its ports information\n  /// </summary>\n  public class RoutingDeviceInfo\n  {\n    [JsonProperty(\"key\")]\n    public string Key { get; set; }\n\n    [JsonProperty(\"name\")]\n    public string Name { get; set; }\n\n    [JsonProperty(\"hasInputs\")]\n    public bool HasInputs { get; set; }\n\n    [JsonProperty(\"hasOutputs\")]\n    public bool HasOutputs { get; set; }\n\n    [JsonProperty(\"hasInputsAndOutputs\")]\n    public bool HasInputsAndOutputs { get; set; }\n\n    [JsonProperty(\"inputPorts\", NullValueHandling = NullValueHandling.Ignore)]\n    public List<PortInfo> InputPorts { get; set; }\n\n    [JsonProperty(\"outputPorts\", NullValueHandling = NullValueHandling.Ignore)]\n    public List<PortInfo> OutputPorts { get; set; }\n  }\n\n  /// <summary>\n  /// Represents a routing port with its properties\n  /// </summary>\n  public class PortInfo\n  {\n    [JsonProperty(\"key\")]\n    public string Key { get; set; }\n\n    [JsonProperty(\"signalType\")]\n    public string SignalType { get; set; }\n\n    [JsonProperty(\"connectionType\")]\n    public string ConnectionType { get; set; }\n\n    [JsonProperty(\"isInternal\")]\n    public bool IsInternal { get; set; }\n  }\n\n  /// <summary>\n  /// Represents a tieline connection between two ports\n  /// </summary>\n  public class TieLineInfo\n  {\n    [JsonProperty(\"sourceDeviceKey\")]\n    public string SourceDeviceKey { get; set; }\n\n    [JsonProperty(\"sourcePortKey\")]\n    public string SourcePortKey { get; set; }\n\n    [JsonProperty(\"destinationDeviceKey\")]\n    public string DestinationDeviceKey { get; set; }\n\n    [JsonProperty(\"destinationPortKey\")]\n    public string DestinationPortKey { get; set; }\n\n    [JsonProperty(\"signalType\")]\n    public string SignalType { get; set; }\n\n    [JsonProperty(\"isInternal\")]\n    public bool IsInternal { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetRoutingPortsHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n    /// <summary>\n    /// Represents a GetRoutingPortsHandler\n    /// </summary>\n    public class GetRoutingPortsHandler : WebApiBaseRequestHandler\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public GetRoutingPortsHandler() : base(true) { }\n\n        /// <summary>\n        /// Handles the GET request\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected override void HandleGet(HttpCwsContext context)\n        {\n            var routeData = context.Request.RouteData;\n\n            if (routeData == null)\n            {\n                context.Response.StatusCode = 400;\n                context.Response.StatusDescription = \"Bad Request\";\n                context.Response.End();\n                return;\n            }\n\n            if(!routeData.Values.TryGetValue(\"deviceKey\", out var deviceKey))\n            {\n                context.Response.StatusCode = 400;\n                context.Response.StatusDescription = \"Bad Request\";\n                context.Response.End();\n                return;\n            }\n\n            var device = DeviceManager.GetDeviceForKey(deviceKey.ToString());\n\n            if (device == null)\n            {\n                context.Response.StatusCode = 404;\n                context.Response.StatusDescription = \"Device Not Found\";\n                context.Response.End();\n                return;\n            }\n\n            var inputPorts = (device as IRoutingInputs)?.InputPorts;\n            var outputPorts = (device as IRoutingOutputs)?.OutputPorts;\n            \n            var response = JsonConvert.SerializeObject( new ReturnValue\n            {\n                InputPorts = inputPorts?.Select(p => p.Key).ToList(),\n                OutputPorts = outputPorts?.Select(p => p.Key).ToList()\n            });\n\n            context.Response.StatusCode = 200;\n            context.Response.StatusDescription = \"OK\";\n            context.Response.ContentType = \"application/json\";\n            context.Response.ContentEncoding = Encoding.UTF8;\n            context.Response.Write(response, false);\n            context.Response.End();\n\n        }\n    }\n\n    internal class ReturnValue \n    {\n        /// <summary>\n        /// Gets or sets the InputPorts\n        /// </summary>\n        [JsonProperty(\"inputPorts\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<string> InputPorts { get; set; }\n\n        /// <summary>\n        /// Gets or sets the OutputPorts\n        /// </summary>\n        [JsonProperty(\"outputPorts\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<string> OutputPorts { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTieLinesRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\nusing System.Linq;\nusing System.Text;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n    /// <summary>\n    /// Represents a GetTieLinesRequestHandler\n    /// </summary>\n    public class GetTieLinesRequestHandler : WebApiBaseRequestHandler\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public GetTieLinesRequestHandler() : base(true) { }\n\n        /// <summary>\n        /// Handles the GET request\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected override void HandleGet(HttpCwsContext context)\n        {\n            var tieLineString = JsonConvert.SerializeObject(TieLineCollection.Default.Select((tl) => new\n            {\n                sourceKey = tl.SourcePort.ParentDevice.Key,\n                sourcePort = tl.SourcePort.Key,\n                destinationKey = tl.DestinationPort.ParentDevice.Key,\n                destinationPort = tl.DestinationPort.Key,\n                type = tl.Type.ToString(),\n            }));\n\n            context.Response.StatusCode = 200;\n            context.Response.StatusDescription = \"OK\";\n            context.Response.ContentType = \"application/json\";\n            context.Response.ContentEncoding = Encoding.UTF8;\n            context.Response.Write(tieLineString, false);\n            context.Response.End();\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesByFilterRequestHandler.cs",
    "content": "﻿using System.Linq;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a GetTypesByFilterRequestHandler\n /// </summary>\n\tpublic class GetTypesByFilterRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic GetTypesByFilterRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar routeData = context.Request.RouteData;\n\t\t\tif (routeData == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tobject filterObj;\n\t\t\tif (!routeData.Values.TryGetValue(\"filter\", out filterObj))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar deviceFactory = DeviceFactory.GetDeviceFactoryDictionary(filterObj.ToString());\n\t\t\tif (deviceFactory == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n\t\t\t\tcontext.Response.StatusDescription = \"Not Found\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar deviceTypes = deviceFactory.Select(t => EssentialsWebApiHelpers.MapDeviceTypeToObject(t)).ToList();\n\t\t\tvar js = JsonConvert.SerializeObject(deviceTypes, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = System.Text.Encoding.UTF8;\n\t\t\tcontext.Response.Write(js, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesRequestHandler.cs",
    "content": "﻿using System.Linq;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a GetTypesRequestHandler\n /// </summary>\n\tpublic class GetTypesRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic GetTypesRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar routeData = context.Request.RouteData;\n\t\t\tif (routeData == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar deviceFactory = DeviceFactory.GetDeviceFactoryDictionary(null);\n\t\t\tif (deviceFactory == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n\t\t\t\tcontext.Response.StatusDescription = \"Not Found\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar deviceTypes = deviceFactory.Select(t => EssentialsWebApiHelpers.MapDeviceTypeToObject(t)).ToList();\n\t\t\tvar js = JsonConvert.SerializeObject(deviceTypes, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = System.Text.Encoding.UTF8;\n\t\t\tcontext.Response.Write(js, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/LoadConfigRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a LoadConfigRequestHandler\n /// </summary>\n\tpublic class LoadConfigRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\t\t\n\t\tpublic LoadConfigRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePost(HttpCwsContext context)\n\t\t{\n\t\t\tvar message = \"\";\n\t\t\tvar cs = Global.ControlSystem as ILoadConfig;\n\t\t\tif(cs != null)\n                cs.GoWithLoad();\n            \n            context.Response.StatusCode = 200;\n            context.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.Write(message, false);\n            context.Response.End();\n        }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/LoginRequestHandler.cs",
    "content": "\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp.CrestronAuthentication;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n    /// <summary>\n    /// Represents a LoginRequestHandler\n    /// </summary>\n    public class LoginRequestHandler : WebApiBaseRequestHandler\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <remarks>\n        /// base(true) enables CORS support by default\n        /// </remarks>\n        public LoginRequestHandler()\n            : base(true)\n        {\n        }\n\n        /// <summary>\n        /// Handles POST method requests for user login and token generation\n        /// </summary>\n        /// <param name=\"context\">The HTTP context for the request.</param>\n        protected override void HandlePost(HttpCwsContext context)\n        {\n            try\n            {\n                if (context.Request.ContentLength < 0)\n                {\n                    context.Response.StatusCode = 400;\n                    context.Response.StatusDescription = \"Bad Request\";\n                    context.Response.End();\n\n                    return;\n                }\n\n                var data = context.Request.GetRequestBody();\n                if (string.IsNullOrEmpty(data))\n                {\n                    context.Response.StatusCode = 400;\n                    context.Response.StatusDescription = \"Bad Request\";\n                    context.Response.End();\n\n                    return;\n                }\n\n                var loginRequest = JsonConvert.DeserializeObject<LoginRequest>(data);\n\n                if (loginRequest == null || string.IsNullOrEmpty(loginRequest.Username) || string.IsNullOrEmpty(loginRequest.Password))\n                {\n                    context.Response.StatusCode = 400;\n                    context.Response.StatusDescription = \"Bad Request\";\n                    context.Response.End();\n\n                    return;\n                }\n\n                Authentication.UserToken token;\n\n                try\n                {\n                    token = Authentication.GetAuthenticationToken(loginRequest.Username, loginRequest.Password);\n                }\n                catch (ArgumentException)\n                {\n                    context.Response.StatusCode = 401;\n                    context.Response.StatusDescription = \"Bad Request\";\n                    context.Response.ContentType = \"application/json\";\n                    context.Response.ContentEncoding = System.Text.Encoding.UTF8;\n                    context.Response.Write(JsonConvert.SerializeObject(new { Error = \"Unauthorized\" }, Formatting.Indented), false);\n                    context.Response.End();\n                    return;\n                }\n\n                if (!token.Valid)\n                {\n                    context.Response.StatusCode = 401;\n                    context.Response.StatusDescription = \"Unauthorized\";\n                    context.Response.End();\n\n                    return;\n                }\n\n                context.Response.StatusCode = 200;\n                context.Response.StatusDescription = \"OK\";\n                context.Response.ContentType = \"application/json\";\n                context.Response.ContentEncoding = System.Text.Encoding.UTF8;\n                context.Response.Write(JsonConvert.SerializeObject(\n                     new\n                     {\n                         Token = new LoginResponse\n                         {\n                             UserName = token.UserName,\n                             Access = token.Access,\n                             State = token.State,\n                             Groups = token.Groups,\n                             ADConnect = token.ADConnect,\n                             Valid = token.Valid\n                         }\n                     }, Formatting.Indented), false);\n                context.Response.End();\n            }\n            catch (System.Exception ex)\n            {\n                context.Response.StatusCode = 500;\n                context.Response.StatusDescription = \"Internal Server Error\";\n                context.Response.ContentType = \"application/json\";\n                context.Response.ContentEncoding = System.Text.Encoding.UTF8;\n                context.Response.Write(JsonConvert.SerializeObject(new { Error = ex.Message }, Formatting.Indented), false);\n                context.Response.End();\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a LoginRequest\n    /// </summary>\n    public class LoginRequest\n    {\n        /// <summary>\n        /// Gets or sets the username.\n        /// </summary>\n        public string Username { get; set; }\n\n        /// <summary>\n        /// Gets or sets the password.\n        /// </summary>\n        public string Password { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a LoginResponse\n    /// </summary>\n    internal class LoginResponse\n    {\n        /// <summary>\n        /// Gets or sets the username.\n        /// </summary>\n        public string UserName { get; set; }\n\n        /// <summary>\n        /// Gets or sets the access level.\n        /// </summary>\n        public Authentication.UserAuthenticationLevelEnum Access { get; set; }\n\n        /// <summary>\n        /// Gets or sets the token authenticated state.\n        /// </summary>\n        public Authentication.eTokenAuthenticatedState State { get; set; }\n\n        /// <summary>\n        /// Gets or sets the list of groups.\n        /// </summary>\n        public List<string> Groups { get; set; }\n\n        /// <summary>\n        /// Gets or sets the active directory connection flag.\n        /// </summary>\n        public int ADConnect { get; set; }\n\n        /// <summary>\n        /// Gets or sets the valid flag indicating whether the token is valid.\n        /// </summary>\n        public bool Valid { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/ReportVersionsRequestHandler.cs",
    "content": "﻿using System.Linq;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a ReportVersionsRequestHandler\n /// </summary>\n\tpublic class ReportVersionsRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic ReportVersionsRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar loadAssemblies = PluginLoader.LoadedAssemblies;\n\t\t\tif (loadAssemblies == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 500;\n\t\t\t\tcontext.Response.StatusDescription = \"Internal Server Error\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar assemblies = loadAssemblies.Select(a => EssentialsWebApiHelpers.MapToAssemblyObject(a)).ToList();\n\n\t\t\tvar js = JsonConvert.SerializeObject(assemblies, Formatting.Indented);\n\t\t\t\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = System.Text.Encoding.UTF8;\n\t\t\tcontext.Response.Write(js, false);\t\t\t\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/RestartProgramRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a RestartProgramRequestHandler\n /// </summary>\n\tpublic class RestartProgramRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\t\t\n\t\tpublic RestartProgramRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePost(HttpCwsContext context)\n\t\t{\n\t\t\tvar message = \"\";\n\t\t\tif(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)\n\t\t\t\tCrestronConsole.SendControlSystemCommand($\"progres -p:{InitialParametersClass.ApplicationNumber}\", ref message);\n\t\t\t\n            context.Response.StatusCode = 200;\n            context.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.Write(message, false);\n            context.Response.End();\n        }\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/ServeDebugAppRequestHandler.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharp.WebScripting;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n    /// <summary>\n    /// Serves the React debug app from the processor's /HTML/debug folder.\n    /// The root route (debug) and all sub-paths (debug/{*filePath}) are handled here.\n    /// Text assets are sent as UTF-8 strings; binary assets are written to the response\n    /// OutputStream.  Any sub-path that does not match a real file falls back to\n    /// index.html so that client-side (React Router) routing continues to work.\n    /// </summary>\n    public class ServeDebugAppRequestHandler : WebApiBaseRequestHandler\n    {\n        private static readonly Dictionary<string, string> MimeTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)\n    {\n        { \".html\", \"text/html; charset=utf-8\" },\n        { \".htm\",  \"text/html; charset=utf-8\" },\n        { \".js\",   \"application/javascript\" },\n        { \".mjs\",  \"application/javascript\" },\n        { \".jsx\",  \"application/javascript\" },\n        { \".css\",  \"text/css\" },\n        { \".json\", \"application/json\" },\n        { \".map\",  \"application/json\" },\n        { \".svg\",  \"image/svg+xml\" },\n        { \".ico\",  \"image/x-icon\" },\n        { \".png\",  \"image/png\" },\n        { \".jpg\",  \"image/jpeg\" },\n        { \".jpeg\", \"image/jpeg\" },\n        { \".gif\",  \"image/gif\" },\n        { \".woff\", \"font/woff\" },\n        { \".woff2\",\"font/woff2\" },\n        { \".ttf\",  \"font/ttf\" },\n        { \".eot\",  \"application/vnd.ms-fontobject\" },\n    };\n\n        private static readonly HashSet<string> TextExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)\n    {\n        \".html\", \".htm\", \".js\", \".mjs\", \".jsx\", \".css\", \".json\", \".map\", \".svg\", \".txt\", \".xml\"\n    };\n\n        /// <summary>\n        /// Constructor. CORS is enabled so browser dev-tools requests succeed.\n        /// </summary>\n        public ServeDebugAppRequestHandler() : base(true) { }\n\n        /// <summary>\n        /// Handles GET requests for the debug app and its static assets.\n        /// </summary>\n        protected override void HandleGet(HttpCwsContext context)\n        {\n            // When acting as the server-level fallback handler, only handle\n            // requests that are actually for the /debug path; defer everything\n            // else to the base class (which returns 501 Not Implemented).\n            var rawUrl = context.Request.RawUrl ?? string.Empty;\n            if (rawUrl.IndexOf(\"/debug\", StringComparison.OrdinalIgnoreCase) < 0)\n            {\n                base.HandleGet(context);\n                return;\n            }\n\n            try\n            {\n                var htmlDebugPath = GetHtmlDebugPath();\n                if (htmlDebugPath == null)\n                {\n                    SendResponse(context, 500, \"Internal Server Error\");\n                    return;\n                }\n\n                var requestedPath = GetRequestedFilePath(context);\n\n                // Paths with no file extension are SPA client-side routes — serve index.html\n                string candidate;\n                if (string.IsNullOrEmpty(requestedPath) || !System.IO.Path.HasExtension(requestedPath))\n                {\n                    candidate = System.IO.Path.Combine(htmlDebugPath, \"index.html\");\n                }\n                else\n                {\n                    var relativePart = requestedPath.Replace('/', System.IO.Path.DirectorySeparatorChar);\n                    candidate = System.IO.Path.Combine(htmlDebugPath, relativePart);\n                }\n\n                // Resolve to an absolute path and guard against path-traversal attacks\n                var resolvedCandidate = System.IO.Path.GetFullPath(candidate);\n                var resolvedBase = System.IO.Path.GetFullPath(htmlDebugPath)\n                                   + System.IO.Path.DirectorySeparatorChar;\n\n                if (!resolvedCandidate.StartsWith(resolvedBase, StringComparison.OrdinalIgnoreCase))\n                {\n                    SendResponse(context, 403, \"Forbidden\");\n                    return;\n                }\n\n                // Missing static asset → fall back to index.html (SPA deep-link support)\n                if (!File.Exists(resolvedCandidate) && System.IO.Path.HasExtension(requestedPath ?? string.Empty))\n                {\n                    resolvedCandidate = System.IO.Path.Combine(htmlDebugPath, \"index.html\");\n                    Debug.LogMessage(LogEventLevel.Debug,\n                        \"ServeDebugAppRequestHandler: '{requestedPath:l}' not found, falling back to index.html\",\n                        requestedPath);\n                }\n\n                if (!File.Exists(resolvedCandidate))\n                {\n                    SendResponse(context, 404, \"Not Found\");\n                    return;\n                }\n\n                var ext = System.IO.Path.GetExtension(resolvedCandidate);\n                var contentType = MimeTypes.TryGetValue(ext, out var mime) ? mime : \"application/octet-stream\";\n\n                context.Response.StatusCode = 200;\n                context.Response.StatusDescription = \"OK\";\n                context.Response.ContentType = contentType;\n\n                if (TextExtensions.Contains(ext))\n                {\n                    string content;\n                    using (var reader = new StreamReader(resolvedCandidate, Encoding.UTF8))\n                        content = reader.ReadToEnd();\n\n                    context.Response.ContentEncoding = Encoding.UTF8;\n                    context.Response.Write(content, false);\n                }\n                else\n                {\n                    var bytes = System.IO.File.ReadAllBytes(resolvedCandidate);\n                    context.Response.OutputStream.Write(bytes, 0, bytes.Length);\n                }\n\n                context.Response.End();\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(LogEventLevel.Error, ex,\n                    \"ServeDebugAppRequestHandler: Unhandled error serving '{rawUrl:l}'\",\n                    context.Request.RawUrl);\n                try { SendResponse(context, 500, \"Internal Server Error\"); }\n                catch { /* best-effort */ }\n            }\n        }\n\n        /// <summary>\n        /// Resolves the absolute path of the /HTML/debug folder on the processor.\n        /// </summary>\n        /// <remarks>\n        /// <c>Global.FilePathPrefix</c> is always <c>{root}/user/programX/</c> (or\n        /// equivalent), so walking up two parents gives the processor root that\n        /// contains the <c>html</c> folder.  This mirrors the two-hop strategy used\n        /// by <c>AssetLoader.ExtractDevToolsZip</c> so that serving and extraction\n        /// always resolve to the same directory.\n        /// </remarks>\n        private static string GetHtmlDebugPath()\n        {\n            try\n            {\n                var separators = new[] { System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar };\n                var programDir = new DirectoryInfo(Global.FilePathPrefix.TrimEnd(separators));\n\n                // Walk up two levels: {root}/user/programX/ → {root}/user/ → {root}\n                // This matches the path calculation used in AssetLoader.ExtractDevToolsZip.\n                var userOrNvramDir = programDir.Parent;\n                var rootDir = userOrNvramDir?.Parent;\n\n                if (rootDir == null)\n                {\n                    Debug.LogMessage(LogEventLevel.Error,\n                        \"ServeDebugAppRequestHandler: Cannot resolve HTML root from FilePathPrefix '{prefix:l}'\",\n                        Global.FilePathPrefix);\n                    return null;\n                }\n\n                return System.IO.Path.Combine(rootDir.FullName, \"html\", \"debug\");\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(LogEventLevel.Error, ex,\n                    \"ServeDebugAppRequestHandler: Error resolving HTML debug path\");\n                return null;\n            }\n        }\n\n        /// <summary>\n        /// Extracts the file sub-path from the request by parsing <c>RawUrl</c>.\n        /// Returns an empty string when the URL ends at <c>/debug</c> (root hit).\n        /// </summary>\n        private static string GetRequestedFilePath(HttpCwsContext context)\n        {\n            var rawUrl = context.Request.RawUrl ?? string.Empty;\n\n            // Locate the /debug segment in the URL\n            const string debugToken = \"/debug\";\n            var idx = rawUrl.IndexOf(debugToken, StringComparison.OrdinalIgnoreCase);\n            if (idx < 0)\n                return string.Empty;\n\n            var afterDebug = rawUrl.Substring(idx + debugToken.Length);\n\n            // Strip query string\n            var qIdx = afterDebug.IndexOf('?');\n            if (qIdx >= 0)\n                afterDebug = afterDebug.Substring(0, qIdx);\n\n            // Strip leading slash to get a relative file path\n            return afterDebug.TrimStart('/');\n        }\n\n        private static void SendResponse(HttpCwsContext context, int statusCode, string statusDescription)\n        {\n            context.Response.StatusCode = statusCode;\n            context.Response.StatusDescription = statusDescription;\n            context.Response.End();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/SetDeviceStreamDebugRequestHandler.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n\t/// <summary>\n\t/// Represents a SetDeviceStreamDebugRequestHandler\n\t/// </summary>\n\tpublic class SetDeviceStreamDebugRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Handles CONNECT method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleConnect(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles DELETE method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleDelete(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles HEAD method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleHead(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles OPTIONS method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleOptions(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles PATCH method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePatch(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles POST method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePost(HttpCwsContext context)\n\t\t{\n\t\t\tif (context.Request.ContentLength < 0)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar data = context.Request.GetRequestBody();\n\t\t\tif (data == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 500;\n\t\t\t\tcontext.Response.StatusDescription = \"Internal Server Error\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar config = new SetDeviceStreamDebugConfig();\n\t\t\tvar body = JsonConvert.DeserializeAnonymousType(data, config);\n\t\t\tif (body == null)\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 500;\n\t\t\t\tcontext.Response.StatusDescription = \"Internal Server Error\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (string.IsNullOrEmpty(body.DeviceKey) || string.IsNullOrEmpty(body.Setting))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 400;\n\t\t\t\tcontext.Response.StatusDescription = \"Bad Request\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!(DeviceManager.GetDeviceForKey(body.DeviceKey) is IStreamDebugging device))\n\t\t\t{\n\t\t\t\tcontext.Response.StatusCode = 404;\n\t\t\t\tcontext.Response.StatusDescription = \"Not Found\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\teStreamDebuggingSetting debugSetting;\n\t\t\ttry\n\t\t\t{\n\t\t\t\tdebugSetting = (eStreamDebuggingSetting)Enum.Parse(typeof(eStreamDebuggingSetting), body.Setting, true);\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(ex, \"Exception handling set debug request\");\n\t\t\t\tcontext.Response.StatusCode = 500;\n\t\t\t\tcontext.Response.StatusDescription = \"Internal Server Error\";\n\t\t\t\tcontext.Response.End();\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar mins = Convert.ToUInt32(body.Timeout);\n\t\t\t\tif (mins > 0)\n\t\t\t\t{\n\t\t\t\t\tdevice.StreamDebugging.SetDebuggingWithSpecificTimeout(debugSetting, mins);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdevice.StreamDebugging.SetDebuggingWithDefaultTimeout(debugSetting);\n\t\t\t\t}\n\n\t\t\t\tcontext.Response.StatusCode = 200;\n\t\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\t\tcontext.Response.End();\n\t\t\t}\n\t\t\tcatch (Exception ex)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(ex, \"Exception handling set debug request\");\n\t\t\t\tcontext.Response.StatusCode = 500;\n\t\t\t\tcontext.Response.StatusDescription = \"Internal Server Error\";\n\t\t\t\tcontext.Response.End();\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles PUT method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandlePut(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles TRACE method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleTrace(HttpCwsContext context)\n\t\t{\n\t\t\tcontext.Response.StatusCode = 501;\n\t\t\tcontext.Response.StatusDescription = \"Not Implemented\";\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n\n\n\t/// <summary>\n\t/// Configuration class for SetDeviceStreamDebugRequestHandler\n\t/// </summary>\n\tpublic class SetDeviceStreamDebugConfig\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the DeviceKey\n\t\t/// </summary>\n\t\t[JsonProperty(\"deviceKey\", NullValueHandling = NullValueHandling.Include)]\n\t\tpublic string DeviceKey { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Setting\n\t\t/// </summary>\n\t\t[JsonProperty(\"setting\", NullValueHandling = NullValueHandling.Include)]\n\t\tpublic string Setting { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Timeout\n\t\t/// </summary>\n\t\t[JsonProperty(\"timeout\")]\n\t\tpublic int Timeout { get; set; }\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\tpublic SetDeviceStreamDebugConfig()\n\t\t{\n\t\t\tDeviceKey = null;\n\t\t\tSetting = null;\n\t\t\tTimeout = 15;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/Web/RequestHandlers/ShowConfigRequestHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\nusing PepperDash.Essentials.Core.Config;\n\nnamespace PepperDash.Essentials.Core.Web.RequestHandlers\n{\n /// <summary>\n /// Represents a ShowConfigRequestHandler\n /// </summary>\n\tpublic class ShowConfigRequestHandler : WebApiBaseRequestHandler\n\t{\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// base(true) enables CORS support by default\n\t\t/// </remarks>\n\t\tpublic ShowConfigRequestHandler()\n\t\t\t: base(true)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles GET method requests\n\t\t/// </summary>\n\t\t/// <param name=\"context\"></param>\n\t\tprotected override void HandleGet(HttpCwsContext context)\n\t\t{\n\t\t\tvar config = JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented);\n\n\t\t\tcontext.Response.StatusCode = 200;\n\t\t\tcontext.Response.StatusDescription = \"OK\";\n\t\t\tcontext.Response.ContentType = \"application/json\";\n\t\t\tcontext.Response.ContentEncoding = System.Text.Encoding.UTF8;\n\t\t\tcontext.Response.Write(config, false);\n\t\t\tcontext.Response.End();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Core/app.config",
    "content": "﻿<?xml version=\"1.0\"?>\n<configuration>\n\t<runtime>\n\t\t<assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n\t\t\t<dependentAssembly>\n\t\t\t\t<assemblyIdentity name=\"SimplSharpPro\" publicKeyToken=\"1099C178B3B54C3B\" culture=\"neutral\"/>\n\t\t\t\t<bindingRedirect oldVersion=\"0.0.0.0-1.5.2.1\" newVersion=\"1.5.2.1\"/>\n\t\t\t</dependentAssembly>\n\t\t</assemblyBinding>\n\t</runtime>\n</configuration>\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Audio/GenericAudioOut.cs",
    "content": "﻿using System.Collections.Generic;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\n\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n\t/// <summary>\n\t/// Represents and audio endpoint\n\t/// </summary>\n\tpublic class GenericAudioOut : EssentialsDevice, IRoutingSink\n\t{\n\t\t/// <summary>\n\t\t/// Gets the current input port\n\t\t/// </summary>\n\t\tpublic RoutingInputPort CurrentInputPort => AnyAudioIn;\n\n\t\t/// <summary>\n\t\t/// Event fired when the current source changes\n\t\t/// </summary>\n\t\tpublic event SourceInfoChangeHandler CurrentSourceChange;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the current source info key\n\t\t/// </summary>\n\t\tpublic string CurrentSourceInfoKey { get; set; }\n\t\t/// <summary>\n\t\t/// Gets or sets the current source info\n\t\t/// </summary>\n\t\tpublic SourceListItem CurrentSourceInfo\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn _CurrentSourceInfo;\n\t\t\t}\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (value == _CurrentSourceInfo) return;\n\n\t\t\t\tvar handler = CurrentSourceChange;\n\n\t\t\t\tif (handler != null)\n\t\t\t\t\thandler(_CurrentSourceInfo, ChangeType.WillChange);\n\n\t\t\t\t_CurrentSourceInfo = value;\n\n\t\t\t\tif (handler != null)\n\t\t\t\t\thandler(_CurrentSourceInfo, ChangeType.DidChange);\n\t\t\t}\n\t\t}\n\t\tSourceListItem _CurrentSourceInfo;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the AnyAudioIn\n\t\t/// </summary>\n\t\tpublic RoutingInputPort AnyAudioIn { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Constructor for GenericAudioOut\n\t\t/// </summary>\n\t\t/// <param name=\"key\">Device key</param>\n\t\t/// <param name=\"name\">Device name</param>\n\t\tpublic GenericAudioOut(string key, string name)\n\t\t\t: base(key, name)\n\t\t{\n\t\t\tAnyAudioIn = new RoutingInputPort(RoutingPortNames.AnyAudioIn, eRoutingSignalType.Audio,\n\t\t\t\teRoutingPortConnectionType.LineAudio, null, this);\n\t\t}\n\n\t\t#region IRoutingInputs Members\n\n\t\t/// <summary>\n\t\t/// Gets the collection of input ports\n\t\t/// </summary>\n\t\tpublic RoutingPortCollection<RoutingInputPort> InputPorts\n\t\t{\n\t\t\tget { return new RoutingPortCollection<RoutingInputPort> { AnyAudioIn }; }\n\t\t}\n\n\t\t#endregion\n\t}\n\n\n\t/// <summary>\n\t/// Represents a GenericAudioOutWithVolume\n\t/// </summary>\n\tpublic class GenericAudioOutWithVolume : GenericAudioOut, IHasVolumeDevice\n\t{\n\t\t/// <summary>\n\t\t/// Gets the volume device key\n\t\t/// </summary>\n\t\tpublic string VolumeDeviceKey { get; private set; }\n\t\t/// <summary>\n\t\t/// Gets the volume zone\n\t\t/// </summary>\n\t\tpublic uint VolumeZone { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets the volume device\n\t\t/// </summary>\n\t\tpublic IBasicVolumeControls VolumeDevice\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\tvar dev = DeviceManager.GetDeviceForKey(VolumeDeviceKey);\n\t\t\t\tif (dev is IAudioZones)\n\t\t\t\t\treturn (dev as IAudioZones).Zone[VolumeZone];\n\t\t\t\telse return dev as IBasicVolumeControls;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor - adds the name to the attached audio device, if appropriate.\n\t\t/// </summary>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"name\"></param>\n\t\t/// <param name=\"audioDevice\"></param>\n\t\t/// <param name=\"zone\"></param>\n\t\tpublic GenericAudioOutWithVolume(string key, string name, string audioDevice, uint zone)\n\t\t\t: base(key, name)\n\t\t{\n\t\t\tVolumeDeviceKey = audioDevice;\n\t\t\tVolumeZone = zone;\n\t\t}\n\n\t}\n\n\t/// <summary>\n\t/// Factory for creating GenericAudioOutWithVolume devices\n\t/// </summary>\n\tpublic class GenericAudioOutWithVolumeFactory : EssentialsDeviceFactory<GenericAudioOutWithVolume>\n\t{\n\t\t/// <summary>\n\t\t/// Constructor for GenericAudioOutWithVolumeFactory\n\t\t/// </summary>\n\t\tpublic GenericAudioOutWithVolumeFactory()\n\t\t{\n\t\t\tTypeNames = new List<string>() { \"genericaudiooutwithvolume\" };\n\t\t}\n\n\t\t/// <summary>\n\t\t/// BuildDevice method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override EssentialsDevice BuildDevice(DeviceConfig dc)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new GenericAudioOutWithVolumeFactory Device\");\n\t\t\tvar zone = dc.Properties.Value<uint>(\"zone\");\n\t\t\treturn new GenericAudioOutWithVolume(dc.Key, dc.Name,\n\t\t\t\t\tdc.Properties.Value<string>(\"volumeDeviceKey\"), zone);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/AudioCodec/AudioCodecBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Devices.Common.Codec;\n\nnamespace PepperDash.Essentials.Devices.Common.AudioCodec\n{\n    /// <summary>\n    /// Abstract base class for audio codec devices\n    /// </summary>\n    public abstract class AudioCodecBase : EssentialsDevice, IHasDialer, IUsageTracking, IAudioCodecInfo\n    {\n\n        /// <summary>\n        /// Event fired when call status changes\n        /// </summary>\n        public event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;\n\n        /// <summary>\n        /// Gets or sets the CodecInfo\n        /// </summary>\n        public AudioCodecInfo CodecInfo { get; protected set; }\n\n        #region IUsageTracking Members\n\n        /// <summary>\n        /// Gets or sets the UsageTracker\n        /// </summary>\n        public UsageTracking UsageTracker { get; set; }\n\n        #endregion\n\n        /// <summary>\n        /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected\n        /// </summary>\n        public bool IsInCall\n        {\n            get\n            {\n                bool value;\n\n                if (ActiveCalls != null)\n                    value = ActiveCalls.Any(c => c.IsActiveCall);\n                else\n                    value = false;\n                return value;\n            }\n        }\n\n        // In most cases only a single call can be active\n        /// <summary>\n        /// Gets or sets the ActiveCalls\n        /// </summary>\n        public List<CodecActiveCallItem> ActiveCalls { get; set; }\n\n        /// <summary>\n        /// Constructor for AudioCodecBase\n        /// </summary>\n        /// <param name=\"key\">Device key</param>\n        /// <param name=\"name\">Device name</param>\n        public AudioCodecBase(string key, string name)\n            : base(key, name)\n        {\n            ActiveCalls = new List<CodecActiveCallItem>();\n        }\n\n        /// <summary>\n        /// Helper method to fire CallStatusChange event with old and new status\n        /// </summary>\n        protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call)\n        {\n            call.Status = newStatus;\n\n            OnCallStatusChange(call);\n\n        }\n\n        /// <summary>\n        /// Handles call status change events\n        /// </summary>\n        /// <param name=\"item\">The call item that changed status</param>\n        protected void OnCallStatusChange(CodecActiveCallItem item)\n        {\n            var handler = CallStatusChange;\n            if (handler != null)\n                handler(this, new CodecCallStatusItemChangeEventArgs(item));\n\n            if (UsageTracker != null)\n            {\n                if (IsInCall && !UsageTracker.UsageTrackingStarted)\n                    UsageTracker.StartDeviceUsage();\n                else if (UsageTracker.UsageTrackingStarted && !IsInCall)\n                    UsageTracker.EndDeviceUsage();\n            }\n        }\n\n        #region IHasDialer Members\n\n        /// <inheritdoc />\n        public abstract void Dial(string number);\n\n        /// <inheritdoc />\n        public abstract void EndCall(CodecActiveCallItem activeCall);\n\n        /// <inheritdoc />\n        public abstract void EndAllCalls();\n\n        /// <inheritdoc />\n        public abstract void AcceptCall(CodecActiveCallItem item);\n\n        /// <inheritdoc />\n        public abstract void RejectCall(CodecActiveCallItem item);\n\n        /// <inheritdoc />\n        public abstract void SendDtmf(string digit);\n\n        #endregion\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/AudioCodec/Interfaces/IAudioCodecInfo.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.AudioCodec\n{\n    /// <summary>\n    /// Implements a common set of data about a codec\n    /// </summary>\n    public interface IAudioCodecInfo\n    {\n        /// <summary>\n        /// Gets the codec information\n        /// </summary>\n        AudioCodecInfo CodecInfo { get; }\n    }\n\n    /// <summary>\n    /// Stores general information about a codec\n    /// </summary>\n    public abstract class AudioCodecInfo\n    {\n        /// <summary>\n        /// Gets or sets the phone number\n        /// </summary>\n        public abstract string PhoneNumber { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/AudioCodec/Interfaces/IHasAudioCodec.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.AudioCodec\n{\n    /// <summary>\n    /// For rooms that have audio codec\n    /// </summary>\n    public interface IHasAudioCodec : IHasInCallFeedback\n    {\n        /// <summary>\n        /// Gets the audio codec device\n        /// </summary>\n        AudioCodecBase AudioCodec { get; }\n\n        //List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem> ActiveCalls { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/AudioCodec/MockAC/MockAC.cs",
    "content": "﻿using System.Collections.Generic;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Devices.Common.Codec;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.AudioCodec\n{\n    /// <summary>\n    /// Represents a MockAC\n    /// </summary>\n    public class MockAC : AudioCodecBase\n    {\n        /// <summary>\n        /// Constructor for MockAC\n        /// </summary>\n        /// <param name=\"key\">Device key</param>\n        /// <param name=\"name\">Device name</param>\n        /// <param name=\"props\">MockAC properties configuration</param>\n        public MockAC(string key, string name, MockAcPropertiesConfig props)\n            : base(key, name)\n        {\n            CodecInfo = new MockAudioCodecInfo();\n\n            CodecInfo.PhoneNumber = props.PhoneNumber;\n        }\n\n        /// <summary>\n        /// Dial method\n        /// </summary>\n        /// <inheritdoc />\n        public override void Dial(string number)\n        {\n            if (!IsInCall)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Dial: {0}\", number);\n                var call = new CodecActiveCallItem()\n                {\n                    Name = \"Mock Outgoing Call\",\n                    Number = number,\n                    Type = eCodecCallType.Audio,\n                    Status = eCodecCallStatus.Connected,\n                    Direction = eCodecCallDirection.Outgoing,\n                    Id = \"mockAudioCall-1\"\n                };\n\n                ActiveCalls.Add(call);\n\n                OnCallStatusChange(call);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Already in call.  Cannot dial new call.\");\n            }\n        }\n\n        /// <summary>\n        /// EndCall method\n        /// </summary>\n        /// <inheritdoc />\n        public override void EndCall(CodecActiveCallItem call)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"EndCall\");\n            ActiveCalls.Remove(call);\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call);\n        }\n\n        /// <summary>\n        /// EndAllCalls method\n        /// </summary>\n        /// <inheritdoc />\n        public override void EndAllCalls()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"EndAllCalls\");\n            for (int i = ActiveCalls.Count - 1; i >= 0; i--)\n            {\n                var call = ActiveCalls[i];\n                ActiveCalls.Remove(call);\n                SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call);\n            }\n        }\n\n        /// <summary>\n        /// AcceptCall method\n        /// </summary>\n        /// <inheritdoc />\n        public override void AcceptCall(CodecActiveCallItem call)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"AcceptCall\");\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call);\n        }\n\n        /// <summary>\n        /// RejectCall method\n        /// </summary>\n        public override void RejectCall(CodecActiveCallItem call)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"RejectCall\");\n            ActiveCalls.Remove(call);\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call);\n        }\n\n        /// <summary>\n        /// SendDtmf method\n        /// </summary>\n        /// <inheritdoc />\n        public override void SendDtmf(string s)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"BEEP BOOP SendDTMF: {0}\", s);\n        }\n\n        /// <summary>\n        /// TestIncomingAudioCall method\n        /// </summary>\n        /// <param name=\"number\">Phone number to call from</param>\n        public void TestIncomingAudioCall(string number)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"TestIncomingAudioCall from {0}\", number);\n            var call = new CodecActiveCallItem() { Name = number, Id = number, Number = number, Type = eCodecCallType.Audio, Direction = eCodecCallDirection.Incoming };\n            ActiveCalls.Add(call);\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Ringing, call);\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a MockAudioCodecInfo\n    /// </summary>\n    public class MockAudioCodecInfo : AudioCodecInfo\n    {\n        string _phoneNumber;\n\n        /// <inheritdoc />\n        public override string PhoneNumber\n        {\n            get\n            {\n                return _phoneNumber;\n            }\n            set\n            {\n                _phoneNumber = value;\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a MockACFactory\n    /// </summary>\n    public class MockACFactory : EssentialsDeviceFactory<MockAC>\n    {\n        /// <summary>\n        /// Constructor for MockACFactory\n        /// </summary>\n        public MockACFactory()\n        {\n            TypeNames = new List<string>() { \"mockac\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new MockAc Device\");\n            var props = Newtonsoft.Json.JsonConvert.DeserializeObject<AudioCodec.MockAcPropertiesConfig>(dc.Properties.ToString());\n            return new AudioCodec.MockAC(dc.Key, dc.Name, props);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/AudioCodec/MockAC/MockAcPropertiesConfig.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.AudioCodec\n{\n    /// <summary>\n    /// Represents a MockAcPropertiesConfig\n    /// </summary>\n    public class MockAcPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the PhoneNumber\n        /// </summary>\n        [JsonProperty(\"phoneNumber\")]\n        public string PhoneNumber { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/CameraBase.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Devices;\nusing PepperDash.Essentials.Core.Presets;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Enumeration of eCameraCapabilities values\n    /// </summary>\n    public enum eCameraCapabilities\n    {\n        /// <summary>\n        /// No camera capabilities\n        /// </summary>\n        None = 0,\n        /// <summary>\n        /// Camera supports pan movement\n        /// </summary>\n        Pan = 1,\n        /// <summary>\n        /// Camera supports tilt movement\n        /// </summary>\n        Tilt = 2,\n        /// <summary>\n        /// Camera supports zoom functionality\n        /// </summary>\n        Zoom = 4,\n        /// <summary>\n        /// Camera supports focus adjustment\n        /// </summary>\n        Focus = 8\n    }\n\n    /// <summary>\n    /// Abstract base class for camera devices that provides common camera functionality and capabilities\n    /// </summary>\n    public abstract class CameraBase : ReconfigurableDevice, IRoutingOutputs\n    {\n        /// <summary>\n        /// Gets or sets the ControlMode\n        /// </summary>\n        [JsonProperty(\"controlMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public eCameraControlMode ControlMode { get; protected set; }\n\n        #region IRoutingOutputs Members\n\n        /// <summary>\n        /// Gets or sets the OutputPorts\n        /// </summary>\n        [JsonIgnore]\n        public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; protected set; }\n\n        #endregion\n\n        /// <summary>\n        /// Gets a value indicating whether this camera supports pan movement\n        /// </summary>\n        [JsonProperty(\"canPan\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CanPan\n        {\n            get\n            {\n                return (Capabilities & eCameraCapabilities.Pan) == eCameraCapabilities.Pan;\n            }\n        }\n\n        /// <summary>\n        /// Gets a value indicating whether this camera supports tilt movement\n        /// </summary>\n        [JsonProperty(\"canTilt\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CanTilt\n        {\n            get\n            {\n                return (Capabilities & eCameraCapabilities.Tilt) == eCameraCapabilities.Tilt;\n            }\n        }\n\n        /// <summary>\n        /// Gets a value indicating whether this camera supports zoom functionality\n        /// </summary>\n        [JsonProperty(\"canZoom\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CanZoom\n        {\n            get\n            {\n                return (Capabilities & eCameraCapabilities.Zoom) == eCameraCapabilities.Zoom;\n            }\n        }\n\n        /// <summary>\n        /// Gets a value indicating whether this camera supports focus adjustment\n        /// </summary>\n        [JsonProperty(\"canFocus\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CanFocus\n        {\n            get\n            {\n                return (Capabilities & eCameraCapabilities.Focus) == eCameraCapabilities.Focus;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets a bitmasked value to indicate the movement capabilities of this camera\n        /// </summary>\n        protected eCameraCapabilities Capabilities { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the CameraBase class with the specified device configuration\n        /// </summary>\n        /// <param name=\"config\">The device configuration</param>\n        protected CameraBase(DeviceConfig config) : base(config)\n        {\n            OutputPorts = new RoutingPortCollection<RoutingOutputPort>();\n\n            ControlMode = eCameraControlMode.Manual;\n\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the CameraBase class with the specified key and name\n        /// </summary>\n        /// <param name=\"key\">The unique key for this camera device</param>\n        /// <param name=\"name\">The friendly name for this camera device</param>\n        protected CameraBase(string key, string name) :\n            this(new DeviceConfig { Name = name, Key = key })\n        {\n\n        }\n\n        /// <summary>\n        /// Links the camera device to the API bridge for control and feedback\n        /// </summary>\n        /// <param name=\"cameraDevice\">The camera device to link</param>\n        /// <param name=\"trilist\">The trilist for communication</param>\n        /// <param name=\"joinStart\">The starting join number for the camera controls</param>\n        /// <param name=\"joinMapKey\">The join map key for custom join mappings</param>\n        /// <param name=\"bridge\">The EiscApiAdvanced bridge for advanced join mapping</param>\n        protected void LinkCameraToApi(CameraBase cameraDevice, BasicTriList trilist, uint joinStart, string joinMapKey,\n            EiscApiAdvanced bridge)\n        {\n            CameraControllerJoinMap joinMap = new CameraControllerJoinMap(joinStart);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey);\n\n            if (customJoins != null)\n            {\n                joinMap.SetCustomJoinData(customJoins);\n            }\n\n            Debug.LogMessage(LogEventLevel.Debug, \"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n            Debug.LogMessage(LogEventLevel.Information, \"Linking to Bridge Type {0}\", cameraDevice.GetType().Name.ToString());\n\n            var commMonitor = cameraDevice as ICommunicationMonitor;\n            commMonitor.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(\n                trilist.BooleanInput[joinMap.IsOnline.JoinNumber]);\n\n            var ptzCamera = cameraDevice as IHasCameraPtzControl;\n\n            if (ptzCamera != null)\n            {\n                trilist.SetBoolSigAction(joinMap.PanLeft.JoinNumber, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.PanLeft();\n                    }\n                    else\n                    {\n                        ptzCamera.PanStop();\n                    }\n                });\n                trilist.SetBoolSigAction(joinMap.PanRight.JoinNumber, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.PanRight();\n                    }\n                    else\n                    {\n                        ptzCamera.PanStop();\n                    }\n                });\n\n                trilist.SetBoolSigAction(joinMap.TiltUp.JoinNumber, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.TiltUp();\n                    }\n                    else\n                    {\n                        ptzCamera.TiltStop();\n                    }\n                });\n                trilist.SetBoolSigAction(joinMap.TiltDown.JoinNumber, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.TiltDown();\n                    }\n                    else\n                    {\n                        ptzCamera.TiltStop();\n                    }\n                });\n\n                trilist.SetBoolSigAction(joinMap.ZoomIn.JoinNumber, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.ZoomIn();\n                    }\n                    else\n                    {\n                        ptzCamera.ZoomStop();\n                    }\n                });\n\n                trilist.SetBoolSigAction(joinMap.ZoomOut.JoinNumber, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.ZoomOut();\n                    }\n                    else\n                    {\n                        ptzCamera.ZoomStop();\n                    }\n                });\n            }\n\n            var powerCamera = cameraDevice as IHasPowerControl;\n            if (powerCamera != null)\n            {\n                trilist.SetSigTrueAction(joinMap.PowerOn.JoinNumber, () => powerCamera.PowerOn());\n                trilist.SetSigTrueAction(joinMap.PowerOff.JoinNumber, () => powerCamera.PowerOff());\n\n                var powerFbCamera = powerCamera as IHasPowerControlWithFeedback;\n                if (powerFbCamera != null)\n                {\n                    powerFbCamera.PowerIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PowerOn.JoinNumber]);\n                    powerFbCamera.PowerIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.PowerOff.JoinNumber]);\n                }\n            }\n\n            if (cameraDevice is ICommunicationMonitor)\n            {\n                var monitoredCamera = cameraDevice as ICommunicationMonitor;\n                monitoredCamera.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(\n                    trilist.BooleanInput[joinMap.IsOnline.JoinNumber]);\n            }\n\n            if (cameraDevice is IHasCameraPresets)\n            {\n                // Set the preset lables when they change\n                var presetsCamera = cameraDevice as IHasCameraPresets;\n                presetsCamera.PresetsListHasChanged += new EventHandler<EventArgs>((o, a) =>\n                {\n                    SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);\n                });\n\n                SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);\n\n                for (int i = 0; i < joinMap.PresetRecallStart.JoinSpan; i++)\n                {\n                    int tempNum = i;\n\n                    trilist.SetSigTrueAction((ushort)(joinMap.PresetRecallStart.JoinNumber + tempNum), () =>\n                    {\n                        presetsCamera.PresetSelect(tempNum);\n                    });\n                    trilist.SetSigTrueAction((ushort)(joinMap.PresetSaveStart.JoinNumber + tempNum), () =>\n                    {\n                        var label = trilist.GetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum));\n\n                        presetsCamera.PresetStore(tempNum, label);\n                    });\n                }\n                trilist.OnlineStatusChange += (sender, args) =>\n                {\n                    if (!args.DeviceOnLine)\n                    { return; }\n\n                    SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);\n                };\n\n            }\n        }\n        private void SendCameraPresetNamesToApi(IHasCameraPresets presetsCamera, CameraControllerJoinMap joinMap, BasicTriList trilist)\n        {\n            for (int i = 1; i <= joinMap.NumberOfPresets.JoinNumber; i++)\n            {\n                int tempNum = i - 1;\n\n                string label = \"\";\n\n                var preset = presetsCamera.Presets.FirstOrDefault(p => p.ID.Equals(i));\n\n                if (preset != null)\n                    label = preset.Description;\n\n                trilist.SetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum), label);\n            }\n        }\n    }\n\n\n    /// <summary>\n    /// Represents a CameraPreset\n    /// </summary>\n    public class CameraPreset : PresetBase\n    {\n        /// <summary>\n        /// Initializes a new instance of the CameraPreset class\n        /// </summary>\n        /// <param name=\"id\">The preset ID</param>\n        /// <param name=\"description\">The preset description</param>\n        /// <param name=\"isDefined\">Whether the preset is defined</param>\n        /// <param name=\"isDefinable\">Whether the preset can be defined</param>\n        public CameraPreset(int id, string description, bool isDefined, bool isDefinable)\n            : base(id, description, isDefined, isDefinable)\n        {\n\n        }\n    }\n\n\n    /// <summary>\n    /// Represents a CameraPropertiesConfig\n    /// </summary>\n    public class CameraPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the CommunicationMonitorProperties\n        /// </summary>\n        public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Control\n        /// </summary>\n        public ControlPropertiesConfig Control { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SupportsAutoMode\n        /// </summary>\n        [JsonProperty(\"supportsAutoMode\")]\n        public bool SupportsAutoMode { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SupportsOffMode\n        /// </summary>\n        [JsonProperty(\"supportsOffMode\")]\n        public bool SupportsOffMode { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Presets\n        /// </summary>\n        [JsonProperty(\"presets\")]\n        public List<CameraPreset> Presets { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/CameraVisca.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Represents a CameraVisca\n    /// </summary>\n    [Obsolete(\"CameraVisca is no longer supported and will be removed in a future release.  Use the CameraVisca plugin instead.\")]\n\n    public class CameraVisca : CameraBase, IHasCameraPtzControl, ICommunicationMonitor, IHasCameraPresets, IHasPowerControlWithFeedback, IBridgeAdvanced, IHasCameraFocusControl, IHasAutoFocusMode\n    {\n        private readonly CameraViscaPropertiesConfig PropertiesConfig;\n\n        /// <summary>\n        /// Gets or sets the Communication\n        /// </summary>\n        public IBasicCommunication Communication { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the CommunicationMonitor\n        /// </summary>\n        public StatusMonitorBase CommunicationMonitor { get; private set; }\n\n        /// <summary>\n        /// Used to store the actions to parse inquiry responses as the inquiries are sent\n        /// </summary>\n        private readonly CrestronQueue<Action<byte[]>> InquiryResponseQueue;\n\n        /// <summary>\n        /// Camera ID (Default 1)\n        /// </summary>\n        public byte ID = 0x01;\n\n        /// <summary>\n        /// Response ID used for VISCA communication\n        /// </summary>\n        public byte ResponseID;\n\n        /// <summary>\n        /// Slow speed value for pan movement\n        /// </summary>\n        public byte PanSpeedSlow = 0x10;\n\n        /// <summary>\n        /// Slow speed value for tilt movement\n        /// </summary>\n        public byte TiltSpeedSlow = 0x10;\n\n        /// <summary>\n        /// Fast speed value for pan movement\n        /// </summary>\n        public byte PanSpeedFast = 0x13;\n\n        /// <summary>\n        /// Fast speed value for tilt movement\n        /// </summary>\n        public byte TiltSpeedFast = 0x13;\n\n        // private bool IsMoving;\n        private bool IsZooming;\n\n        bool _powerIsOn;\n\n        /// <summary>\n        /// Gets or sets a value indicating whether the camera power is on\n        /// </summary>\n        public bool PowerIsOn\n        {\n            get\n            {\n                return _powerIsOn;\n            }\n            private set\n            {\n                if (value != _powerIsOn)\n                {\n                    _powerIsOn = value;\n                    PowerIsOnFeedback.FireUpdate();\n                    CameraIsOffFeedback.FireUpdate();\n                }\n            }\n        }\n\n        const byte ZoomInCmd = 0x02;\n        const byte ZoomOutCmd = 0x03;\n        const byte ZoomStopCmd = 0x00;\n\n        /// <summary>\n        /// Used to determine when to move the camera at a faster speed if a direction is held\n        /// </summary>\n        CTimer SpeedTimer;\n        // TODO: Implment speed timer for PTZ controls\n\n        long FastSpeedHoldTimeMs = 2000;\n\n        byte[] IncomingBuffer = new byte[] { };\n\n        /// <summary>\n        /// Feedback indicating whether the camera power is on\n        /// </summary>\n        public BoolFeedback PowerIsOnFeedback { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the CameraVisca class\n        /// </summary>\n        /// <param name=\"key\">The unique key for this camera device</param>\n        /// <param name=\"name\">The friendly name for this camera device</param>\n        /// <param name=\"comm\">The communication interface for VISCA protocol</param>\n        /// <param name=\"props\">The camera properties configuration</param>\n        public CameraVisca(string key, string name, IBasicCommunication comm, CameraViscaPropertiesConfig props) :\n            base(key, name)\n        {\n            InquiryResponseQueue = new CrestronQueue<Action<byte[]>>(15);\n\n            Presets = props.Presets;\n\n            PropertiesConfig = props;\n\n            ID = (byte)(props.Id + 0x80);\n            ResponseID = (byte)((props.Id * 0x10) + 0x80);\n\n            SetupCameraSpeeds();\n\n            OutputPorts.Add(new RoutingOutputPort(\"videoOut\", eRoutingSignalType.Video, eRoutingPortConnectionType.None, null, this, true));\n\n            // Default to all capabilties\n            Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom | eCameraCapabilities.Focus;\n\n            Communication = comm;\n            if (comm is ISocketStatus socket)\n            {\n                // This instance uses IP control\n                socket.ConnectionChange += new EventHandler<GenericSocketStatusChageEventArgs>(Socket_ConnectionChange);\n            }\n            else\n            {\n                // This instance uses RS-232 control\n            }\n\n            Communication.BytesReceived += new EventHandler<GenericCommMethodReceiveBytesArgs>(Communication_BytesReceived);\n            PowerIsOnFeedback = new BoolFeedback(\"powerIsOn\", () => { return PowerIsOn; });\n            CameraIsOffFeedback = new BoolFeedback(\"cameraIsOff\", () => { return !PowerIsOn; });\n\n            if (props.CommunicationMonitorProperties != null)\n            {\n                CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);\n            }\n            else\n            {\n                CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, \"\\x81\\x09\\x04\\x00\\xFF\");\n            }\n            DeviceManager.AddDevice(CommunicationMonitor);\n        }\n\n\n        /// <summary>\n        /// Sets up camera speed values based on config\n        /// </summary>\n        void SetupCameraSpeeds()\n        {\n            if (PropertiesConfig.FastSpeedHoldTimeMs > 0)\n            {\n                FastSpeedHoldTimeMs = PropertiesConfig.FastSpeedHoldTimeMs;\n            }\n\n            if (PropertiesConfig.PanSpeedSlow > 0)\n            {\n                PanSpeedSlow = (byte)PropertiesConfig.PanSpeedSlow;\n            }\n            if (PropertiesConfig.PanSpeedFast > 0)\n            {\n                PanSpeedFast = (byte)PropertiesConfig.PanSpeedFast;\n            }\n\n            if (PropertiesConfig.TiltSpeedSlow > 0)\n            {\n                TiltSpeedSlow = (byte)PropertiesConfig.TiltSpeedSlow;\n            }\n            if (PropertiesConfig.TiltSpeedFast > 0)\n            {\n                TiltSpeedFast = (byte)PropertiesConfig.TiltSpeedFast;\n            }\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            Communication.Connect();\n\n\n            CommunicationMonitor.StatusChange += (o, a) => { Debug.LogMessage(LogEventLevel.Verbose, this, \"Communication monitor state: {0}\", CommunicationMonitor.Status); };\n            CommunicationMonitor.Start();\n\n\n            CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), \"con\" + Key, \"\", ConsoleAccessLevelEnum.AccessOperator);\n            return true;\n        }\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            LinkCameraToApi(this, trilist, joinStart, joinMapKey, bridge);\n        }\n\n        void Socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)\n        {\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Socket Status Change: {0}\", e.Client.ClientStatus.ToString());\n\n            if (e.Client.IsConnected)\n            {\n\n            }\n            else\n            {\n\n            }\n        }\n\n\n        void SendBytes(byte[] b)\n        {\n\n            if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Sending:{0}\", ComTextHelper.GetEscapedText(b));\n\n            Communication.SendBytes(b);\n        }\n\n        void Communication_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e)\n        {\n            var newBytes = new byte[IncomingBuffer.Length + e.Bytes.Length];\n\n            try\n            {\n                // This is probably not thread-safe buffering\n                // Append the incoming bytes with whatever is in the buffer\n                IncomingBuffer.CopyTo(newBytes, 0);\n                e.Bytes.CopyTo(newBytes, IncomingBuffer.Length);\n                if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1\n                    Debug.LogMessage(LogEventLevel.Verbose, this, \"Received:{0}\", ComTextHelper.GetEscapedText(newBytes));\n\n                byte[] message = new byte[] { };\n\n                // Search for the delimiter 0xFF character\n                for (int i = 0; i < newBytes.Length; i++)\n                {\n                    if (newBytes[i] == 0xFF)\n                    {\n                        // i will be the index of the delmiter character\n                        message = newBytes.Take(i).ToArray();\n                        // Skip over what we just took and save the rest for next time\n                        newBytes = newBytes.Skip(i).ToArray();\n                    }\n                }\n\n                if (message.Length > 0)\n                {\n                    // Check for matching ID\n                    if (message[0] != ResponseID)\n                    {\n                        return;\n                    }\n\n                    switch (message[1])\n                    {\n                        case 0x40:\n                            {\n                                // ACK received\n                                Debug.LogMessage(LogEventLevel.Verbose, this, \"ACK Received\");\n                                break;\n                            }\n                        case 0x50:\n                            {\n\n                                if (message[2] == 0xFF)\n                                {\n                                    // Completion received\n                                    Debug.LogMessage(LogEventLevel.Verbose, this, \"Completion Received\");\n                                }\n                                else\n                                {\n                                    // Inquiry response received.  Dequeue the next response handler and invoke it\n                                    if (InquiryResponseQueue.Count > 0)\n                                    {\n                                        var inquiryAction = InquiryResponseQueue.Dequeue();\n\n                                        inquiryAction.Invoke(message.Skip(2).ToArray());\n                                    }\n                                    else\n                                    {\n                                        Debug.LogMessage(LogEventLevel.Verbose, this, \"Response Queue is empty. Nothing to dequeue.\");\n                                    }\n                                }\n\n                                break;\n                            }\n                        case 0x60:\n                            {\n                                // Error message\n\n                                switch (message[2])\n                                {\n                                    case 0x01:\n                                        {\n                                            // Message Length Error\n                                            Debug.LogMessage(LogEventLevel.Verbose, this, \"Error from device: Message Length Error\");\n                                            break;\n                                        }\n                                    case 0x02:\n                                        {\n                                            // Syntax Error\n                                            Debug.LogMessage(LogEventLevel.Verbose, this, \"Error from device: Syntax Error\");\n                                            break;\n                                        }\n                                    case 0x03:\n                                        {\n                                            // Command Buffer Full\n                                            Debug.LogMessage(LogEventLevel.Verbose, this, \"Error from device: Command Buffer Full\");\n                                            break;\n                                        }\n                                    case 0x04:\n                                        {\n                                            // Command Cancelled\n                                            Debug.LogMessage(LogEventLevel.Verbose, this, \"Error from device: Command Cancelled\");\n                                            break;\n                                        }\n                                    case 0x05:\n                                        {\n                                            // No Socket\n                                            Debug.LogMessage(LogEventLevel.Verbose, this, \"Error from device: No Socket\");\n                                            break;\n                                        }\n                                    case 0x41:\n                                        {\n                                            // Command not executable\n                                            Debug.LogMessage(LogEventLevel.Verbose, this, \"Error from device: Command not executable\");\n                                            break;\n                                        }\n                                }\n                                break;\n                            }\n                    }\n\n                    if (message == new byte[] { ResponseID, 0x50, 0x02, 0xFF })\n                    {\n                        PowerIsOn = true;\n                    }\n                    else if (message == new byte[] { ResponseID, 0x50, 0x03, 0xFF })\n                    {\n                        PowerIsOn = false;\n                    }\n\n                }\n\n            }\n            catch (Exception err)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Error parsing feedback: {0}\", err);\n            }\n            finally\n            {\n                // Save whatever partial message is here\n                IncomingBuffer = newBytes;\n            }\n        }\n\n        /// <summary>\n        /// Sends a pan/tilt command. If the command is not for fastSpeed then it starts a timer to initiate fast speed.\n        /// </summary>\n        /// <param name=\"cmd\">The VISCA command to send</param>\n        /// <param name=\"fastSpeedEnabled\">Whether fast speed is enabled for this command</param>\n\t\tprivate void SendPanTiltCommand(byte[] cmd, bool fastSpeedEnabled)\n        {\n            SendBytes(GetPanTiltCommand(cmd, fastSpeedEnabled));\n\n            if (!fastSpeedEnabled)\n            {\n                if (SpeedTimer != null)\n                {\n                    StopSpeedTimer();\n                }\n\n                // Start the timer to send fast speed if still moving after FastSpeedHoldTime elapses\n                SpeedTimer = new CTimer((o) => SendPanTiltCommand(GetPanTiltCommand(cmd, true), true), FastSpeedHoldTimeMs);\n            }\n\n        }\n\n        private void StopSpeedTimer()\n        {\n            if (SpeedTimer != null)\n            {\n                SpeedTimer.Stop();\n                SpeedTimer.Dispose();\n                SpeedTimer = null;\n            }\n        }\n\n        /// <summary>\n        /// Generates the pan/tilt command with either slow or fast speed\n        /// </summary>\n        /// <param name=\"cmd\"></param>\n        /// <param name=\"fastSpeed\"></param>\n        /// <returns></returns>\n        private byte[] GetPanTiltCommand(byte[] cmd, bool fastSpeed)\n        {\n            byte panSpeed;\n            byte tiltSpeed;\n\n            if (!fastSpeed)\n            {\n                panSpeed = PanSpeedSlow;\n                tiltSpeed = TiltSpeedSlow;\n            }\n            else\n            {\n                panSpeed = PanSpeedFast;\n                tiltSpeed = TiltSpeedFast;\n            }\n\n            var temp = new byte[] { ID, 0x01, 0x06, 0x01, panSpeed, tiltSpeed };\n            int length = temp.Length + cmd.Length + 1;\n\n            byte[] sum = new byte[length];\n            temp.CopyTo(sum, 0);\n            cmd.CopyTo(sum, temp.Length);\n            sum[length - 1] = 0xFF;\n\n            return sum;\n        }\n\n\n        void SendPowerQuery()\n        {\n            SendBytes(new byte[] { ID, 0x09, 0x04, 0x00, 0xFF });\n            InquiryResponseQueue.Enqueue(HandlePowerResponse);\n        }\n\n        /// <summary>\n        /// PowerOn method\n        /// </summary>\n        public void PowerOn()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x00, 0x02, 0xFF });\n            SendPowerQuery();\n        }\n\n        void HandlePowerResponse(byte[] response)\n        {\n            switch (response[0])\n            {\n                case 0x02:\n                    {\n                        PowerIsOn = true;\n                        break;\n                    }\n                case 0x03:\n                    {\n                        PowerIsOn = false;\n                        break;\n                    }\n            }\n        }\n\n        /// <summary>\n        /// PowerOff method\n        /// </summary>\n        public void PowerOff()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x00, 0x03, 0xFF });\n            SendPowerQuery();\n        }\n\n        /// <summary>\n        /// PowerToggle method\n        /// </summary>\n        public void PowerToggle()\n        {\n            if (PowerIsOnFeedback.BoolValue)\n                PowerOff();\n            else\n                PowerOn();\n        }\n\n        /// <summary>\n        /// PanLeft method\n        /// </summary>\n        public void PanLeft()\n        {\n            SendPanTiltCommand(new byte[] { 0x01, 0x03 }, false);\n            // IsMoving = true;\n        }\n        /// <summary>\n        /// PanRight method\n        /// </summary>\n        public void PanRight()\n        {\n            SendPanTiltCommand(new byte[] { 0x02, 0x03 }, false);\n            // IsMoving = true;\n        }\n        /// <summary>\n        /// PanStop method\n        /// </summary>\n        public void PanStop()\n        {\n            Stop();\n        }\n        /// <summary>\n        /// TiltDown method\n        /// </summary>\n        public void TiltDown()\n        {\n            SendPanTiltCommand(new byte[] { 0x03, 0x02 }, false);\n            // IsMoving = true;\n        }\n        /// <summary>\n        /// TiltUp method\n        /// </summary>\n        public void TiltUp()\n        {\n            SendPanTiltCommand(new byte[] { 0x03, 0x01 }, false);\n            // IsMoving = true;\n        }\n        /// <summary>\n        /// TiltStop method\n        /// </summary>\n        public void TiltStop()\n        {\n            Stop();\n        }\n\n        private void SendZoomCommand(byte cmd)\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x07, cmd, 0xFF });\n        }\n\n\n        /// <summary>\n        /// ZoomIn method\n        /// </summary>\n        public void ZoomIn()\n        {\n            SendZoomCommand(ZoomInCmd);\n            IsZooming = true;\n        }\n        /// <summary>\n        /// ZoomOut method\n        /// </summary>\n        public void ZoomOut()\n        {\n            SendZoomCommand(ZoomOutCmd);\n            IsZooming = true;\n        }\n        /// <summary>\n        /// ZoomStop method\n        /// </summary>\n        public void ZoomStop()\n        {\n            Stop();\n        }\n\n        /// <summary>\n        /// Stop method\n        /// </summary>\n        public void Stop()\n        {\n            if (IsZooming)\n            {\n                SendZoomCommand(ZoomStopCmd);\n                IsZooming = false;\n            }\n            else\n            {\n                StopSpeedTimer();\n                SendPanTiltCommand(new byte[] { 0x03, 0x03 }, false);\n                // IsMoving = false;\n            }\n        }\n        /// <summary>\n        /// PositionHome method\n        /// </summary>\n        public void PositionHome()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x06, 0x02, PanSpeedFast, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF });\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x47, 0x00, 0x00, 0x00, 0x00, 0xFF });\n        }\n        /// <summary>\n        /// RecallPreset method\n        /// </summary>\n        public void RecallPreset(int presetNumber)\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x3F, 0x02, (byte)presetNumber, 0xFF });\n        }\n        /// <summary>\n        /// SavePreset method\n        /// </summary>\n        public void SavePreset(int presetNumber)\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x3F, 0x01, (byte)presetNumber, 0xFF });\n        }\n\n        #region IHasCameraPresets Members\n\n        /// <summary>\n        /// Event that is raised when the presets list has changed\n        /// </summary>\n        public event EventHandler<EventArgs> PresetsListHasChanged;\n\n        /// <summary>\n        /// Raises the PresetsListHasChanged event\n        /// </summary>\n        protected void OnPresetsListHasChanged()\n        {\n            var handler = PresetsListHasChanged;\n            if (handler == null)\n                return;\n\n            handler.Invoke(this, EventArgs.Empty);\n        }\n\n        /// <summary>\n        /// Gets or sets the Presets\n        /// </summary>\n        public List<CameraPreset> Presets { get; private set; }\n\n        /// <summary>\n        /// PresetSelect method\n        /// </summary>\n        public void PresetSelect(int preset)\n        {\n            RecallPreset(preset);\n        }\n\n        /// <summary>\n        /// PresetStore method\n        /// </summary>\n        public void PresetStore(int preset, string description)\n        {\n            SavePreset(preset);\n        }\n\n\n        #endregion\n\n        #region IHasCameraFocusControl Members\n\n        /// <summary>\n        /// FocusNear method\n        /// </summary>\n        public void FocusNear()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x08, 0x03, 0xFF });\n        }\n\n        /// <summary>\n        /// FocusFar method\n        /// </summary>\n        public void FocusFar()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x08, 0x02, 0xFF });\n        }\n\n        /// <summary>\n        /// FocusStop method\n        /// </summary>\n        public void FocusStop()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x08, 0x00, 0xFF });\n        }\n\n        /// <summary>\n        /// TriggerAutoFocus method\n        /// </summary>\n        public void TriggerAutoFocus()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x18, 0x01, 0xFF });\n            SendAutoFocusQuery();\n        }\n\n        #endregion\n\n        #region IHasAutoFocus Members\n\n        /// <summary>\n        /// SetFocusModeAuto method\n        /// </summary>\n        public void SetFocusModeAuto()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x38, 0x02, 0xFF });\n            SendAutoFocusQuery();\n        }\n\n        /// <summary>\n        /// SetFocusModeManual method\n        /// </summary>\n        public void SetFocusModeManual()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x38, 0x03, 0xFF });\n            SendAutoFocusQuery();\n        }\n\n        /// <summary>\n        /// ToggleFocusMode method\n        /// </summary>\n        public void ToggleFocusMode()\n        {\n            SendBytes(new byte[] { ID, 0x01, 0x04, 0x38, 0x10, 0xFF });\n            SendAutoFocusQuery();\n        }\n\n        #endregion\n\n        void SendAutoFocusQuery()\n        {\n            SendBytes(new byte[] { ID, 0x09, 0x04, 0x38, 0xFF });\n            InquiryResponseQueue.Enqueue(HandleAutoFocusResponse);\n        }\n\n        void HandleAutoFocusResponse(byte[] response)\n        {\n            switch (response[0])\n            {\n                case 0x02:\n                    {\n                        // Auto Mode\n                        PowerIsOn = true;\n                        break;\n                    }\n                case 0x03:\n                    {\n                        // Manual Mode\n                        PowerIsOn = false;\n                        break;\n                    }\n            }\n        }\n\n        #region IHasCameraOff Members\n\n        /// <summary>\n        /// Gets or sets the CameraIsOffFeedback\n        /// </summary>\n        public BoolFeedback CameraIsOffFeedback { get; private set; }\n\n\n        /// <summary>\n        /// CameraOff method\n        /// </summary>\n        public void CameraOff()\n        {\n            PowerOff();\n        }\n\n        #endregion\n    }\n\n    /// <summary>\n    /// Represents a CameraViscaFactory\n    /// </summary>\n    public class CameraViscaFactory : EssentialsDeviceFactory<CameraVisca>\n    {\n        /// <summary>\n        /// Initializes a new instance of the CameraViscaFactory class\n        /// </summary>\n        public CameraViscaFactory()\n        {\n            TypeNames = new List<string>() { \"cameravisca\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new CameraVisca Device\");\n            var comm = CommFactory.CreateCommForDevice(dc);\n            var props = Newtonsoft.Json.JsonConvert.DeserializeObject<Cameras.CameraViscaPropertiesConfig>(\n                dc.Properties.ToString());\n            return new Cameras.CameraVisca(dc.Key, dc.Name, comm, props);\n        }\n    }\n\n\n    /// <summary>\n    /// Represents a CameraViscaPropertiesConfig\n    /// </summary>\n    public class CameraViscaPropertiesConfig : CameraPropertiesConfig\n    {\n        /// <summary>\n        /// Control ID of the camera (1-7)\n        /// Gets or sets the Id\n        /// </summary>\n        [JsonProperty(\"id\")]\n        public uint Id { get; set; }\n\n        /// <summary>\n        /// Slow Pan speed (0-18)\n        /// </summary>\n        [JsonProperty(\"panSpeedSlow\")]\n        public uint PanSpeedSlow { get; set; }\n\n        /// <summary>\n        /// Fast Pan speed (0-18)\n        /// </summary>\n        [JsonProperty(\"panSpeedFast\")]\n        public uint PanSpeedFast { get; set; }\n\n        /// <summary>\n        /// Slow tilt speed (0-18)\n        /// </summary>\n        [JsonProperty(\"tiltSpeedSlow\")]\n        public uint TiltSpeedSlow { get; set; }\n\n        /// <summary>\n        /// Fast tilt speed (0-18)\n        /// </summary>\n        [JsonProperty(\"tiltSpeedFast\")]\n        public uint TiltSpeedFast { get; set; }\n\n        /// <summary>\n        /// Time a button must be held before fast speed is engaged (Milliseconds)\n        /// </summary>\n        [JsonProperty(\"fastSpeedHoldTimeMs\")]\n        public uint FastSpeedHoldTimeMs { get; set; }\n\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/IHasCameraPresets.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Describes a camera with preset functionality\n    /// </summary>\n    public interface IHasCameraPresets\n    {\n        /// <summary>\n        /// Event that is raised when the presets list has changed\n        /// </summary>\n        event EventHandler<EventArgs> PresetsListHasChanged;\n\n        /// <summary>\n        /// Gets the list of camera presets\n        /// </summary>\n        List<CameraPreset> Presets { get; }\n\n        /// <summary>\n        /// Selects the specified preset\n        /// </summary>\n        /// <param name=\"preset\">The preset number to select</param>\n        void PresetSelect(int preset);\n\n        /// <summary>\n        /// Stores a preset at the specified location with the given description\n        /// </summary>\n        /// <param name=\"preset\">The preset number to store</param>\n        /// <param name=\"description\">The description for the preset</param>\n        void PresetStore(int preset, string description);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/CameraSelectedEventArgs.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Event arguments for the CameraSelected event\n    /// </summary>\n    [Obsolete(\"Use CameraSelectedEventArgs<T> instead. This class will be removed in a future version\")]\n    public class CameraSelectedEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the SelectedCamera\n        /// </summary>\n        public CameraBase SelectedCamera { get; private set; }\n\n        /// <summary>\n        /// Constructor for CameraSelectedEventArgs\n        /// </summary>\n        /// <param name=\"camera\"></param>\n        public CameraSelectedEventArgs(CameraBase camera)\n        {\n            SelectedCamera = camera;\n        }\n    }\n\n    /// <summary>\n    /// Event arguments for the CameraSelected event\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    public class CameraSelectedEventArgs<T> : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the SelectedCamera\n        /// </summary>\n        public T SelectedCamera { get; private set; }\n\n        /// <summary>\n        /// Constructor for CameraSelectedEventArgs\n        /// </summary>\n        /// <param name=\"camera\"></param>\n        public CameraSelectedEventArgs(T camera)\n        {\n            SelectedCamera = camera;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IAmFarEndCamera.cs",
    "content": "﻿using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Defines the contract for IAmFarEndCamera\n    /// </summary>\n    public interface IAmFarEndCamera : IKeyName\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/ICameraCapabilities.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Interface for camera capabilities\n    /// </summary>\n    public interface ICameraCapabilities: IKeyName\n    {\n        /// <summary>\n        /// Indicates whether the camera can pan\n        /// </summary>\n        [JsonProperty(\"canPan\", NullValueHandling = NullValueHandling.Ignore)]\n        bool CanPan { get;  }\n\n        /// <summary>\n        /// Indicates whether the camera can tilt\n        /// </summary>\n        [JsonProperty(\"canTilt\", NullValueHandling = NullValueHandling.Ignore)]\n         bool CanTilt { get; }\n\n        /// <summary>\n        /// Indicates whether the camera can zoom\n        /// </summary>\n        [JsonProperty(\"canZoom\", NullValueHandling = NullValueHandling.Ignore)]\n        bool CanZoom { get;  }\n\n\n        /// <summary>\n        /// Indicates whether the camera can focus\n        /// </summary>\n        [JsonProperty(\"canFocus\", NullValueHandling = NullValueHandling.Ignore)]\n        bool CanFocus { get;  }\n    }\n\n    /// <summary>\n    /// Indicates the capabilities of a camera\n    /// </summary>\n    public class CameraCapabilities : ICameraCapabilities\n    {\n\n        /// <summary>\n        /// Unique Key\n        /// </summary>\n        [JsonProperty(\"key\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Key { get; set; }\n\n        /// <summary>\n        /// Isn't it obvious :)\n        /// </summary>\n        [JsonProperty(\"name\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Name { get; set; }\n\n\n        /// <summary>\n        /// Indicates whether the camera can pan\n        /// </summary>\n        [JsonProperty(\"canPan\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CanPan { get; set; }\n\n        /// <summary>\n        /// Indicates whether the camera can tilt\n        /// </summary>\n        [JsonProperty(\"canTilt\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CanTilt { get; set; }\n\n        /// <summary>\n        /// Indicates whether the camera can zoom\n        /// </summary>\n        [JsonProperty(\"canZoom\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CanZoom { get; set; }\n\n\n        /// <summary>\n        /// Indicates whether the camera can focus\n        /// </summary>\n        [JsonProperty(\"canFocus\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CanFocus { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasAutoFocusMode.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Interface for devices that have auto focus mode control\n    /// </summary>\n    public interface IHasAutoFocusMode : IHasCameraControls\n    {\n        /// <summary>\n        /// Sets the focus mode to auto or manual, or toggles between them.\n        /// </summary>\n        void SetFocusModeAuto();\n\n        /// <summary>\n        /// Sets the focus mode to manual, allowing for manual focus adjustments.\n        /// </summary>\n        void SetFocusModeManual();\n\n        /// <summary>\n        /// Toggles the focus mode between auto and manual.\n        /// </summary>\n        void ToggleFocusMode();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraAutoMode.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n\n    /// <summary>\n    /// Interface for devices that have camera auto mode control\n    /// </summary>\n    public interface IHasCameraAutoMode : IHasCameraControls\n    {\n        /// <summary>\n        /// Enables or disables the camera's auto mode, which may include automatic adjustments for focus, exposure, and other settings.\n        /// </summary>\n        void CameraAutoModeOn();\n\n        /// <summary>\n        /// Disables the camera's auto mode, allowing for manual control of camera settings.\n        /// </summary>\n        void CameraAutoModeOff();\n\n        /// <summary>\n        /// Toggles the camera's auto mode state. If the camera is in auto mode, it will switch to manual mode, and vice versa.\n        /// </summary>\n        void CameraAutoModeToggle();\n\n        /// <summary>\n        /// Feedback that indicates whether the camera's auto mode is currently enabled.\n        /// </summary>\n        BoolFeedback CameraAutoModeIsOnFeedback { get; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraControls.cs",
    "content": "﻿using PepperDash.Core;\n\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n\n    /// <summary>\n    /// Interface for devices that have camera controls\n    /// </summary>\n    public interface IHasCameraControls : IKeyName\n    {\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraFocusControl.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Defines the contract for IHasCameraFocusControl\n    /// </summary>\n    public interface IHasCameraFocusControl : IHasCameraControls\n    {\n        /// <summary>\n        /// Focuses the camera near\n        /// </summary>\n        void FocusNear();\n\n        /// <summary>\n        /// Focuses the camera far\n        /// </summary>\n        void FocusFar();\n\n        /// <summary>\n        /// Stops the camera focus movement\n        /// </summary>\n        void FocusStop();\n\n        /// <summary>\n        /// Triggers the camera's auto focus functionality, if available.\n        /// </summary>\n        void TriggerAutoFocus();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMute.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Describes the ability to mute and unmute camera video\n    /// </summary>\n    public interface IHasCameraMute : IKeyName\n    {\n        /// <summary>\n        /// Feedback that indicates whether the camera is muted\n        /// </summary>\n        BoolFeedback CameraIsMutedFeedback { get; }\n\n        /// <summary>\n        /// Mutes the camera video, preventing it from being sent to the far end\n        /// </summary>\n        void CameraMuteOn();\n\n        /// <summary>\n        /// Unmutes the camera video, allowing it to be sent to the far end\n        /// </summary>\n        void CameraMuteOff();\n\n        /// <summary>\n        /// Toggles the camera mute state. If the camera is muted, it will be unmuted, and vice versa.\n        /// </summary>\n        void CameraMuteToggle();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraMuteWithUnmuteRequest.cs",
    "content": "﻿using System;\n\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Interface for devices that can mute and unmute their camera video, with an event for unmute requests\n    /// </summary>\n    public interface IHasCameraMuteWithUnmuteReqeust : IHasCameraMute\n    {\n        /// <summary>\n        /// Event that is raised when a video unmute is requested, typically by the far end\n        /// </summary>\n        event EventHandler VideoUnmuteRequested;\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraOff.cs",
    "content": "﻿using PepperDash.Essentials.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n\n\n    /// <summary>\n    /// To be implmented on codecs that can disable their camera(s) to blank the near end video\n    /// </summary>\n    public interface IHasCameraOff : IHasCameraControls\n    {\n        /// <summary>\n        /// Feedback that indicates whether the camera is off\n        /// </summary>\n        BoolFeedback CameraIsOffFeedback { get; }\n\n        /// <summary>\n        /// Turns the camera off, blanking the near end video\n        /// </summary>\n        void CameraOff();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPanControl.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Interface for camera pan control\n    /// </summary>\n    public interface IHasCameraPanControl : IHasCameraControls\n    {\n        /// <summary>\n        /// Pans the camera left\n        /// </summary>\n        void PanLeft();\n\n        /// <summary>\n        /// Pans the camera right\n        /// </summary>\n        void PanRight();\n\n        /// <summary>\n        /// Stops the camera pan movement\n        /// </summary>\n        void PanStop();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraPtzControl.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Defines the contract for IHasCameraPtzControl\n    /// </summary>\n    public interface IHasCameraPtzControl : IHasCameraPanControl, IHasCameraTiltControl, IHasCameraZoomControl\n    {\n        /// <summary>\n        /// Resets the camera position\n        /// </summary>\n        void PositionHome();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraTiltControl.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Defines the contract for IHasCameraTiltControl\n    /// </summary>\n    public interface IHasCameraTiltControl : IHasCameraControls\n    {\n        /// <summary>\n        /// Tilts the camera down\n        /// </summary>\n        void TiltDown();\n\n        /// <summary>\n        /// Tilts the camera up\n        /// </summary>\n        void TiltUp();\n\n        /// <summary>\n        /// Stops the camera tilt movement\n        /// </summary>\n        void TiltStop();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameraZoomControl.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Defines the contract for IHasCameraZoomControl\n    /// </summary>\n    public interface IHasCameraZoomControl : IHasCameraControls\n    {\n        /// <summary>\n        /// Zooms the camera in\n        /// </summary>\n        void ZoomIn();\n\n        /// <summary>\n        /// Zooms the camera out\n        /// </summary>\n        void ZoomOut();\n\n        /// <summary>\n        /// Stops the camera zoom movement\n        /// </summary>\n        void ZoomStop();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCameras.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Interface for devices that have cameras\n    /// </summary>\n    [Obsolete(\"Use IHasCamerasWithControls instead. This interface will be removed in a future version\")]\n    public interface IHasCameras : IKeyName\n    {\n        /// <summary>\n        /// Event that is raised when a camera is selected\n        /// </summary>\n        event EventHandler<CameraSelectedEventArgs> CameraSelected;\n\n        /// <summary>\n        /// List of cameras on the device.  This should be a list of CameraBase objects\n        /// </summary>\n        List<CameraBase> Cameras { get; }\n\n        /// <summary>\n        /// The currently selected camera.  This should be a CameraBase object\n        /// </summary>\n        CameraBase SelectedCamera { get; }\n\n        /// <summary>\n        /// Feedback that indicates the currently selected camera\n        /// </summary>\n        StringFeedback SelectedCameraFeedback { get; }\n\n        /// <summary>\n        /// Selects a camera from the list of available cameras based on the provided key.\n        /// </summary>\n        /// <param name=\"key\">The unique identifier or name of the camera to select.</param>\n        void SelectCamera(string key);\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCamerasWithControls.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Interface for devices that have cameras with controls\n    /// </summary>\n    public interface IHasCamerasWithControls : IKeyName, IKeyed\n    {\n        /// <summary>\n        /// List of cameras on the device. This should be a list of IHasCameraControls objects\n        /// </summary>\n        \n        List<IHasCameraControls> Cameras { get; }\n\n        /// <summary>\n        /// The currently selected camera. This should be an IHasCameraControls object\n        /// </summary>\n        IHasCameraControls SelectedCamera { get; }\n\n        /// <summary>\n        /// Feedback that indicates the currently selected camera\n        /// </summary>\n        StringFeedback SelectedCameraFeedback { get; }\n\n        /// <summary>\n        /// Event that is raised when a camera is selected\n        /// </summary>\n        event EventHandler<CameraSelectedEventArgs<IHasCameraControls>> CameraSelected;\n\n        /// <summary>\n        /// Selects a camera from the list of available cameras based on the provided key.\n        /// </summary>\n        /// <param name=\"key\"></param>\n        void SelectCamera(string key);\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasCodecCameras.cs",
    "content": "﻿\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n\n    /// <summary>\n    /// Defines the contract for IHasCodecCameras\n    /// </summary>\n    public interface IHasCodecCameras : IHasCameras, IHasFarEndCameraControl\n    {\n\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/IHasFarEndCameraControl.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Interface for devices that have a far end camera control\n    /// </summary>\n    public interface IHasFarEndCameraControl : IKeyName\n    {\n        /// <summary>\n        /// Gets the far end camera, which is typically a CameraBase object that represents the camera at the far end of a call\n        /// </summary>\n        CameraBase FarEndCamera { get; }\n\n        /// <summary>\n        /// Feedback that indicates whether the far end camera is being controlled\n        /// </summary>\n        BoolFeedback ControllingFarEndCameraFeedback { get; }\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Cameras/Interfaces/eCameraControlMode.cs",
    "content": "﻿\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Enum for camera control modes\n    /// </summary>\n    public enum eCameraControlMode\n    {\n        /// <summary>\n        /// Manual control mode, where the camera is controlled directly by the user or system\n        /// </summary>\n        Manual = 0,\n        /// <summary>\n        /// Off control mode, where the camera is turned off or disabled\n        /// </summary>\n        Off,\n        /// <summary>\n        ///  Auto control mode, where the camera automatically adjusts settings based on the environment or conditions\n        /// </summary>\n        Auto\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/Call.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a Call\n  /// </summary>\n  public class Call\n  {\n    /// <summary>\n    /// Gets or sets the Number\n    /// </summary>\n    public string Number { get; set; }\n    /// <summary>\n    /// Gets or sets the Protocol\n    /// </summary>\n    public string Protocol { get; set; }\n    /// <summary>\n    /// Gets or sets the CallRate\n    /// </summary>\n    public string CallRate { get; set; }\n    /// <summary>\n    /// Gets or sets the CallType\n    /// </summary>\n    public string CallType { get; set; }\n  }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/Cisco/IPresenterTrack.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec.Cisco\n{\n    /// <summary>\n    /// Describes the available tracking modes for a Cisco codec's Presenter Track feature.\n    /// </summary>\n    public enum ePresenterTrackMode\n    {\n        /// <summary>\n        /// Presenter Track is turned off.\n        /// </summary>\n        Off,\n        /// <summary>\n        /// Presenter Track follows the speaker's movements.\n        /// </summary>\n        Follow,\n        /// <summary>\n        /// Presenter Track is set to background mode, where it tracks the speaker but does not actively follow.\n        /// </summary>\n        Background,\n        /// <summary>\n        /// Presenter Track is set to persistent mode, where it maintains a fixed position or focus on the speaker.\n        /// </summary>\n        Persistent\n    }\n\n\n    /// <summary>\n    /// Describes the Presenter Track controls for a Cisco codec.\n    /// </summary>\n    public interface IPresenterTrack : IKeyed\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        bool PresenterTrackAvailability { get; }\n\n        /// <summary>\n        /// Feedback indicating whether Presenter Track is available.\n        /// </summary>\n        BoolFeedback PresenterTrackAvailableFeedback { get; }\n\n        /// <summary>\n        /// Feedback indicating the current status of Presenter Track is off\n        /// </summary>\n        BoolFeedback PresenterTrackStatusOffFeedback { get; }\n\n        /// <summary>\n        /// Feedback indicating the current status of Presenter Track is follow\n        /// </summary>\n        BoolFeedback PresenterTrackStatusFollowFeedback { get; }\n\n        /// <summary>\n        /// Feedback indicating the current status of Presenter Track is background\n        /// </summary>\n        BoolFeedback PresenterTrackStatusBackgroundFeedback { get; }\n\n        /// <summary>\n        /// Feedback indicating the current status of Presenter Track is persistent\n        /// </summary>\n        BoolFeedback PresenterTrackStatusPersistentFeedback { get; }\n\n        /// <summary>\n        /// Indicates the current status of Presenter Track.\n        /// </summary>\n        bool PresenterTrackStatus { get; }\n\n        /// <summary>\n        /// Turns off Presenter Track.\n        /// </summary>\n        void PresenterTrackOff();\n\n        /// <summary>\n        /// Turns on Presenter Track in follow mode.\n        /// </summary>\n        void PresenterTrackFollow();\n\n        /// <summary>\n        /// Turns on Presenter Track in background mode.\n        /// </summary>\n        void PresenterTrackBackground();\n\n        /// <summary>\n        /// Turns on Presenter Track in persistent mode.\n        /// </summary>\n        void PresenterTrackPersistent();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/Cisco/ISpeakerTrack.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec.Cisco\n{\n    /// <summary>\n    /// Describes the available tracking modes for a Cisco codec\n    /// </summary>\n    public interface ISpeakerTrack : IKeyed\n    {\n        /// <summary>\n        /// Indicates whether Speaker Track is available on the codec.\n        /// </summary>\n        bool SpeakerTrackAvailability { get; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        BoolFeedback SpeakerTrackAvailableFeedback { get; }\n\n        /// <summary>\n        /// Feedback indicating the current status of Speaker Track is off\n        /// </summary>\n        bool SpeakerTrackStatus { get; }\n\n        /// <summary>\n        /// Turns Speaker Track off\n        /// </summary>\n        void SpeakerTrackOff();\n        /// <summary>\n        /// Turns Speaker Track on\n        /// </summary>\n        void SpeakerTrackOn();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/CodecActiveCallItem.cs",
    "content": "﻿\n\nusing System;\n\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n\n{\n    /// <summary>\n    /// Represents a CodecActiveCallItem\n    /// </summary>\n    public class CodecActiveCallItem\n    {\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n\t\t[JsonProperty(\"name\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Number\n        /// </summary>\n        [JsonProperty(\"number\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Number { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        [JsonProperty(\"type\", NullValueHandling = NullValueHandling.Ignore)]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public eCodecCallType Type { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Status\n        /// </summary>\n        [JsonProperty(\"status\", NullValueHandling = NullValueHandling.Ignore)]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public eCodecCallStatus Status { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Direction\n        /// </summary>\n        [JsonProperty(\"direction\", NullValueHandling = NullValueHandling.Ignore)]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public eCodecCallDirection Direction { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Id\n        /// </summary>\n        [JsonProperty(\"id\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Id { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IsOnHold\n        /// </summary>\n        [JsonProperty(\"isOnHold\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool IsOnHold { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Duration\n        /// </summary>\n        [JsonProperty(\"duration\", NullValueHandling = NullValueHandling.Ignore)]\n        public TimeSpan Duration { get; set; }\n\n        //public object CallMetaData { get; set; }\n\n        /// <summary>\n        /// Returns true when this call is any status other than \n        /// Unknown, Disconnected, Disconnecting\n        /// </summary>\n        [JsonProperty(\"isActiveCall\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool IsActiveCall\n        {\n            get\n            {\n                return !(Status == eCodecCallStatus.Disconnected\n                    || Status == eCodecCallStatus.Disconnecting\n                    || Status == eCodecCallStatus.Idle\n                    || Status == eCodecCallStatus.Unknown);\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a CodecCallStatusItemChangeEventArgs\n    /// </summary>\n    public class CodecCallStatusItemChangeEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Gets or sets the CallItem\n        /// </summary>\n        public CodecActiveCallItem CallItem { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the CodecCallStatusItemChangeEventArgs class\n        /// </summary>\n        /// <param name=\"item\">The call item that changed</param>\n        public CodecCallStatusItemChangeEventArgs(CodecActiveCallItem item)\n        {\n            CallItem = item;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/CodecDirectory.cs",
    "content": "\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a codec directory\n  /// </summary>\n  public class CodecDirectory\n  {\n    /// <summary>\n    /// Represents the contents of the directory\n    /// We don't want to serialize this for messages to MobileControl.  MC can combine Contacts and Folders to get the same data\n    /// </summary>\n    [JsonIgnore]\n    public List<DirectoryItem> CurrentDirectoryResults { get; private set; }\n\n    /// <summary>\n    /// Gets the Contacts in the CurrentDirectoryResults\n    /// </summary>\n    [JsonProperty(\"contacts\")]\n    public List<DirectoryItem> Contacts\n    {\n      get\n      {\n        return CurrentDirectoryResults.OfType<DirectoryContact>().Cast<DirectoryItem>().ToList();\n      }\n    }\n\n    /// <summary>\n    /// Gets the Folders in the CurrentDirectoryResults\n    /// </summary>\n    [JsonProperty(\"folders\")]\n    public List<DirectoryItem> Folders\n    {\n      get\n      {\n        return CurrentDirectoryResults.OfType<DirectoryFolder>().Cast<DirectoryItem>().ToList();\n      }\n    }\n\n    /// <summary>\n    /// Used to store the ID of the current folder for CurrentDirectoryResults\n    /// Gets or sets the ResultsFolderId\n    /// </summary>\n    [JsonProperty(\"resultsFolderId\")]\n    public string ResultsFolderId { get; set; }\n\n    /// <summary>\n    /// Constructor for <see cref=\"CodecDirectory\"/>\n    /// </summary>\n    public CodecDirectory()\n    {\n      CurrentDirectoryResults = new List<DirectoryItem>();\n    }\n\n    /// <summary>\n    /// Adds folders to the directory\n    /// </summary>\n    /// <param name=\"folders\"></param>        \n    public void AddFoldersToDirectory(List<DirectoryItem> folders)\n    {\n      if (folders != null)\n        CurrentDirectoryResults.AddRange(folders);\n\n      SortDirectory();\n    }\n\n    /// <summary>\n    /// Adds contacts to the directory\n    /// </summary>\n    /// <param name=\"contacts\"></param>        \n    public void AddContactsToDirectory(List<DirectoryItem> contacts)\n    {\n      if (contacts != null)\n        CurrentDirectoryResults.AddRange(contacts);\n\n      SortDirectory();\n    }\n\n    /// <summary>\n    /// Filters the CurrentDirectoryResults by the predicate\n    /// </summary>\n    /// <param name=\"predicate\"></param>\n\n    public void FilterContacts(Func<DirectoryItem, bool> predicate)\n    {\n      CurrentDirectoryResults = CurrentDirectoryResults.Where(predicate).ToList();\n    }\n\n    /// <summary>\n    /// Sorts the DirectoryResults list to display all folders alphabetically, then all contacts alphabetically\n    /// </summary>\n    private void SortDirectory()\n    {\n      var sortedFolders = new List<DirectoryItem>();\n\n      sortedFolders.AddRange(CurrentDirectoryResults.Where(f => f is DirectoryFolder));\n\n      sortedFolders.OrderBy(f => f.Name);\n\n      var sortedContacts = new List<DirectoryItem>();\n\n      sortedContacts.AddRange(CurrentDirectoryResults.Where(c => c is DirectoryContact));\n\n      sortedFolders.OrderBy(c => c.Name);\n\n      CurrentDirectoryResults.Clear();\n\n      CurrentDirectoryResults.AddRange(sortedFolders);\n\n      CurrentDirectoryResults.AddRange(sortedContacts);\n    }\n\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/CodecScheduleAwareness.cs",
    "content": "\n\nusing System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a CodecScheduleAwareness\n  /// </summary>\n  public class CodecScheduleAwareness\n  {\n    List<Meeting> _meetings;\n\n    /// <summary>\n    /// Event that is raised when a meeting event changes\n    /// </summary>\n    public event EventHandler<MeetingEventArgs> MeetingEventChange;\n\n    /// <summary>\n    /// Event that is raised when the meetings list has changed\n    /// </summary>\n    public event EventHandler<EventArgs> MeetingsListHasChanged;\n\n    private int _meetingWarningMinutes = 5;\n\n    //private Meeting _previousChangedMeeting;\n\n    //private eMeetingEventChangeType _previousChangeType = eMeetingEventChangeType.Unknown;\n\n    /// <summary>\n    /// Gets or sets the number of minutes before a meeting to issue a warning\n    /// </summary>\n    public int MeetingWarningMinutes\n    {\n      get { return _meetingWarningMinutes; }\n      set { _meetingWarningMinutes = value; }\n    }\n\n    /// <summary>\n    /// Setter triggers MeetingsListHasChanged event\n    /// </summary>\n    public List<Meeting> Meetings\n    {\n      get\n      {\n        return _meetings;\n      }\n      set\n      {\n        _meetings = value;\n        MeetingsListHasChanged?.Invoke(this, new EventArgs());\n      }\n    }\n\n    private readonly CTimer _scheduleChecker;\n\n    /// <summary>\n    /// Initializes a new instance of the CodecScheduleAwareness class with default poll time\n    /// </summary>\n    public CodecScheduleAwareness()\n    {\n      Meetings = new List<Meeting>();\n\n      _scheduleChecker = new CTimer(CheckSchedule, null, 1000, 1000);\n    }\n\n    /// <summary>\n    /// Initializes a new instance of the CodecScheduleAwareness class with specified poll time\n    /// </summary>\n    /// <param name=\"pollTime\">The poll time in milliseconds for checking schedule changes</param>\n    public CodecScheduleAwareness(long pollTime)\n    {\n      Meetings = new List<Meeting>();\n\n      _scheduleChecker = new CTimer(CheckSchedule, null, pollTime, pollTime);\n    }\n\n    /// <summary>\n    /// Helper method to fire MeetingEventChange.  Should only fire once for each changeType on each meeting\n    /// </summary>\n    /// <param name=\"changeType\"></param>\n    /// <param name=\"meeting\"></param>\n    private void OnMeetingChange(eMeetingEventChangeType changeType, Meeting meeting)\n    {\n      Debug.LogMessage(LogEventLevel.Verbose, \"*****************OnMeetingChange.  id: {0} changeType: {1}**********************\", meeting.Id, changeType);\n      if (changeType != (changeType & meeting.NotifiedChangeTypes))\n      {\n        // Add this change type to the NotifiedChangeTypes\n        meeting.NotifiedChangeTypes |= changeType;\n        MeetingEventChange?.Invoke(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting });\n      }\n      else\n      {\n        Debug.LogMessage(LogEventLevel.Verbose, \"Meeting: {0} already notified of changeType: {1}\", meeting.Id, changeType);\n      }\n    }\n\n\n    /// <summary>\n    /// Checks the schedule to see if any MeetingEventChange updates should be fired\n    /// </summary>\n    /// <param name=\"o\"></param>\n    private void CheckSchedule(object o)\n    {\n      //  Iterate the meeting list and check if any meeting need to do anything\n\n      const double meetingTimeEpsilon = 0.05;\n      foreach (var m in Meetings)\n      {\n        var changeType = eMeetingEventChangeType.Unknown;\n\n        if (eMeetingEventChangeType.MeetingStartWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStartWarning) && m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingStart.Seconds > 0)       // Meeting is about to start\n        {\n          Debug.LogMessage(LogEventLevel.Verbose, \"********************* MeetingStartWarning. TotalMinutes: {0}  Seconds: {1}\", m.TimeToMeetingStart.TotalMinutes, m.TimeToMeetingStart.Seconds);\n          changeType = eMeetingEventChangeType.MeetingStartWarning;\n        }\n        else if (eMeetingEventChangeType.MeetingStart != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStart) && Math.Abs(m.TimeToMeetingStart.TotalMinutes) < meetingTimeEpsilon)           // Meeting Start\n        {\n          Debug.LogMessage(LogEventLevel.Verbose, \"********************* MeetingStart\");\n          changeType = eMeetingEventChangeType.MeetingStart;\n        }\n        else if (eMeetingEventChangeType.MeetingEndWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEndWarning) && m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingEnd.Seconds > 0)    // Meeting is about to end\n        {\n          Debug.LogMessage(LogEventLevel.Verbose, \"********************* MeetingEndWarning. TotalMinutes: {0}  Seconds: {1}\", m.TimeToMeetingEnd.TotalMinutes, m.TimeToMeetingEnd.Seconds);\n          changeType = eMeetingEventChangeType.MeetingEndWarning;\n        }\n        else if (eMeetingEventChangeType.MeetingEnd != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEnd) && Math.Abs(m.TimeToMeetingEnd.TotalMinutes) < meetingTimeEpsilon)             // Meeting has ended\n        {\n          Debug.LogMessage(LogEventLevel.Verbose, \"********************* MeetingEnd\");\n          changeType = eMeetingEventChangeType.MeetingEnd;\n        }\n\n        if (changeType != eMeetingEventChangeType.Unknown)\n        {\n          OnMeetingChange(changeType, m);\n        }\n      }\n\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/ContactMethod.cs",
    "content": "using Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a ContactMethod\n  /// </summary>\n  public class ContactMethod\n  {\n    /// <summary>\n    /// Gets or sets the ContactMethodId\n    /// </summary>\n    [JsonProperty(\"contactMethodId\")]\n    public string ContactMethodId { get; set; }\n\n    /// <summary>\n    /// Gets or sets the Number\n    /// </summary>\n    [JsonProperty(\"number\")]\n    public string Number { get; set; }\n\n    /// <summary>\n    /// Gets or sets the Device\n    /// </summary>\n    [JsonProperty(\"device\")]\n    [JsonConverter(typeof(StringEnumConverter))]\n    public eContactMethodDevice Device { get; set; }\n\n    /// <summary>\n    /// Gets or sets the CallType\n    /// </summary>\n    [JsonProperty(\"callType\")]\n    [JsonConverter(typeof(StringEnumConverter))]\n    public eContactMethodCallType CallType { get; set; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/DirectoryContact.cs",
    "content": "using System.Collections.Generic;\n\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a DirectoryContact\n  /// </summary>\n  public class DirectoryContact : DirectoryItem\n  {\n\n    /// <summary>\n    /// Gets or sets the ContactId\n    /// </summary>\n    [JsonProperty(\"contactId\")]\n    public string ContactId { get; set; }\n\n    /// <summary>\n    /// Gets or sets the Title\n    /// </summary>\n    [JsonProperty(\"title\")]\n    public string Title { get; set; }\n\n    /// <summary>\n    /// Gets or sets the ContactMethods\n    /// </summary>\n    [JsonProperty(\"contactMethods\")]\n    public List<ContactMethod> ContactMethods { get; set; }\n\n    /// <summary>\n    /// Constructor for <see cref=\"DirectoryContact\"/>\n    /// </summary>\n    public DirectoryContact()\n    {\n      ContactMethods = new List<ContactMethod>();\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/DirectoryEventArgs.cs",
    "content": "\n\nusing System;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a DirectoryEventArgs\n  /// </summary>\n  public class DirectoryEventArgs : EventArgs\n  {\n    /// <summary>\n    /// Gets or sets the Directory\n    /// </summary>\n    public CodecDirectory Directory { get; set; }\n    /// <summary>\n    /// Gets or sets the DirectoryIsOnRoot\n    /// </summary>\n    public bool DirectoryIsOnRoot { get; set; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/DirectoryFolder.cs",
    "content": "using System.Collections.Generic;\n\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a DirectoryFolder\n  /// </summary>\n  public class DirectoryFolder : DirectoryItem\n  {\n\n    /// <summary>\n    /// Gets or sets the Contacts\n    /// </summary>\n    [JsonProperty(\"contacts\")]\n    public List<DirectoryContact> Contacts { get; set; }\n\n    /// <summary>\n    /// Constructor for <see cref=\"DirectoryFolder\"/>\n    /// </summary>\n    public DirectoryFolder()\n    {\n      Contacts = new List<DirectoryContact>();\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/DirectoryItem.cs",
    "content": "\n\nusing System;\n\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a DirectoryItem\n  /// </summary>\n  public class DirectoryItem : ICloneable\n  {\n    /// <summary>\n    /// Clone method\n    /// </summary>\n    public object Clone()\n    {\n      return MemberwiseClone();\n    }\n\n    /// <summary>\n    /// Gets or sets the FolderId\n    /// </summary>\n    [JsonProperty(\"folderId\")]\n    public string FolderId { get; set; }\n\n\n    /// <summary>\n    /// Gets or sets the Name\n    /// </summary>\n    [JsonProperty(\"name\")]\n    public string Name { get; set; }\n\n    /// <summary>\n    /// Gets or sets the ParentFolderId\n    /// </summary>\n    [JsonProperty(\"parentFolderId\")]\n    public string ParentFolderId { get; set; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/ICiscoCodecCameraConfig.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Describes a cisco codec device that can allow configuration of cameras\n    /// </summary>\n    public interface ICiscoCodecCameraConfig\n    {\n        /// <summary>\n        /// Sets the assigned serial number for the specified camera\n        /// </summary>\n        /// <param name=\"cameraId\">The camera identifier</param>\n        /// <param name=\"serialNumber\">The serial number to assign</param>\n        void SetCameraAssignedSerialNumber(uint cameraId, string serialNumber);\n\n        /// <summary>\n        /// Sets the name for the camera on the specified video connector\n        /// </summary>\n        /// <param name=\"videoConnectorId\">The video connector identifier</param>\n        /// <param name=\"name\">The name to assign</param>\n        void SetCameraName(uint videoConnectorId, string name);\n\n        /// <summary>\n        /// Sets the input source type for the specified video connector\n        /// </summary>\n        /// <param name=\"videoConnectorId\">The video connector identifier</param>\n        /// <param name=\"sourceType\">The source type to set</param>\n        void SetInputSourceType(uint videoConnectorId, eCiscoCodecInputSourceType sourceType);\n    }\n\n    /// <summary>\n    /// Enumeration of Cisco codec input source types\n    /// </summary>\n    public enum eCiscoCodecInputSourceType\n    {\n        /// <summary>\n        /// PC source type\n        /// </summary>\n        PC,\n\n        /// <summary>\n        /// Camera source type\n        /// </summary>\n        camera,\n\n        /// <summary>\n        /// Document camera source type\n        /// </summary>\n        document_camera,\n\n        /// <summary>\n        /// Media player source type\n        /// </summary>\n        mediaplayer,\n\n        /// <summary>\n        /// Other source type\n        /// </summary>\n        other,\n\n        /// <summary>\n        /// Whiteboard source type\n        /// </summary>\n        whiteboard\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/IHasCallHold.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Defines the contract for IHasCallHold\n    /// </summary>\n    public interface IHasCallHold\n    {\n        /// <summary>\n        /// Put the specified call on hold\n        /// </summary>\n        /// <param name=\"activeCall\"></param>\n        void HoldCall(CodecActiveCallItem activeCall);\n\n        /// <summary>\n        /// Resume the specified call\n        /// </summary>\n        /// <param name=\"activeCall\"></param>\n        void ResumeCall(CodecActiveCallItem activeCall);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/IHasDirectoryHistoryStack.cs",
    "content": "using System.Collections.Generic;\nusing PepperDash.Essentials.Devices.Common.Codec;\n\n\n/// <summary>\n/// Defines the contract for IHasDirectoryHistoryStack\n/// </summary>\npublic interface IHasDirectoryHistoryStack : IHasDirectory\n{\n  /// <summary>\n  /// Gets the DirectoryBrowseHistoryStack\n  /// </summary>\n  Stack<CodecDirectory> DirectoryBrowseHistoryStack { get; }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/IHasDoNotDisturb.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Describes a device that has Do Not Disturb mode capability\n    /// </summary>\n    public interface IHasDoNotDisturbMode\n    {\n        /// <summary>\n        /// Indictes whether Do Not Disturb mode is on (Activated)\n        /// </summary>\n        BoolFeedback DoNotDisturbModeIsOnFeedback { get; }\n\n        /// <summary>\n        /// Activates Do Not Disturb mode\n        /// </summary>\n        void ActivateDoNotDisturbMode();\n\n        /// <summary>\n        /// Deactivates Do Not Disturb mode\n        /// </summary>\n        void DeactivateDoNotDisturbMode();\n\n        /// <summary>\n        /// Toggles Do Not Disturb mode\n        /// </summary>\n        void ToggleDoNotDisturbMode();\n    }\n\n    /// <summary>\n    /// Defines the contract for devices that support Do Not Disturb mode with timeout functionality\n    /// </summary>\n    public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode\n    {\n        /// <summary>\n        /// Activates Do Not Disturb mode with a timeout\n        /// </summary>\n        /// <param name=\"timeout\"></param>\n        void ActivateDoNotDisturbMode(int timeout);\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/IHasExternalSourceSwitching.cs",
    "content": "﻿using System;\nusing PepperDash.Essentials.Devices.Common.VideoCodec.Cisco;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n\t/// <summary>\n\t/// Defines the contract for IHasExternalSourceSwitching\n\t/// </summary>\n\tpublic interface IHasExternalSourceSwitching\n\t{\n\t\t/// <summary>\n\t\t/// Gets a value indicating whether the external source list is enabled\n\t\t/// </summary>\n\t\tbool ExternalSourceListEnabled { get; }\n\n\t\t/// <summary>\n\t\t/// Gets the external source input port identifier\n\t\t/// </summary>\n\t\tstring ExternalSourceInputPort { get; }\n\n\t\t/// <summary>\n\t\t/// Adds an external source to the available sources\n\t\t/// </summary>\n\t\t/// <param name=\"connectorId\">The connector identifier</param>\n\t\t/// <param name=\"key\">The unique key for the source</param>\n\t\t/// <param name=\"name\">The display name for the source</param>\n\t\t/// <param name=\"type\">The type of external source</param>\n\t\tvoid AddExternalSource(string connectorId, string key, string name, eExternalSourceType type);\n\n\t\t/// <summary>\n\t\t/// Sets the state of the specified external source\n\t\t/// </summary>\n\t\t/// <param name=\"key\">The unique key of the external source</param>\n\t\t/// <param name=\"mode\">The mode to set for the source</param>\n\t\tvoid SetExternalSourceState(string key, eExternalSourceMode mode);\n\n\t\t/// <summary>\n\t\t/// Clears all external sources from the list\n\t\t/// </summary>\n\t\tvoid ClearExternalSources();\n\n\t\t/// <summary>\n\t\t/// Sets the selected source by its key\n\t\t/// </summary>\n\t\t/// <param name=\"key\">The unique key of the source to select</param>\n\t\tvoid SetSelectedSource(string key);\n\n\t\t/// <summary>\n\t\t/// Sets the action to run when routing between sources\n\t\t/// </summary>\n\t\tAction<string, string> RunRouteAction { set; }\n\t}\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/IInvitableContact.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Defines the contract for IInvitableContact\n  /// </summary>\n  public interface IInvitableContact\n  {\n    /// <summary>\n    /// Gets a value indicating whether this contact is invitable\n    /// </summary>\n    bool IsInvitableContact { get; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/InvitableDirectoryContact.cs",
    "content": "using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents an InvitableDirectoryContact\n  /// </summary>\n  public class InvitableDirectoryContact : DirectoryContact, IInvitableContact\n  {\n    /// <summary>\n    /// Gets a value indicating whether this contact is invitable\n    /// </summary>\n    [JsonProperty(\"isInvitableContact\")]\n    public bool IsInvitableContact\n    {\n      get\n      {\n        return this is IInvitableContact;\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/Meeting.cs",
    "content": "\n\nusing System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a Meeting\n  /// </summary>\n  public class Meeting\n  {\n    /// <summary>\n    /// Minutes before the meeting to show warning\n    /// </summary>\n    [JsonProperty(\"minutesBeforeMeeting\")]\n    public int MinutesBeforeMeeting;\n\n    /// <summary>\n    /// Gets or sets the meeting ID\n    /// </summary>\n    [JsonProperty(\"id\")]\n    public string Id { get; set; }\n    /// <summary>\n    /// Gets or sets the meeting organizer\n    /// </summary>\n    [JsonProperty(\"organizer\")]\n    public string Organizer { get; set; }\n    /// <summary>\n    /// Gets or sets the Title\n    /// </summary>\n    [JsonProperty(\"title\")]\n    public string Title { get; set; }\n    /// <summary>\n    /// Gets or sets the Agenda\n    /// </summary>\n    [JsonProperty(\"agenda\")]\n    public string Agenda { get; set; }\n\n    /// <summary>\n    /// Gets the meeting warning time span in minutes before the meeting starts\n    /// </summary>\n    [JsonProperty(\"meetingWarningMinutes\")]\n    public TimeSpan MeetingWarningMinutes\n    {\n      get { return TimeSpan.FromMinutes(MinutesBeforeMeeting); }\n    }\n    /// <summary>\n    /// Gets the time remaining until the meeting starts\n    /// </summary>\n    [JsonProperty(\"timeToMeetingStart\")]\n    public TimeSpan TimeToMeetingStart\n    {\n      get\n      {\n        return StartTime - DateTime.Now;\n      }\n    }\n    /// <summary>\n    /// Gets the time remaining until the meeting ends\n    /// </summary>\n    [JsonProperty(\"timeToMeetingEnd\")]\n    public TimeSpan TimeToMeetingEnd\n    {\n      get\n      {\n        return EndTime - DateTime.Now;\n      }\n    }\n    /// <summary>\n    /// Gets or sets the StartTime\n    /// </summary>\n    [JsonProperty(\"startTime\")]\n    public DateTime StartTime { get; set; }\n    /// <summary>\n    /// Gets or sets the EndTime\n    /// </summary>\n    [JsonProperty(\"endTime\")]\n    public DateTime EndTime { get; set; }\n    /// <summary>\n    /// Gets the duration of the meeting\n    /// </summary>\n    [JsonProperty(\"duration\")]\n    public TimeSpan Duration\n    {\n      get\n      {\n        return EndTime - StartTime;\n      }\n    }\n    /// <summary>\n    /// Gets or sets the Privacy\n    /// </summary>\n    [JsonProperty(\"privacy\")]\n    public eMeetingPrivacy Privacy { get; set; }\n    /// <summary>\n    /// Gets a value indicating whether the meeting can be joined\n    /// </summary>\n    [JsonProperty(\"joinable\")]\n    public bool Joinable\n    {\n      get\n      {\n        var joinable = StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now\n            && DateTime.Now <= EndTime.AddSeconds(-_joinableCooldownSeconds);\n        //Debug.LogMessage(LogEventLevel.Verbose, \"Meeting Id: {0} joinable: {1}\", Id, joinable);\n        return joinable;\n      }\n    }\n\n    /// <summary>\n    /// Gets or sets the Dialable\n    /// </summary>\n    [JsonProperty(\"dialable\")]\n    public bool Dialable { get; set; }\n\n    //public string ConferenceNumberToDial { get; set; }\n\n    /// <summary>\n    /// Gets or sets the ConferencePassword\n    /// </summary>\n    [JsonProperty(\"conferencePassword\")]\n    public string ConferencePassword { get; set; }\n\n    /// <summary>\n    /// Gets or sets the IsOneButtonToPushMeeting\n    /// </summary>\n    [JsonProperty(\"isOneButtonToPushMeeting\")]\n    public bool IsOneButtonToPushMeeting { get; set; }\n\n    /// <summary>\n    /// Gets or sets the Calls\n    /// </summary>\n    [JsonProperty(\"calls\")]\n    public List<Call> Calls { get; private set; }\n\n    /// <summary>\n    /// Tracks the change types that have already been notified for\n    /// Gets or sets the NotifiedChangeTypes\n    /// </summary>\n    [JsonIgnore]\n    public eMeetingEventChangeType NotifiedChangeTypes { get; set; }\n\n    [JsonIgnore] private readonly int _joinableCooldownSeconds;\n\n    /// <summary>\n    /// Constructor for Meeting <see cref=\"Meeting\"/>\n    /// </summary>\n    public Meeting()\n    {\n      Calls = new List<Call>();\n      _joinableCooldownSeconds = 300;\n    }\n\n    /// <summary>\n    /// Constructor for Meeting <see cref=\"Meeting\"/>\n    /// </summary>\n    /// <param name=\"joinableCooldownSeconds\">Number of seconds after meeting start when it is no longer joinable</param>\n    public Meeting(int joinableCooldownSeconds)\n    {\n      Calls = new List<Call>();\n      _joinableCooldownSeconds = joinableCooldownSeconds;\n    }\n\n\n\n    #region Overrides of Object\n\n    /// <summary>\n    /// ToString method\n    /// </summary>\n    /// <inheritdoc />\n    public override string ToString()\n    {\n      return string.Format(\"{0}:{1}: {2}-{3}\", Title, Agenda, StartTime, EndTime);\n    }\n\n    #endregion\n  }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/MeetingEventArgs.cs",
    "content": "\n\nusing System;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Represents a MeetingEventArgs\n  /// </summary>\n  public class MeetingEventArgs : EventArgs\n  {\n    /// <summary>\n    /// Gets or sets the ChangeType\n    /// </summary>\n    public eMeetingEventChangeType ChangeType { get; set; }\n    /// <summary>\n    /// Gets or sets the Meeting\n    /// </summary>\n    public Meeting Meeting { get; set; }\n  }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/eCodecCallDirection.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.Codec\n\n{\n    /// <summary>\n    /// Enumeration of eCodecCallDirection values\n    /// </summary>\n    public enum eCodecCallDirection\n    {\n        /// <summary>\n        /// Unknown call direction\n        /// </summary>\n        Unknown = 0,\n\n        /// <summary>\n        /// Incoming call direction\n        /// </summary>\n        Incoming,\n\n        /// <summary>\n        /// Outgoing call direction\n        /// </summary>\n        Outgoing\n    }\n\n    /// <summary>\n    /// Represents a CodecCallDirection\n    /// </summary>\n    public class CodecCallDirection\n    {\n        /// <summary>\n        /// Takes the Cisco call type and converts to the matching enum\n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// ConvertToDirectionEnum method\n        /// </summary>\n        public static eCodecCallDirection ConvertToDirectionEnum(string s)\n        {\n            switch (s.ToLower())\n            {\n                case \"incoming\":\n                    {\n                        return eCodecCallDirection.Incoming;\n                    }\n                case \"outgoing\":\n                    {\n                        return eCodecCallDirection.Outgoing;\n                    }\n                default:\n                    return eCodecCallDirection.Unknown;\n            }\n\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/eCodecCallStatus.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Enumeration of eCodecCallStatus values\n    /// </summary>\n    public enum eCodecCallStatus\n    {\n        /// <summary>\n        /// Unknown call status\n        /// </summary>\n        Unknown = 0,\n\n        /// <summary>\n        /// Call is connected\n        /// </summary>\n        Connected,\n\n        /// <summary>\n        /// Call is connecting\n        /// </summary>\n        Connecting,\n\n        /// <summary>\n        /// Call is dialing\n        /// </summary>\n        Dialing,\n\n        /// <summary>\n        /// Call is disconnected\n        /// </summary>\n        Disconnected,\n\n        /// <summary>\n        /// Call is disconnecting\n        /// </summary>\n        Disconnecting,\n\n        /// <summary>\n        /// Early media is being sent/received\n        /// </summary>\n        EarlyMedia,\n\n        /// <summary>\n        /// Call is idle\n        /// </summary>\n        Idle,\n\n        /// <summary>\n        /// Call is on hold\n        /// </summary>\n        OnHold,\n\n        /// <summary>\n        /// Call is ringing\n        /// </summary>\n        Ringing,\n\n        /// <summary>\n        /// Call is preserved\n        /// </summary>\n        Preserved,\n\n        /// <summary>\n        /// Call is remote preserved\n        /// </summary>\n        RemotePreserved,\n    }\n\n\n    /// <summary>\n    /// Represents a CodecCallStatus\n    /// </summary>\n    public class CodecCallStatus\n    {\n\n        /// <summary>\n        /// Takes the Cisco call type and converts to the matching enum\n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// ConvertToStatusEnum method\n        /// </summary>\n        public static eCodecCallStatus ConvertToStatusEnum(string s)\n        {\n            switch (s)\n            {\n                case \"Connected\":\n                    {\n                        return eCodecCallStatus.Connected;\n                    }\n                case \"Connecting\":\n                    {\n                        return eCodecCallStatus.Connecting;\n                    }\n                case \"Dialling\":\n                    {\n                        return eCodecCallStatus.Dialing;\n                    }\n                case \"Disconnected\":\n                    {\n                        return eCodecCallStatus.Disconnected;\n                    }\n                case \"Disconnecting\":\n                    {\n                        return eCodecCallStatus.Disconnecting;\n                    }\n                case \"EarlyMedia\":\n                    {\n                        return eCodecCallStatus.EarlyMedia;\n                    }\n                case \"Idle\":\n                    {\n                        return eCodecCallStatus.Idle;\n                    }\n                case \"OnHold\":\n                    {\n                        return eCodecCallStatus.OnHold;\n                    }\n                case \"Ringing\":\n                    {\n                        return eCodecCallStatus.Ringing;\n                    }\n                case \"Preserved\":\n                    {\n                        return eCodecCallStatus.Preserved;\n                    }\n                case \"RemotePreserved\":\n                    {\n                        return eCodecCallStatus.RemotePreserved;\n                    }\n                default:\n                    return eCodecCallStatus.Unknown;\n            }\n\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/eCodecCallType.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.Codec\n\n{\n    /// <summary>\n    /// Enumeration of eCodecCallType values\n    /// </summary>\n    public enum eCodecCallType\n    {\n        /// <summary>\n        /// Unknown call type\n        /// </summary>\n        Unknown = 0,\n\n        /// <summary>\n        /// Audio-only call type\n        /// </summary>\n        Audio,\n\n        /// <summary>\n        /// Video call type\n        /// </summary>\n        Video,\n\n        /// <summary>\n        /// Audio call that can be escalated to video\n        /// </summary>\n        AudioCanEscalate,\n\n        /// <summary>\n        /// Forward all call type\n        /// </summary>\n        ForwardAllCall\n    }\n\n    /// <summary>\n    /// Represents a CodecCallType\n    /// </summary>\n    public class CodecCallType\n    {\n\n        /// <summary>\n        /// Takes the Cisco call type and converts to the matching enum\n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// ConvertToTypeEnum method\n        /// </summary>\n        public static eCodecCallType ConvertToTypeEnum(string s)\n        {\n            switch (s)\n            {\n                case \"Audio\":\n                    {\n                        return eCodecCallType.Audio;\n                    }\n                case \"Video\":\n                    {\n                        return eCodecCallType.Video;\n                    }\n                case \"AudioCanEscalate\":\n                    {\n                        return eCodecCallType.AudioCanEscalate;\n                    }\n                case \"ForwardAllCall\":\n                    {\n                        return eCodecCallType.ForwardAllCall;\n                    }\n                default:\n                    return eCodecCallType.Unknown;\n            }\n\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/eContactMethodCallType.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Enumeration of eContactMethodCallType values\n  /// </summary>\n  public enum eContactMethodCallType\n  {\n    /// <summary>\n    /// Unknown call type\n    /// </summary>\n    Unknown = 0,\n    /// <summary>\n    /// Audio call type\n    /// </summary>\n    Audio,\n    /// <summary>\n    /// Video call type\n    /// </summary>\n    Video\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/eContactMethodDevice.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Enumeration of eContactMethodDevice values\n  /// </summary>\n  public enum eContactMethodDevice\n  {\n    /// <summary>\n    /// Unknown contact method\n    /// </summary>\n    Unknown = 0,\n    /// <summary>\n    /// Mobile contact method\n    /// </summary>\n    Mobile,\n    /// <summary>\n    /// Other contact method\n    /// </summary>\n    Other,\n    /// <summary>\n    /// Telephone contact method\n    /// </summary>\n    Telephone,\n    /// <summary>\n    /// Video contact method\n    /// </summary>\n    Video\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/eMeetingEventChangeType.cs",
    "content": "\n\nusing System;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n  /// <summary>\n  /// Enumeration of eMeetingEventChangeType values\n  /// </summary>\n  [Flags]\n  public enum eMeetingEventChangeType\n  {\n    /// <summary>\n    /// Unknown change type\n    /// </summary>\n    Unknown = 0,\n    /// <summary>\n    /// Meeting start warning\n    /// </summary>\n    MeetingStartWarning = 1,\n    /// <summary>\n    /// Meeting start\n    /// </summary>\n    MeetingStart = 2,\n    /// <summary>\n    /// Meeting end warning\n    /// </summary>\n    MeetingEndWarning = 4,\n    /// <summary>\n    /// Meeting end\n    /// </summary>\n    MeetingEnd = 8\n  }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/eMeetingPrivacy.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Enumeration of eMeetingPrivacy values\n    /// </summary>\n    public enum eMeetingPrivacy\n    {\n        /// <summary>\n        /// Unknown meeting privacy level\n        /// </summary>\n        Unknown = 0,\n\n        /// <summary>\n        /// Public meeting\n        /// </summary>\n        Public,\n\n        /// <summary>\n        /// Private meeting\n        /// </summary>\n        Private\n    }\n\n    /// <summary>\n    /// Represents a CodecCallPrivacy\n    /// </summary>\n    public class CodecCallPrivacy\n    {\n        /// <summary>\n        /// Takes the Cisco privacy type and converts to the matching enum\n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// ConvertToDirectionEnum method\n        /// </summary>\n        public static eMeetingPrivacy ConvertToDirectionEnum(string s)\n        {\n            switch (s.ToLower())\n            {\n                case \"public\":\n                    {\n                        return eMeetingPrivacy.Public;\n                    }\n                case \"private\":\n                    {\n                        return eMeetingPrivacy.Private;\n                    }\n                default:\n                    return eMeetingPrivacy.Unknown;\n            }\n\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/iCodecAudio.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Defines minimum volume controls for a codec device with dialing capabilities\n    /// </summary>\n    public interface ICodecAudio : IBasicVolumeWithFeedback, IPrivacy\n    {\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/iHasCallFavorites.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Defines the contract for IHasCallFavorites\n    /// </summary>\n    public interface IHasCallFavorites\n    {\n        /// <summary>\n        /// Gets the call favorites for this device\n        /// </summary>\n        CodecCallFavorites CallFavorites { get; }\n    }\n\n    /// <summary>\n    /// Represents a CodecCallFavorites\n    /// </summary>\n    public class CodecCallFavorites\n    {\n        /// <summary>\n        /// Gets or sets the Favorites\n        /// </summary>\n        public List<CodecActiveCallItem> Favorites { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the CodecCallFavorites class\n        /// </summary>\n        public CodecCallFavorites()\n        {\n            Favorites = new List<CodecActiveCallItem>();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/iHasCallHistory.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing PepperDash.Essentials.Devices.Common.VideoCodec;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n\n    /// <summary>\n    /// Defines the contract for IHasCallHistory\n    /// </summary>\n    public interface IHasCallHistory\n    {\n        /// <summary>\n        /// Gets the call history for this device\n        /// </summary>\n        CodecCallHistory CallHistory { get; }\n\n        /// <summary>\n        /// Removes the specified call history entry\n        /// </summary>\n        /// <param name=\"entry\">The call history entry to remove</param>\n        void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry);\n    }\n\n    /// <summary>\n    /// Enumeration of eCodecOccurrenceType values\n    /// </summary>\n    public enum eCodecOccurrenceType\n    {\n        /// <summary>\n        /// Unknown occurrence type\n        /// </summary>\n        Unknown = 0,\n\n        /// <summary>\n        /// Call was placed (outgoing)\n        /// </summary>\n        Placed = 1,\n\n        /// <summary>\n        /// Call was received (incoming)\n        /// </summary>\n        Received = 2,\n\n        /// <summary>\n        /// Call received no answer\n        /// </summary>\n        NoAnswer = 3,\n    }\n\n    /// <summary>\n    /// Represents a CodecCallHistory\n    /// </summary>\n    public class CodecCallHistory\n    {\n        /// <summary>\n        /// Event that is raised when the recent calls list has changed\n        /// </summary>\n        public event EventHandler<EventArgs> RecentCallsListHasChanged;\n\n        /// <summary>\n        /// Gets or sets the RecentCalls\n        /// </summary>\n        public List<CallHistoryEntry> RecentCalls { get; private set; }\n\n        /// <summary>\n        /// Item that gets added to the list when there are no recent calls in history\n        /// </summary>\n        CallHistoryEntry ListEmptyEntry;\n\n        /// <summary>\n        /// Initializes a new instance of the CodecCallHistory class\n        /// </summary>\n        public CodecCallHistory()\n        {\n            ListEmptyEntry = new CallHistoryEntry() { Name = \"No Recent Calls\" };\n\n            RecentCalls = new List<CallHistoryEntry>();\n\n            RecentCalls.Add(ListEmptyEntry);\n        }\n\n        void OnRecentCallsListChange()\n        {\n            var handler = RecentCallsListHasChanged;\n            if (handler != null)\n            {\n                handler(this, new EventArgs());\n            }\n        }\n\n        /// <summary>\n        /// RemoveEntry method\n        /// </summary>\n        public void RemoveEntry(CallHistoryEntry entry)\n        {\n            RecentCalls.Remove(entry);\n            OnRecentCallsListChange();\n        }\n\n        /// <summary>\n        /// Represents a CallHistoryEntry\n        /// </summary>\n        public class CallHistoryEntry : CodecActiveCallItem\n        {\n            /// <summary>\n            /// Gets or sets the StartTime\n            /// </summary>\n            [JsonConverter(typeof(IsoDateTimeConverter))]\n            [JsonProperty(\"startTime\")]\n            public DateTime StartTime { get; set; }\n            /// <summary>\n            /// Gets or sets the occurrence type for this call history entry\n            /// </summary>\n            [JsonConverter(typeof(StringEnumConverter))]\n            [JsonProperty(\"occurrenceType\")]\n            public eCodecOccurrenceType OccurrenceType { get; set; }\n\n            /// <summary>\n            /// Gets or sets the occurrence history identifier\n            /// </summary>\n            [JsonProperty(\"occurrenceHistoryId\")]\n            public string OccurrenceHistoryId { get; set; }\n        }\n\n        /// <summary>\n        /// Converts a list of call history entries returned by a Cisco codec to the generic list type\n        /// </summary>\n        /// <param name=\"entries\"></param>\n        /// <returns></returns>\n        /// <summary>\n        /// ConvertCiscoCallHistoryToGeneric method\n        /// </summary>\n        public void ConvertCiscoCallHistoryToGeneric(List<CiscoCallHistory.Entry> entries)\n        {\n            var genericEntries = new List<CallHistoryEntry>();\n\n            foreach (CiscoCallHistory.Entry entry in entries)\n            {\n\n                genericEntries.Add(new CallHistoryEntry()\n                {\n                    Name = entry.DisplayName.Value,\n                    Number = entry.CallbackNumber.Value,\n                    StartTime = entry.LastOccurrenceStartTime.Value,\n                    OccurrenceHistoryId = entry.LastOccurrenceHistoryId.Value,\n                    OccurrenceType = ConvertToOccurenceTypeEnum(entry.OccurrenceType.Value)\n                });\n            }\n\n            // Check if list is empty and if so, add an item to display No Recent Calls\n            if (genericEntries.Count == 0)\n                genericEntries.Add(ListEmptyEntry);\n\n            RecentCalls = genericEntries;\n            OnRecentCallsListChange();\n        }\n\n        /// <summary>\n        /// Takes the Cisco occurence type and converts it to the matching enum\n        /// </summary>\n        /// <param name=\"s\"></param>\n        /// <summary>\n        /// ConvertToOccurenceTypeEnum method\n        /// </summary>\n        public eCodecOccurrenceType ConvertToOccurenceTypeEnum(string s)\n        {\n            switch (s)\n            {\n                case \"Placed\":\n                    {\n                        return eCodecOccurrenceType.Placed;\n                    }\n                case \"Received\":\n                    {\n                        return eCodecOccurrenceType.Received;\n                    }\n                case \"NoAnswer\":\n                    {\n                        return eCodecOccurrenceType.NoAnswer;\n                    }\n                default:\n                    return eCodecOccurrenceType.Unknown;\n            }\n\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/iHasContentSharing.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Defines the contract for IHasContentSharing\n    /// </summary>\n    public interface IHasContentSharing\n    {\n        /// <summary>\n        /// Gets feedback indicating whether content sharing is currently active\n        /// </summary>\n        BoolFeedback SharingContentIsOnFeedback { get; }\n\n        /// <summary>\n        /// Gets feedback about the current sharing source\n        /// </summary>\n        StringFeedback SharingSourceFeedback { get; }\n\n        /// <summary>\n        /// Gets a value indicating whether content should be automatically shared while in a call\n        /// </summary>\n        bool AutoShareContentWhileInCall { get; }\n\n        /// <summary>\n        /// Starts content sharing\n        /// </summary>\n        void StartSharing();\n\n        /// <summary>\n        /// Stops content sharing\n        /// </summary>\n        void StopSharing();\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/iHasDialer.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Requirements for a device that has dialing capabilities\n    /// </summary>\n    public interface IHasDialer\n    {\n        // Add requirements for Dialer functionality\n\n        /// <summary>\n        /// Event that is raised when call status changes\n        /// </summary>\n        event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;\n\n        /// <summary>\n        /// Dials the specified number\n        /// </summary>\n        /// <param name=\"number\">The number to dial</param>\n        void Dial(string number);\n\n        /// <summary>\n        /// Ends the specified active call\n        /// </summary>\n        /// <param name=\"activeCall\">The active call to end</param>\n        void EndCall(CodecActiveCallItem activeCall);\n\n        /// <summary>\n        /// Ends all active calls\n        /// </summary>\n        void EndAllCalls();\n\n        /// <summary>\n        /// Accepts the specified incoming call\n        /// </summary>\n        /// <param name=\"item\">The call item to accept</param>\n        void AcceptCall(CodecActiveCallItem item);\n\n        /// <summary>\n        /// Rejects the specified incoming call\n        /// </summary>\n        /// <param name=\"item\">The call item to reject</param>\n        void RejectCall(CodecActiveCallItem item);\n\n        /// <summary>\n        /// Sends DTMF digits during a call\n        /// </summary>\n        /// <param name=\"digit\">The DTMF digit(s) to send</param>\n        void SendDtmf(string digit);\n\n        /// <summary>\n        /// Gets a value indicating whether the device is currently in a call\n        /// </summary>\n        bool IsInCall { get; }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/iHasDirectory.cs",
    "content": "﻿\n\nusing System;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Devices.Common.VideoCodec;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Defines the API for codecs with a directory\n    /// </summary>\n    public interface IHasDirectory\n    {\n        /// <summary>\n        /// Event that fires when a directory result is returned from the codec\n        /// </summary>\n        event EventHandler<DirectoryEventArgs> DirectoryResultReturned;\n\n        /// <summary>\n        /// Gets the DirectoryRoot\n        /// </summary>\n        CodecDirectory DirectoryRoot { get; }\n\n        /// <summary>\n        /// Gets the CurrentDirectoryResult\n        /// </summary>\n        CodecDirectory CurrentDirectoryResult { get; }\n\n        /// <summary>\n        /// Gets the PhonebookSyncState\n        /// </summary>\n        CodecPhonebookSyncState PhonebookSyncState { get; }\n\n        /// <summary>\n        /// Method to initiate a search of the directory on the server\n        /// </summary>\n        void SearchDirectory(string searchString);\n\n        /// <summary>\n        /// Method to get the contents of a specific folder in the directory on the server\n        /// </summary>\n        void GetDirectoryFolderContents(string folderId);\n\n        /// <summary>\n        /// Method to set the current directory to the root folder\n        /// </summary>\n        void SetCurrentDirectoryToRoot();\n\n        /// <summary>\n        /// Method to get the contents of the parent folder in the directory on the server\n        /// </summary>\n        void GetDirectoryParentFolderContents();\n\n        /// <summary>\n        /// Gets the CurrentDirectoryResultIsNotDirectoryRoot\n        /// </summary>\n        BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Codec/iHasScheduleAwareness.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.Codec\n{\n\n    /// <summary>\n    /// Defines the contract for IHasScheduleAwareness\n    /// </summary>\n    public interface IHasScheduleAwareness\n    {\n        /// <summary>\n        /// Gets the CodecScheduleAwareness instance\n        /// </summary>\n        CodecScheduleAwareness CodecSchedule { get; }\n\n        /// <summary>\n        /// Method to initiate getting the schedule from the server\n        /// </summary>\n        void GetSchedule();\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/DSP/DspBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.Devices.Common.DSP\n{\n    /// <summary>\n    /// Base class for DSP devices\n    /// </summary>\n    public abstract class DspBase : EssentialsDevice, ILevelControls\n    {\n        /// <summary>\n        /// Gets the collection of level control points\n        /// </summary>\n        public Dictionary<string, IBasicVolumeWithFeedback> LevelControlPoints { get; private set; }\n\n        /// <summary>\n        /// Gets the collection of dialer control points\n        /// </summary>\n        public Dictionary<string, DspControlPoint> DialerControlPoints { get; private set; }\n\n        /// <summary>\n        /// Gets the collection of switcher control points\n        /// </summary>\n        public Dictionary<string, DspControlPoint> SwitcherControlPoints { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the DspBase class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        public DspBase(string key, string name) :\n                base(key, name)\n        {\n\n            LevelControlPoints = new Dictionary<string, IBasicVolumeWithFeedback>();\n            DialerControlPoints = new Dictionary<string, DspControlPoint>();\n            SwitcherControlPoints = new Dictionary<string, DspControlPoint>();\n        }\n\n\n        // in audio call feedback\n\n        // VOIP\n        // Phone dialer\n\n    }\n\n    // Fusion\n    // Privacy state\n    // Online state\n    // level/mutes ?\n\n    // AC Log call stats\n\n    // Typical presets:\n    // call default preset to restore levels and mutes\n\n    /// <summary>\n    /// Base class for DSP control points\n    /// </summary>\n    public abstract class DspControlPoint : IKeyName\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; }\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the DspControlPoint class\n        /// </summary>\n        /// <param name=\"key\">The control point key</param>\n        protected DspControlPoint(string key) => Key = key;\n    }\n\n    /// <summary>\n    /// Base class for DSP level control points with volume and mute functionality\n    /// </summary>\n    public abstract class DspLevelControlPoint : DspControlPoint, IBasicVolumeWithFeedback\n    {\n        /// <summary>\n        /// Gets or sets the MuteFeedback\n        /// </summary>\n        public BoolFeedback MuteFeedback { get; }\n        /// <summary>\n        /// Gets or sets the VolumeLevelFeedback\n        /// </summary>\n        public IntFeedback VolumeLevelFeedback { get; }\n\n        /// <summary>\n        /// Initializes a new instance of the DspLevelControlPoint class\n        /// </summary>\n        /// <param name=\"key\">The control point key</param>\n        /// <param name=\"muteFeedbackFunc\">Function to get mute status</param>\n        /// <param name=\"volumeLevelFeedbackFunc\">Function to get volume level</param>\n        protected DspLevelControlPoint(string key, Func<bool> muteFeedbackFunc, Func<int> volumeLevelFeedbackFunc) : base(key)\n        {\n            MuteFeedback = new BoolFeedback(\"mute\", muteFeedbackFunc);\n            VolumeLevelFeedback = new IntFeedback(\"volume\", volumeLevelFeedbackFunc);\n        }\n\n        /// <summary>\n        /// Turns mute off\n        /// </summary>\n        public abstract void MuteOff();\n        /// <summary>\n        /// Turns mute on\n        /// </summary>\n        public abstract void MuteOn();\n        /// <summary>\n        /// Toggles mute state\n        /// </summary>\n        public abstract void MuteToggle();\n        /// <summary>\n        /// Sets the volume level\n        /// </summary>\n        /// <param name=\"level\">The volume level to set</param>\n        public abstract void SetVolume(ushort level);\n        /// <summary>\n        /// Decreases volume\n        /// </summary>\n        /// <param name=\"pressRelease\">True when pressed, false when released</param>\n        public abstract void VolumeDown(bool pressRelease);\n        /// <summary>\n        /// Increases volume\n        /// </summary>\n        /// <param name=\"pressRelease\">True when pressed, false when released</param>\n        public abstract void VolumeUp(bool pressRelease);\n    }\n\n    /// <summary>\n    /// Base class for DSP dialer control points\n    /// </summary>\n    public abstract class DspDialerBase : DspControlPoint\n    {\n        /// <summary>\n        /// Initializes a new instance of the DspDialerBase class\n        /// </summary>\n        /// <param name=\"key\">The dialer control point key</param>\n        protected DspDialerBase(string key) : base(key) { }\n    }\n\n\n    // Main program \n    // VTC \n    // ATC\n    // Mics, unusual\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/DeviceFactory.cs",
    "content": "﻿\n\nusing System;\nusing System.Linq;\nusing System.Reflection;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n    /// <summary>\n    /// Represents a DeviceFactory\n    /// </summary>\n    public class DeviceFactory\n    {\n        /// <summary>\n        /// Initializes a new instance of the DeviceFactory class\n        /// </summary>\n        public DeviceFactory()\n        {\n            var assy = Assembly.GetExecutingAssembly();\n            PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy);\n\n            var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);\n\n            if (types != null)\n            {\n                foreach (var type in types)\n                {\n                    try\n                    {\n                        var factory = (IDeviceFactory)Activator.CreateInstance(type);\n                        LoadDeviceFactories(factory);\n                    }\n                    catch (Exception e)\n                    {\n                        Debug.LogMessage(LogEventLevel.Information, \"Unable to load type: '{1}' DeviceFactory: {0}\", e, type.Name);\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// Loads device factories from the specified plugin device factory and registers them for use.\n        /// </summary>\n        /// <remarks>This method retrieves metadata from the provided <paramref name=\"deviceFactory\"/>, including\n        /// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type\n        /// names are converted to lowercase for registration.</remarks>\n        /// <param name=\"deviceFactory\">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>\n        private static void LoadDeviceFactories(IDeviceFactory deviceFactory)\n        {\n            foreach (var typeName in deviceFactory.TypeNames)\n            {\n                //Debug.LogMessage(LogEventLevel.Verbose, \"Getting Description Attribute from class: '{0}'\", typeof(T).FullName);\n                string description = (deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0)\n                    ? descriptionAttribute[0].Description\n                    : \"No description available\";\n\n                Core.DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/BasicIrDisplay.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Displays\n{\n\t/// <summary>\n\t/// Represents a BasicIrDisplay\n\t/// </summary>\n\tpublic class BasicIrDisplay : DisplayBase, IBasicVolumeControls, IBridgeAdvanced\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the IrPort\n\t\t/// </summary>\n\t\tpublic IrOutputPortController IrPort { get; private set; }\n\t\t/// <summary>\n\t\t/// Gets or sets the IrPulseTime\n\t\t/// </summary>\n\t\tpublic ushort IrPulseTime { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets the power is on feedback function\n\t\t/// </summary>\n\t\tprotected Func<bool> PowerIsOnFeedbackFunc\n\t\t{\n\t\t\tget { return () => _PowerIsOn; }\n\t\t}\n\t\t/// <summary>\n\t\t/// Gets the is cooling down feedback function\n\t\t/// </summary>\n\t\tprotected override Func<bool> IsCoolingDownFeedbackFunc\n\t\t{\n\t\t\tget { return () => _IsCoolingDown; }\n\t\t}\n\t\t/// <summary>\n\t\t/// Gets the is warming up feedback function\n\t\t/// </summary>\n\t\tprotected override Func<bool> IsWarmingUpFeedbackFunc\n\t\t{\n\t\t\tget { return () => _IsWarmingUp; }\n\t\t}\n\n\t\tbool _PowerIsOn;\n\t\tbool _IsWarmingUp;\n\t\tbool _IsCoolingDown;\n\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the BasicIrDisplay class\n\t\t/// </summary>\n\t\t/// <param name=\"key\">The device key</param>\n\t\t/// <param name=\"name\">The device name</param>\n\t\t/// <param name=\"port\">The IR output port</param>\n\t\t/// <param name=\"irDriverFilepath\">The path to the IR driver file</param>\n\t\tpublic BasicIrDisplay(string key, string name, IROutputPort port, string irDriverFilepath)\n\t\t\t: base(key, name)\n\t\t{\n\t\t\tIrPort = new IrOutputPortController(key + \"-ir\", port, irDriverFilepath);\n\t\t\tDeviceManager.AddDevice(IrPort);\n\n\t\t\tIsWarmingUpFeedback.OutputChange += (o, a) => Debug.LogMessage(LogEventLevel.Verbose, this, \"Warming up={0}\", _IsWarmingUp);\n\t\t\tIsCoolingDownFeedback.OutputChange += (o, a) => Debug.LogMessage(LogEventLevel.Verbose, this, \"Cooling down={0}\", _IsCoolingDown);\n\n\t\t\tInputPorts.AddRange(new RoutingPortCollection<RoutingInputPort>\n\t\t\t{\n\t\t\t\tnew RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n\t\t\t\t\teRoutingPortConnectionType.Hdmi, new Action(Hdmi1), this, false),\n\t\t\t\tnew RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n\t\t\t\t\teRoutingPortConnectionType.Hdmi, new Action(Hdmi2), this, false),\n\t\t\t\tnew RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n\t\t\t\t\teRoutingPortConnectionType.Hdmi, new Action(Hdmi3), this, false),\n\t\t\t\tnew RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n\t\t\t\t\teRoutingPortConnectionType.Hdmi, new Action(Hdmi4), this, false),\n\t\t\t\tnew RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n\t\t\t\t\teRoutingPortConnectionType.Hdmi, new Action(Component1), this, false),\n\t\t\t\tnew RoutingInputPort(RoutingPortNames.CompositeIn, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n\t\t\t\t\teRoutingPortConnectionType.Hdmi, new Action(Video1), this, false),\n\t\t\t\tnew RoutingInputPort(RoutingPortNames.AntennaIn, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n\t\t\t\t\teRoutingPortConnectionType.Hdmi, new Action(Antenna), this, false),\n\t\t\t});\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hdmi1 method\n\t\t/// </summary>\n\t\tpublic void Hdmi1()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_HDMI_1, IrPulseTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hdmi2 method\n\t\t/// </summary>\n\t\tpublic void Hdmi2()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_HDMI_2, IrPulseTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hdmi3 method\n\t\t/// </summary>\n\t\tpublic void Hdmi3()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_HDMI_3, IrPulseTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hdmi4 method\n\t\t/// </summary>\n\t\tpublic void Hdmi4()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_HDMI_4, IrPulseTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Component1 method\n\t\t/// </summary>\n\t\tpublic void Component1()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_COMPONENT_1, IrPulseTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Video1 method\n\t\t/// </summary>\n\t\tpublic void Video1()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_VIDEO_1, IrPulseTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Antenna method\n\t\t/// </summary>\n\t\tpublic void Antenna()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_ANTENNA, IrPulseTime);\n\t\t}\n\n\t\t#region IPower Members\n\n\t\t/// <summary>\n\t\t/// PowerOn method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void PowerOn()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_POWER_ON, IrPulseTime);\n\t\t\t_PowerIsOn = true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// PowerOff method\n\t\t/// </summary>\n\t\tpublic override void PowerOff()\n\t\t{\n\t\t\t_PowerIsOn = false;\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_POWER_OFF, IrPulseTime);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// PowerToggle method\n\t\t/// </summary>\n\t\tpublic override void PowerToggle()\n\t\t{\n\t\t\t_PowerIsOn = false;\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_POWER, IrPulseTime);\n\t\t}\n\n\t\t#endregion\n\n\t\t#region IBasicVolumeControls Members\n\n\t\t/// <summary>\n\t\t/// VolumeUp method\n\t\t/// </summary>\n\t\tpublic void VolumeUp(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_VOL_PLUS, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// VolumeDown method\n\t\t/// </summary>\n\t\tpublic void VolumeDown(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_VOL_MINUS, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// MuteToggle method\n\t\t/// </summary>\n\t\tpublic void MuteToggle()\n\t\t{\n\t\t\tIrPort.Pulse(IROutputStandardCommands.IROut_MUTE, 200);\n\t\t}\n\n\t\t#endregion\n\n\t\tvoid StartWarmingTimer()\n\t\t{\n\t\t\t_IsWarmingUp = true;\n\t\t\tIsWarmingUpFeedback.FireUpdate();\n\t\t\tnew CTimer(o =>\n\t\t\t{\n\t\t\t\t_IsWarmingUp = false;\n\t\t\t\tIsWarmingUpFeedback.FireUpdate();\n\t\t\t}, 10000);\n\t\t}\n\n\t\tvoid StartCoolingTimer()\n\t\t{\n\t\t\t_IsCoolingDown = true;\n\t\t\tIsCoolingDownFeedback.FireUpdate();\n\t\t\tnew CTimer(o =>\n\t\t\t{\n\t\t\t\t_IsCoolingDown = false;\n\t\t\t\tIsCoolingDownFeedback.FireUpdate();\n\t\t\t}, 7000);\n\t\t}\n\n\t\t#region IRoutingSink Members\n\n\t\t/// <summary>\n\t\t/// Typically called by the discovery routing algorithm.\n\t\t/// </summary>\n\t\t/// <param name=\"inputSelector\">A delegate containing the input selector method to call</param>\n\t\t/// <summary>\n\t\t/// ExecuteSwitch method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override void ExecuteSwitch(object inputSelector)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Switching to input '{0}'\", (inputSelector as Action).ToString());\n\n\t\t\tAction finishSwitch = () =>\n\t\t\t\t{\n\t\t\t\t\tvar action = inputSelector as Action;\n\t\t\t\t\tif (action != null)\n\t\t\t\t\t\taction();\n\t\t\t\t};\n\n\t\t\tif (!_PowerIsOn)\n\t\t\t{\n\t\t\t\tPowerOn();\n\t\t\t\tEventHandler<FeedbackEventArgs> oneTimer = null;\n\t\t\t\toneTimer = (o, a) =>\n\t\t\t\t\t {\n\t\t\t\t\t\t if (IsWarmingUpFeedback.BoolValue) return; // Only catch done warming\n\t\t\t\t\t\t IsWarmingUpFeedback.OutputChange -= oneTimer;\n\t\t\t\t\t\t finishSwitch();\n\t\t\t\t\t };\n\t\t\t\tIsWarmingUpFeedback.OutputChange += oneTimer;\n\t\t\t}\n\t\t\telse // Do it!\n\t\t\t\tfinishSwitch();\n\t\t}\n\n\t\t#endregion\n\n\t\t/// <summary>\n\t\t/// LinkToApi method\n\t\t/// </summary>\n\t\tpublic void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n\t\t{\n\t\t\tLinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Represents a BasicIrDisplayFactory\n\t/// </summary>\n\tpublic class BasicIrDisplayFactory : EssentialsDeviceFactory<BasicIrDisplay>\n\t{\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the BasicIrDisplayFactory class\n\t\t/// </summary>\n\t\tpublic BasicIrDisplayFactory()\n\t\t{\n\t\t\tTypeNames = new List<string>() { \"basicirdisplay\" };\n\t\t}\n\n\t\t/// <summary>\n\t\t/// BuildDevice method\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic override EssentialsDevice BuildDevice(DeviceConfig dc)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new BasicIrDisplay Device\");\n\t\t\tvar ir = IRPortHelper.GetIrPort(dc.Properties);\n\t\t\tif (ir != null)\n\t\t\t{\n\t\t\t\tvar display = new BasicIrDisplay(dc.Key, dc.Name, ir.Port, ir.FileName);\n\t\t\t\tdisplay.IrPulseTime = 200;       // Set default pulse time for IR commands.\n\t\t\t\treturn display;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Core.Routing;\nusing Serilog.Events;\nusing Feedback = PepperDash.Essentials.Core.Feedback;\n\nnamespace PepperDash.Essentials.Devices.Common.Displays\n{\n\t/// <summary>\n\t/// Abstract base class for display devices that provides common display functionality\n\t/// including power control, input switching, and routing capabilities.\n\t/// </summary>\n\tpublic abstract class DisplayBase : EssentialsDevice, IDisplay, ICurrentSources, IHasFeedback\n\t{\n\t\tprivate RoutingInputPort _currentInputPort;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the current input port that is selected on the display.\n\t\t/// </summary>\n\t\tpublic RoutingInputPort CurrentInputPort\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn _currentInputPort;\n\t\t\t}\n\n\t\t\tprotected set\n\t\t\t{\n\t\t\t\tif (_currentInputPort == value) return;\n\n\t\t\t\t_currentInputPort = value;\n\n\t\t\t\tInputChanged?.Invoke(this, _currentInputPort);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Event that is raised when the input changes on the display.\n\t\t/// </summary>\n\t\tpublic event InputChangedEventHandler InputChanged;\n\n\t\t/// <summary>\n\t\t/// Event that is raised when the current source information changes.\n\t\t/// </summary>\n\t\tpublic event SourceInfoChangeHandler CurrentSourceChange;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the CurrentSourceInfoKey\n\t\t/// </summary>\n\t\tpublic string CurrentSourceInfoKey { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the current source information for the display.\n\t\t/// </summary>\n\t\tpublic SourceListItem CurrentSourceInfo\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn _CurrentSourceInfo;\n\t\t\t}\n\t\t\tset\n\t\t\t{\n\t\t\t\tif (value == _CurrentSourceInfo) return;\n\n\t\t\t\tvar handler = CurrentSourceChange;\n\n\t\t\t\thandler?.Invoke(_CurrentSourceInfo, ChangeType.WillChange);\n\n\t\t\t\t_CurrentSourceInfo = value;\n\n\t\t\t\thandler?.Invoke(_CurrentSourceInfo, ChangeType.DidChange);\n\t\t\t}\n\t\t}\n\t\tSourceListItem _CurrentSourceInfo;\n\n\t\t/// <inheritdoc/> \n\t\tpublic Dictionary<eRoutingSignalType, SourceListItem> CurrentSources { get; private set; }\n\n\t\t/// <inheritdoc/>\n\t\tpublic Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; private set; }\n\n\t\t/// <inheritdoc />\n\t\tpublic event EventHandler CurrentSourcesChanged;\n\n\t\t/// <summary>\n\t\t/// Gets feedback indicating whether the display is currently cooling down after being powered off.\n\t\t/// </summary>\n\t\tpublic BoolFeedback IsCoolingDownFeedback { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IsWarmingUpFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback IsWarmingUpFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the UsageTracker\n\t\t/// </summary>\n\t\tpublic UsageTracking UsageTracker { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the WarmupTime\n\t\t/// </summary>\n\t\tpublic uint WarmupTime { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the CooldownTime\n\t\t/// </summary>\n\t\tpublic uint CooldownTime { get; set; }\n\n\t\t/// <summary>\n\t\t/// Abstract function that must be implemented by derived classes to provide the cooling down feedback value.\n\t\t/// Must be implemented by concrete sub-classes.\n\t\t/// </summary>\n\t\tabstract protected Func<bool> IsCoolingDownFeedbackFunc { get; }\n\n\t\t/// <summary>\n\t\t/// Abstract function that must be implemented by derived classes to provide the warming up feedback value.\n\t\t/// Must be implemented by concrete sub-classes.\n\t\t/// </summary>\n\t\tabstract protected Func<bool> IsWarmingUpFeedbackFunc { get; }\n\n\t\t/// <summary>\n\t\t/// Timer used for managing display warmup timing.\n\t\t/// </summary>\n\t\tprotected CTimer WarmupTimer;\n\n\t\t/// <summary>\n\t\t/// Timer used for managing display cooldown timing.\n\t\t/// </summary>\n\t\tprotected CTimer CooldownTimer;\n\n\t\t#region IRoutingInputs Members\n\n\t\t/// <summary>\n\t\t/// Gets the collection of input ports available on this display device.\n\t\t/// </summary>\n\t\tpublic RoutingPortCollection<RoutingInputPort> InputPorts { get; private set; }\n\n\t\t#endregion\n\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the DisplayBase class.\n\t\t/// </summary>\n\t\t/// <param name=\"key\">The unique key identifier for this display device.</param>\n\t\t/// <param name=\"name\">The friendly name for this display device.</param>\n\t\tprotected DisplayBase(string key, string name)\n\t\t: base(key, name)\n\t\t{\n\t\t\tIsCoolingDownFeedback = new BoolFeedback(\"IsCoolingDown\", IsCoolingDownFeedbackFunc);\n\t\t\tIsWarmingUpFeedback = new BoolFeedback(\"IsWarmingUp\", IsWarmingUpFeedbackFunc);\n\n\t\t\tFeedbacks.Add(IsCoolingDownFeedback);\n\t\t\tFeedbacks.Add(IsWarmingUpFeedback);\n\n\t\t\tInputPorts = new RoutingPortCollection<RoutingInputPort>();\n\n\t\t\tCurrentSources = new Dictionary<eRoutingSignalType, SourceListItem>\n\t\t\t{\n\t\t\t\t{ eRoutingSignalType.Audio, null },\n\t\t\t\t{ eRoutingSignalType.Video, null },\n\t\t\t};\n\n\t\t\tCurrentSourceKeys = new Dictionary<eRoutingSignalType, string>\n\t\t\t{\n\t\t\t\t{ eRoutingSignalType.Audio, string.Empty },\n\t\t\t\t{ eRoutingSignalType.Video, string.Empty },\n\t\t\t};\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Powers on the display device. Must be implemented by derived classes.\n\t\t/// </summary>\n\t\tpublic abstract void PowerOn();\n\n\t\t/// <summary>\n\t\t/// Powers off the display device. Must be implemented by derived classes.\n\t\t/// </summary>\n\t\tpublic abstract void PowerOff();\n\n\t\t/// <summary>\n\t\t/// Toggles the power state of the display device. Must be implemented by derived classes.\n\t\t/// </summary>\n\t\tpublic abstract void PowerToggle();\n\n\t\t/// <summary>\n\t\t/// Gets the collection of feedback objects for this display device.\n\t\t/// </summary>\n\t\t/// <inheritdoc />\n\t\tpublic virtual FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();\n\n\n\t\t/// <summary>\n\t\t/// Executes a switch to the specified input on the display device. Must be implemented by derived classes.\n\t\t/// </summary>\n\t\t/// <param name=\"selector\">The selector object that identifies which input to switch to.</param>\n\t\tpublic abstract void ExecuteSwitch(object selector);\n\n\t\t/// <summary>\n\t\t/// Links the display device to an API using a trilist, join start, join map key, and bridge.\n\t\t/// This overload uses serialized join map configuration.\n\t\t/// </summary>\n\t\t/// <param name=\"displayDevice\">The display device to link.</param>\n\t\t/// <param name=\"trilist\">The BasicTriList for communication.</param>\n\t\t/// <param name=\"joinStart\">The starting join number for the device.</param>\n\t\t/// <param name=\"joinMapKey\">The key for the join map configuration.</param>\n\t\t/// <param name=\"bridge\">The EISC API bridge instance.</param>\n\t\tprotected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey,\n\t\t\t\tEiscApiAdvanced bridge)\n\t\t{\n\t\t\tvar joinMap = new DisplayControllerJoinMap(joinStart);\n\n\t\t\tvar joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n\t\t\tif (!string.IsNullOrEmpty(joinMapSerialized))\n\t\t\t\tjoinMap = JsonConvert.DeserializeObject<DisplayControllerJoinMap>(joinMapSerialized);\n\n\t\t\tif (bridge != null)\n\t\t\t{\n\t\t\t\tbridge.AddJoinMap(Key, joinMap);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n\t\t\t}\n\n\t\t\tLinkDisplayToApi(displayDevice, trilist, joinMap);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Links the display device to an API using a trilist and join map.\n\t\t/// This overload uses a pre-configured join map instance.\n\t\t/// </summary>\n\t\t/// <param name=\"displayDevice\">The display device to link.</param>\n\t\t/// <param name=\"trilist\">The BasicTriList for communication.</param>\n\t\t/// <param name=\"joinMap\">The join map configuration for the device.</param>\n\t\tprotected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, DisplayControllerJoinMap joinMap)\n\t\t{\n\t\t\tthis.LogDebug(\"Linking to Trilist {ipId}\", trilist.ID.ToString(\"X\"));\n\t\t\tthis.LogDebug(\"Linking to Display: {displayName}\", displayDevice.Name);\n\n\t\t\ttrilist.StringInput[joinMap.Name.JoinNumber].StringValue = displayDevice.Name;\n\n\t\t\tif (displayDevice is ICommunicationMonitor commMonitor)\n\t\t\t{\n\t\t\t\tcommMonitor.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]);\n\t\t\t}\n\n\t\t\t// TODO: revisit this as there could be issues with this approach\n\t\t\tvar inputNumber = 0;\n\t\t\tvar inputKeys = new List<string>();\n\n\t\t\tvar inputNumberFeedback = new IntFeedback(\"inputNumber\", () => inputNumber);\n\n\t\t\t// Add input number feedback to the device feedback collection to keep it around...\n\t\t\tFeedbacks.Add(inputNumberFeedback);\n\n\t\t\t// Two way feedbacks\t\t\t\n\t\t\tif (displayDevice is TwoWayDisplayBase twoWayDisplay)\n\t\t\t{\n\t\t\t\ttrilist.SetBool(joinMap.IsTwoWayDisplay.JoinNumber, true);\n\n\t\t\t\ttwoWayDisplay.CurrentInputFeedback.OutputChange += (o, a) => this.LogDebug(\"CurrentInputFeedback_OutputChange {input}\", a.StringValue);\n\n\t\t\t\tinputNumberFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputSelect.JoinNumber]);\n\n\t\t\t\ttwoWayDisplay.PowerIsOnFeedback.OutputChange += (o, a) =>\n\t\t\t\t{\n\t\t\t\t\tif (!a.BoolValue)\n\t\t\t\t\t{\n\t\t\t\t\t\tinputNumber = 102;\n\t\t\t\t\t\tinputNumberFeedback.FireUpdate();\n\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tinputNumber = 0;\n\t\t\t\t\t\tinputNumberFeedback.FireUpdate();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\ttwoWayDisplay.PowerIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.PowerOff.JoinNumber]);\n\t\t\t\ttwoWayDisplay.PowerIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PowerOn.JoinNumber]);\n\t\t\t}\n\n\t\t\t// Power Off\n\t\t\ttrilist.SetSigTrueAction(joinMap.PowerOff.JoinNumber, () =>\n\t\t\t{\n\t\t\t\tinputNumber = 102;\n\t\t\t\tinputNumberFeedback.FireUpdate();\n\t\t\t\tdisplayDevice.PowerOff();\n\t\t\t});\n\n\t\t\t// PowerOn\n\t\t\ttrilist.SetSigTrueAction(joinMap.PowerOn.JoinNumber, () =>\n\t\t\t{\n\t\t\t\tinputNumber = 0;\n\t\t\t\tinputNumberFeedback.FireUpdate();\n\t\t\t\tdisplayDevice.PowerOn();\n\t\t\t});\n\n\n\t\t\tfor (int i = 0; i < displayDevice.InputPorts.Count; i++)\n\t\t\t{\n\t\t\t\tvar localindex = i;\n\t\t\t\tif (localindex >= joinMap.InputNamesOffset.JoinSpan)\n\t\t\t\t{\n\t\t\t\t\tthis.LogWarning(\"Device has {inputCount} inputs.  The Join Map allows up to {joinSpan} inputs.  Discarding inputs {discardStart} - {discardEnd} from bridge.\",\n\t\t\t\t\t\tdisplayDevice.InputPorts.Count, joinMap.InputNamesOffset.JoinSpan, localindex + 1, displayDevice.InputPorts.Count);\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tinputKeys.Add(displayDevice.InputPorts[localindex].Key);\n\n\t\t\t\t\tvar tempKey = inputKeys.ElementAt(localindex);\n\n\t\t\t\t\ttrilist.SetSigTrueAction((ushort)(joinMap.InputSelectOffset.JoinNumber + localindex), () => displayDevice.ExecuteSwitch(displayDevice.InputPorts[tempKey].Selector));\n\n\t\t\t\t\tthis.LogDebug(\"Setting Input Select Action on Digital Join {joinNumber} to Input: {input}\", joinMap.InputSelectOffset.JoinNumber + localindex, displayDevice.InputPorts[tempKey].Key);\n\n\t\t\t\t\ttrilist.SetString((uint)(joinMap.InputNamesOffset.JoinNumber + localindex), displayDevice.InputPorts[localindex].Key);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.LogDebug(\"Setting Input Select Action on Analog Join {inputSelectJoin}\", joinMap.InputSelect);\n\n\t\t\ttrilist.SetUShortSigAction(joinMap.InputSelect.JoinNumber, (requestedInput) =>\n\t\t\t{\n\t\t\t\tif (requestedInput == 0)\n\t\t\t\t{\n\t\t\t\t\tdisplayDevice.PowerOff();\n\t\t\t\t\tinputNumber = 0;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// using 1-based indexing for inputs coming from SIMPL, so need to check if the input is <= the count, not <\n\t\t\t\tif (requestedInput > 0 && requestedInput <= displayDevice.InputPorts.Count && requestedInput != inputNumber)\n\t\t\t\t{\n\t\t\t\t\tdisplayDevice.ExecuteSwitch(displayDevice.InputPorts.ElementAt(requestedInput - 1).Selector);\n\n\t\t\t\t\tinputNumber = requestedInput;\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (requestedInput == 102)\n\t\t\t\t{\n\t\t\t\t\tdisplayDevice.PowerToggle();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (displayDevice is TwoWayDisplayBase)\n\t\t\t\t{\n\t\t\t\t\tinputNumberFeedback?.FireUpdate();\n\t\t\t\t}\n\t\t\t});\n\n\n\t\t\tvar volumeDisplay = displayDevice as IBasicVolumeControls;\n\t\t\tif (volumeDisplay == null) return;\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, volumeDisplay.VolumeUp);\n\t\t\ttrilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, volumeDisplay.VolumeDown);\n\t\t\ttrilist.SetSigTrueAction(joinMap.VolumeMute.JoinNumber, volumeDisplay.MuteToggle);\n\n\t\t\tvar volumeDisplayWithFeedback = volumeDisplay as IBasicVolumeWithFeedback;\n\n\t\t\tif (volumeDisplayWithFeedback == null) return;\n\t\t\ttrilist.SetSigTrueAction(joinMap.VolumeMuteOn.JoinNumber, volumeDisplayWithFeedback.MuteOn);\n\t\t\ttrilist.SetSigTrueAction(joinMap.VolumeMuteOff.JoinNumber, volumeDisplayWithFeedback.MuteOff);\n\n\n\t\t\ttrilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, volumeDisplayWithFeedback.SetVolume);\n\t\t\tvolumeDisplayWithFeedback.VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]);\n\t\t\tvolumeDisplayWithFeedback.MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMute.JoinNumber]);\n\t\t\tvolumeDisplayWithFeedback.MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]);\n\t\t\tvolumeDisplayWithFeedback.MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]);\n\t\t}\n\n\t\t/// <inheritdoc />\n\t\tpublic virtual void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)\n\t\t{\n\t\t\tforeach (eRoutingSignalType type in Enum.GetValues(typeof(eRoutingSignalType)))\n\t\t\t{\n\t\t\t\tvar flagValue = Convert.ToInt32(type);\n\t\t\t\t// Skip if flagValue is 0 or not a power of two (i.e., not a single-bit flag).\n\t\t\t\t// (flagValue & (flagValue - 1)) != 0 checks if more than one bit is set.\n\t\t\t\tif (flagValue == 0 || (flagValue & (flagValue - 1)) != 0)\n\t\t\t\t{\n\t\t\t\t\tthis.LogDebug(\"Skipping {type}\", type);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis.LogDebug(\"setting {type}\", type);\n\n\t\t\t\tif (signalType.HasFlag(type))\n\t\t\t\t{\n\t\t\t\t\tUpdateCurrentSources(type, sourceListKey, sourceListItem);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Raise the CurrentSourcesChanged event\n\t\t\tCurrentSourcesChanged?.Invoke(this, EventArgs.Empty);\n\t\t}\n\n\t\tprivate void UpdateCurrentSources(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)\n\t\t{\n\t\t\tif (CurrentSources.ContainsKey(signalType))\n\t\t\t{\n\t\t\t\tCurrentSources[signalType] = sourceListItem;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCurrentSources.Add(signalType, sourceListItem);\n\t\t\t}\n\n\t\t\t// Update the current source key for the specified signal type\n\t\t\tif (CurrentSourceKeys.ContainsKey(signalType))\n\t\t\t{\n\t\t\t\tCurrentSourceKeys[signalType] = sourceListKey;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCurrentSourceKeys.Add(signalType, sourceListKey);\n\t\t\t}\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/InputInterfaces.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials.Devices.Displays\n{\n    /// <summary>\n    /// Defines the contract for IInputHdmi1\n    /// </summary>\n    [Obsolete()]\n    public interface IInputHdmi1\n    {\n        /// <summary>\n        /// Switches to HDMI 1 input\n        /// </summary>\n        void InputHdmi1();\n    }\n\n    /// <summary>\n    /// Defines the contract for IInputHdmi2\n    /// </summary>\n    [Obsolete()]\n    public interface IInputHdmi2\n    {\n        /// <summary>\n        /// Switches to HDMI 2 input\n        /// </summary>\n        void InputHdmi2();\n    }\n\n    /// <summary>\n    /// Defines the contract for IInputHdmi3\n    /// </summary>\n    [Obsolete()]\n    public interface IInputHdmi3\n    {\n        /// <summary>\n        /// Switches to HDMI 3 input\n        /// </summary>\n        void InputHdmi3();\n    }\n\n    /// <summary>\n    /// Defines the contract for IInputHdmi4\n    /// </summary>\n    [Obsolete()]\n    public interface IInputHdmi4\n    {\n        /// <summary>\n        /// Switches to HDMI 4 input\n        /// </summary>\n        void InputHdmi4();\n    }\n\n    /// <summary>\n    /// Defines the contract for IInputDisplayPort1\n    /// </summary>\n    [Obsolete()]\n    public interface IInputDisplayPort1\n    {\n        /// <summary>\n        /// Switches to DisplayPort 1 input\n        /// </summary>\n        void InputDisplayPort1();\n    }\n\n    /// <summary>\n    /// Defines the contract for IInputDisplayPort2\n    /// </summary>\n    [Obsolete()]\n    public interface IInputDisplayPort2\n    {\n        /// <summary>\n        /// Switches to DisplayPort 2 input\n        /// </summary>\n        void InputDisplayPort2();\n    }\n\n    /// <summary>\n    /// Defines the contract for IInputVga1\n    /// </summary>\n    [Obsolete()]\n    public interface IInputVga1\n    {\n        /// <summary>\n        /// Switches to VGA 1 input\n        /// </summary>\n        void InputVga1();\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/MockDisplay.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Displays\n{\n    /// <summary>\n    /// Represents a MockDisplay\n    /// </summary>\n    public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced, IHasInputs<string>, IRoutingSinkWithSwitchingWithInputPort, IHasPowerControlWithFeedback\n    {\n        /// <summary>\n        /// Gets or sets the Inputs\n        /// </summary>\n        public ISelectableItems<string> Inputs { get; private set; }\n\n        bool _PowerIsOn;\n        bool _IsWarmingUp;\n        bool _IsCoolingDown;\n\n        /// <summary>\n        /// Gets the power is on feedback function\n        /// </summary>\n        protected override Func<bool> PowerIsOnFeedbackFunc\n        {\n            get\n            {\n                return () =>\n                    {\n                        return _PowerIsOn;\n                    };\n            }\n        }\n        /// <summary>\n        /// Gets the is cooling down feedback function\n        /// </summary>\n        protected override Func<bool> IsCoolingDownFeedbackFunc\n        {\n            get\n            {\n                return () =>\n                {\n                    return _IsCoolingDown;\n                };\n            }\n        }\n        /// <summary>\n        /// Gets the is warming up feedback function\n        /// </summary>\n        protected override Func<bool> IsWarmingUpFeedbackFunc\n        {\n            get\n            {\n                return () =>\n                {\n                    return _IsWarmingUp;\n                };\n            }\n        }\n        /// <summary>\n        /// Gets the current input feedback function\n        /// </summary>\n        protected override Func<string> CurrentInputFeedbackFunc { get { return () => Inputs.CurrentItem; } }\n\n        int VolumeHeldRepeatInterval = 200;\n        ushort VolumeInterval = 655;\n        ushort _FakeVolumeLevel = 31768;\n        bool _IsMuted;\n\n        /// <summary>\n        /// Initializes a new instance of the MockDisplay class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        public MockDisplay(string key, string name)\n            : base(key, name)\n        {\n            Inputs = new MockDisplayInputs\n            {\n                Items = new Dictionary<string, ISelectableItem>\n                {\n                    { \"HDMI1\", new MockDisplayInput ( \"HDMI1\", \"HDMI 1\",this ) },\n                    { \"HDMI2\", new MockDisplayInput (\"HDMI2\", \"HDMI 2\",this ) },\n                    { \"HDMI3\", new MockDisplayInput (\"HDMI3\", \"HDMI 3\",this ) },\n                    { \"HDMI4\", new MockDisplayInput (\"HDMI4\", \"HDMI 4\",this )},\n                    { \"DP\", new MockDisplayInput (\"DP\", \"DisplayPort\", this ) }\n                }\n            };\n\n            Inputs.CurrentItemChanged += (o, a) => CurrentInputFeedback.FireUpdate();\n\n            var hdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo,\n                eRoutingPortConnectionType.Hdmi, \"HDMI1\", this);\n            var hdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo,\n                eRoutingPortConnectionType.Hdmi, \"HDMI2\", this);\n            var hdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo,\n                eRoutingPortConnectionType.Hdmi, \"HDMI3\", this);\n            var hdmiIn4 = new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.AudioVideo,\n                eRoutingPortConnectionType.Hdmi, \"HDMI4\", this);\n            var dpIn = new RoutingInputPort(RoutingPortNames.DisplayPortIn, eRoutingSignalType.AudioVideo,\n                eRoutingPortConnectionType.DisplayPort, \"DP\", this);\n            InputPorts.AddRange(new[] { hdmiIn1, hdmiIn2, hdmiIn3, hdmiIn4, dpIn });\n\n            VolumeLevelFeedback = new IntFeedback(\"volume\", () => { return _FakeVolumeLevel; });\n            MuteFeedback = new BoolFeedback(\"muteOn\", () => _IsMuted);\n\n            WarmupTime = 10000;\n            CooldownTime = 10000;\n        }\n\n        /// <summary>\n        /// PowerOn method\n        /// </summary>\n        /// <inheritdoc />\n        public override void PowerOn()\n        {\n            if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)\n            {\n                _IsWarmingUp = true;\n                IsWarmingUpFeedback.InvokeFireUpdate();\n                // Fake power-up cycle\n                WarmupTimer = new CTimer(o =>\n                    {\n                        _IsWarmingUp = false;\n                        _PowerIsOn = true;\n                        IsWarmingUpFeedback.InvokeFireUpdate();\n                        PowerIsOnFeedback.InvokeFireUpdate();\n                    }, WarmupTime);\n            }\n        }\n\n        /// <summary>\n        /// PowerOff method\n        /// </summary>\n        /// <inheritdoc />\n        public override void PowerOff()\n        {\n            // If a display has unreliable-power off feedback, just override this and\n            // remove this check.\n            if (PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)\n            {\n                _IsCoolingDown = true;\n                IsCoolingDownFeedback.InvokeFireUpdate();\n                // Fake cool-down cycle\n                CooldownTimer = new CTimer(o =>\n                    {\n                        Debug.LogMessage(LogEventLevel.Verbose, \"Cooldown timer ending\", this);\n                        _IsCoolingDown = false;\n                        IsCoolingDownFeedback.InvokeFireUpdate();\n                        _PowerIsOn = false;\n                        PowerIsOnFeedback.InvokeFireUpdate();\n                    }, CooldownTime);\n            }\n        }\n\n        /// <summary>\n        /// PowerToggle method\n        /// </summary>\n        /// <inheritdoc />\n        public override void PowerToggle()\n        {\n            if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue)\n                PowerOff();\n            else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue)\n                PowerOn();\n        }\n\n        /// <summary>\n        /// ExecuteSwitch method\n        /// </summary>\n        /// <inheritdoc />\n        public override void ExecuteSwitch(object selector)\n        {\n            try\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"ExecuteSwitch: {0}\", this, selector);\n\n                if (!_PowerIsOn)\n                {\n                    PowerOn();\n                }\n\n                if (!Inputs.Items.TryGetValue(selector.ToString(), out var input))\n                    return;\n\n                Debug.LogMessage(LogEventLevel.Verbose, \"Selected input: {input}\", this, input.Key);\n                input.Select();\n\n                var inputPort = InputPorts.FirstOrDefault(port =>\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Checking input port {inputPort} with selector {portSelector} against {selector}\", this, port, port.Selector, selector);\n                    return port.Selector.ToString() == selector.ToString();\n                });\n\n                if (inputPort == null)\n                {\n                    Debug.LogMessage(LogEventLevel.Verbose, \"Unable to find input port for selector {selector}\", this, selector);\n                    return;\n                }\n\n                Debug.LogMessage(LogEventLevel.Verbose, \"Setting current input port to {inputPort}\", this, inputPort);\n                CurrentInputPort = inputPort;\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Error making switch: {Exception}\", this, ex);\n            }\n        }\n\n        /// <summary>\n        /// SetInput method\n        /// </summary>\n        public void SetInput(string selector)\n        {\n            ISelectableItem currentInput = null;\n\n            try\n            {\n                currentInput = Inputs.Items.SingleOrDefault(Inputs => Inputs.Value.IsSelected).Value;\n            }\n            catch { }\n\n\n            if (currentInput != null)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"SetInput: {0}\", selector);\n                currentInput.IsSelected = false;\n            }\n\n            if (!Inputs.Items.TryGetValue(selector, out var input))\n                return;\n\n            input.IsSelected = true;\n\n            Inputs.CurrentItem = selector;\n        }\n\n\n        #region IBasicVolumeWithFeedback Members\n\n        /// <summary>\n        /// Gets or sets the VolumeLevelFeedback\n        /// </summary>\n        public IntFeedback VolumeLevelFeedback { get; private set; }\n\n        /// <summary>\n        /// SetVolume method\n        /// </summary>\n        public void SetVolume(ushort level)\n        {\n            _FakeVolumeLevel = level;\n            VolumeLevelFeedback.InvokeFireUpdate();\n        }\n\n        /// <summary>\n        /// MuteOn method\n        /// </summary>\n        public void MuteOn()\n        {\n            _IsMuted = true;\n            MuteFeedback.InvokeFireUpdate();\n        }\n\n        /// <summary>\n        /// MuteOff method\n        /// </summary>\n        public void MuteOff()\n        {\n            _IsMuted = false;\n            MuteFeedback.InvokeFireUpdate();\n        }\n\n        /// <summary>\n        /// Gets or sets the MuteFeedback\n        /// </summary>\n        public BoolFeedback MuteFeedback { get; private set; }\n\n\n        #endregion\n\n        #region IBasicVolumeControls Members\n\n        /// <summary>\n        /// VolumeUp method\n        /// </summary>\n        public void VolumeUp(bool pressRelease)\n        {\n            //while (pressRelease)\n            //{\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Volume Down {0}\", pressRelease);\n            if (pressRelease)\n            {\n                var newLevel = _FakeVolumeLevel + VolumeInterval;\n                SetVolume((ushort)newLevel);\n                CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);\n            }\n            //}\n        }\n\n        /// <summary>\n        /// VolumeDown method\n        /// </summary>\n        public void VolumeDown(bool pressRelease)\n        {\n            //while (pressRelease)\n            //{\n            Debug.LogMessage(LogEventLevel.Verbose, this, \"Volume Up {0}\", pressRelease);\n            if (pressRelease)\n            {\n                var newLevel = _FakeVolumeLevel - VolumeInterval;\n                SetVolume((ushort)newLevel);\n                CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);\n            }\n            //}\n        }\n\n        /// <summary>\n        /// MuteToggle method\n        /// </summary>\n        public void MuteToggle()\n        {\n            _IsMuted = !_IsMuted;\n            MuteFeedback.InvokeFireUpdate();\n        }\n\n        #endregion\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge);\n        }\n    }\n\n    /// <summary>\n    /// Represents a MockDisplayFactory\n    /// </summary>\n    public class MockDisplayFactory : EssentialsDeviceFactory<MockDisplay>\n    {\n        /// <summary>\n        /// Initializes a new instance of the MockDisplayFactory class\n        /// </summary>\n        public MockDisplayFactory()\n        {\n            TypeNames = new List<string>() { \"mockdisplay\", \"mockdisplay2\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Mock Display Device\");\n            return new MockDisplay(dc.Key, dc.Name);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/MockDisplayInputs.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.Devices.Common.Displays\n{\n    /// <summary>\n    /// Represents a MockDisplayInputs\n    /// </summary>\n    public class MockDisplayInputs : ISelectableItems<string>\n    {\n        private Dictionary<string, ISelectableItem> _items;\n\n        /// <summary>\n        /// Gets or sets the collection of selectable items\n        /// </summary>\n        public Dictionary<string, ISelectableItem> Items\n        {\n            get\n            {\n                return _items;\n            }\n            set\n            {\n                if (_items == value)\n                    return;\n\n                _items = value;\n\n                ItemsUpdated?.Invoke(this, null);\n            }\n        }\n\n        private string _currentItem;\n\n        /// <summary>\n        /// Gets or sets the currently selected item\n        /// </summary>\n        public string CurrentItem\n        {\n            get\n            {\n                return _currentItem;\n            }\n            set\n            {\n                if (_currentItem == value)\n                    return;\n\n                _currentItem = value;\n\n                CurrentItemChanged?.Invoke(this, null);\n            }\n        }\n\n        /// <summary>\n        /// Occurs when the items collection is updated\n        /// </summary>\n        public event EventHandler ItemsUpdated;\n        /// <summary>\n        /// Occurs when the current item changes\n        /// </summary>\n        public event EventHandler CurrentItemChanged;\n    }\n\n    /// <summary>\n    /// Represents a MockDisplayInput\n    /// </summary>\n    public class MockDisplayInput : ISelectableItem\n    {\n        private MockDisplay _parent;\n\n        private bool _isSelected;\n\n        /// <summary>\n        /// Gets or sets a value indicating whether this input is selected\n        /// </summary>\n        public bool IsSelected\n        {\n            get\n            {\n                return _isSelected;\n            }\n            set\n            {\n                if (_isSelected == value)\n                    return;\n\n                _isSelected = value;\n\n                ItemUpdated?.Invoke(this, null);\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; set; }\n\n        /// <summary>\n        /// Occurs when this item is updated\n        /// </summary>\n        public event EventHandler ItemUpdated;\n\n        /// <summary>\n        /// Initializes a new instance of the MockDisplayInput class\n        /// </summary>\n        /// <param name=\"key\">The input key</param>\n        /// <param name=\"name\">The input name</param>\n        /// <param name=\"parent\">The parent mock display</param>\n        public MockDisplayInput(string key, string name, MockDisplay parent)\n        {\n            Key = key;\n            Name = name;\n            _parent = parent;\n        }\n\n        /// <summary>\n        /// Select method\n        /// </summary>\n        public void Select()\n        {\n            if (!_parent.PowerIsOnFeedback.BoolValue) _parent.PowerOn();\n\n            foreach (var input in _parent.Inputs.Items)\n            {\n                input.Value.IsSelected = input.Key == this.Key;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Timers;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.CrestronIO;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Devices.Common.Displays;\n\nnamespace PepperDash.Essentials.Devices.Common.Shades\n{\n    /// <summary>\n    /// Enumeration for requested state\n    /// </summary>\n    enum RequestedState\n    {\n        None,\n        Raise,\n        Lower,\n    }\n\n    /// <summary>\n    /// Controls a single shade using three relays\n    /// </summary>\n    public class ScreenLiftController : EssentialsDevice, IProjectorScreenLiftControl\n    {\n        readonly ScreenLiftControllerConfigProperties Config;\n        readonly ScreenLiftRelaysConfig RaiseRelayConfig;\n        readonly ScreenLiftRelaysConfig LowerRelayConfig;\n        readonly ScreenLiftRelaysConfig LatchedRelayConfig;\n\n        DisplayBase DisplayDevice;\n        ISwitchedOutput RaiseRelay;\n        ISwitchedOutput LowerRelay;\n        ISwitchedOutput LatchedRelay;\n\n        private bool _isMoving;\n        private RequestedState _requestedState;\n        private RequestedState _currentMovement;\n        private Timer _movementTimer;\n\n        /// <summary>\n        /// Gets or sets the InUpPosition\n        /// </summary>\n        public bool InUpPosition\n        {\n            get { return _isInUpPosition; }\n            set\n            {\n                if (value == _isInUpPosition)\n                    return;\n                _isInUpPosition = value;\n                IsInUpPosition.FireUpdate();\n                PositionChanged?.Invoke(this, new EventArgs());\n            }\n        }\n\n        private bool _isInUpPosition { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public eScreenLiftControlType Type { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Mode\n        /// </summary>\n        public eScreenLiftControlMode Mode { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the DisplayDeviceKey\n        /// </summary>\n        public string DisplayDeviceKey { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the IsInUpPosition\n        /// </summary>\n        public BoolFeedback IsInUpPosition { get; private set; }\n\n        /// <summary>\n        /// Event that fires when the position changes\n        /// </summary>\n        public event EventHandler<EventArgs> PositionChanged;\n\n        /// <summary>\n        /// Constructor for ScreenLiftController\n        /// </summary>\n        public ScreenLiftController(\n            string key,\n            string name,\n            ScreenLiftControllerConfigProperties config\n        )\n            : base(key, name)\n        {\n            Config = config;\n            DisplayDeviceKey = Config.DisplayDeviceKey;\n            Mode = Config.Mode;\n            Type = Config.Type;\n\n            IsInUpPosition = new BoolFeedback(\"isInUpPosition\", () => _isInUpPosition);\n\n            // Initialize movement timer for reuse\n            _movementTimer = new Timer();\n            _movementTimer.Elapsed += OnMovementComplete;\n            _movementTimer.AutoReset = false;\n\n            switch (Mode)\n            {\n                case eScreenLiftControlMode.momentary:\n                {\n                    RaiseRelayConfig = Config.Relays[\"raise\"];\n                    LowerRelayConfig = Config.Relays[\"lower\"];\n                    break;\n                }\n                case eScreenLiftControlMode.latched:\n                {\n                    LatchedRelayConfig = Config.Relays[\"latched\"];\n                    break;\n                }\n            }\n\n            IsInUpPosition.OutputChange += (sender, args) =>\n            {\n                this.LogDebug(\n                    \"ScreenLiftController '{name}' IsInUpPosition changed to {position}\",\n                    Name,\n                    IsInUpPosition.BoolValue ? \"Up\" : \"Down\"\n                );\n\n                if (!Config.MuteOnScreenUp)\n                {\n                    return;\n                }\n\n                if (args.BoolValue)\n                {\n                    return;\n                }\n\n                if (DisplayDevice is IBasicVideoMuteWithFeedback videoMute)\n                {\n                    this.LogInformation(\"Unmuting video because screen is down\");\n                    videoMute.VideoMuteOff();\n                }\n            };\n\n            IsInUpPosition.FireUpdate();\n        }\n\n        private void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            if (\n                !DisplayDevice.IsCoolingDownFeedback.BoolValue\n                && Type == eScreenLiftControlType.lift\n            )\n            {\n                Raise();\n                return;\n            }\n            if (\n                DisplayDevice.IsCoolingDownFeedback.BoolValue\n                && Type == eScreenLiftControlType.screen\n            )\n            {\n                Raise();\n                return;\n            }\n        }\n\n        private void IsWarmingUpFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            if (DisplayDevice.IsWarmingUpFeedback.BoolValue)\n            {\n                Lower();\n            }\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            //Create ISwitchedOutput objects based on props\n            switch (Mode)\n            {\n                case eScreenLiftControlMode.momentary:\n                {\n                    this.LogDebug(\"Getting relays for {mode}\", Mode);\n                    RaiseRelay = GetSwitchedOutputFromDevice(RaiseRelayConfig.DeviceKey);\n                    LowerRelay = GetSwitchedOutputFromDevice(LowerRelayConfig.DeviceKey);\n                    break;\n                }\n                case eScreenLiftControlMode.latched:\n                {\n                    this.LogDebug(\"Getting relays for {mode}\", Mode);\n                    LatchedRelay = GetSwitchedOutputFromDevice(LatchedRelayConfig.DeviceKey);\n                    break;\n                }\n            }\n\n            this.LogDebug(\"Getting display with key {displayKey}\", DisplayDeviceKey);\n            DisplayDevice = GetDisplayBaseFromDevice(DisplayDeviceKey);\n\n            if (DisplayDevice != null)\n            {\n                this.LogDebug(\"Subscribing to {displayKey} feedbacks\", DisplayDeviceKey);\n\n                DisplayDevice.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange;\n                DisplayDevice.IsCoolingDownFeedback.OutputChange +=\n                    IsCoolingDownFeedback_OutputChange;\n            }\n\n            return base.CustomActivate();\n        }\n\n        /// <summary>\n        /// Raise method\n        /// </summary>\n        public void Raise()\n        {\n            if (RaiseRelay == null && LatchedRelay == null)\n                return;\n\n            this.LogDebug(\"Raise called for {type}\", Type);\n\n            if (Config.MuteOnScreenUp && DisplayDevice is IBasicVideoMuteWithFeedback videoMute)\n            {\n                this.LogInformation(\"Muting video because screen is going up\");\n                videoMute.VideoMuteOn();\n            }\n\n            // If device is moving, bank the command\n            if (_isMoving)\n            {\n                this.LogDebug(\"Device is moving, banking Raise command\");\n                _requestedState = RequestedState.Raise;\n                return;\n            }\n\n            this.LogDebug(\"Raising {type}\", Type);\n\n            switch (Mode)\n            {\n                case eScreenLiftControlMode.momentary:\n                {\n                    PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs);\n\n                    // Set moving flag and start timer if movement time is configured\n                    if (RaiseRelayConfig.MoveTimeInMs > 0)\n                    {\n                        _isMoving = true;\n                        _currentMovement = RequestedState.Raise;\n                        if (_movementTimer.Enabled)\n                        {\n                            _movementTimer.Stop();\n                        }\n                        _movementTimer.Interval = RaiseRelayConfig.MoveTimeInMs;\n                        _movementTimer.Start();\n                    }\n                    else\n                    {\n                        InUpPosition = true;\n                    }\n                    break;\n                }\n                case eScreenLiftControlMode.latched:\n                {\n                    LatchedRelay.Off();\n                    InUpPosition = true;\n                    break;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Lower method\n        /// </summary>\n        public void Lower()\n        {\n            if (LowerRelay == null && LatchedRelay == null)\n                return;\n\n            this.LogDebug(\"Lower called for {type}\", Type);\n\n            // If device is moving, bank the command\n            if (_isMoving)\n            {\n                this.LogDebug(\"Device is moving, banking Lower command\");\n                _requestedState = RequestedState.Lower;\n                return;\n            }\n\n            this.LogDebug(\"Lowering {type}\", Type);\n\n            switch (Mode)\n            {\n                case eScreenLiftControlMode.momentary:\n                {\n                    PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs);\n\n                    // Set moving flag and start timer if movement time is configured\n                    if (LowerRelayConfig.MoveTimeInMs > 0)\n                    {\n                        _isMoving = true;\n                        _currentMovement = RequestedState.Lower;\n                        if (_movementTimer.Enabled)\n                        {\n                            _movementTimer.Stop();\n                        }\n                        _movementTimer.Interval = LowerRelayConfig.MoveTimeInMs;\n                        _movementTimer.Start();\n                    }\n                    else\n                    {\n                        InUpPosition = false;\n                    }\n                    break;\n                }\n                case eScreenLiftControlMode.latched:\n                {\n                    LatchedRelay.On();\n                    InUpPosition = false;\n                    break;\n                }\n            }\n        }\n\n        private void DisposeMovementTimer()\n        {\n            if (_movementTimer != null)\n            {\n                _movementTimer.Stop();\n                _movementTimer.Elapsed -= OnMovementComplete;\n                _movementTimer.Dispose();\n                _movementTimer = null;\n            }\n        }\n\n        /// <summary>\n        /// Called when movement timer completes\n        /// </summary>\n        private void OnMovementComplete(object sender, ElapsedEventArgs e)\n        {\n            this.LogDebug(\"Movement complete\");\n\n            // Update position based on completed movement\n            if (_currentMovement == RequestedState.Raise)\n            {\n                InUpPosition = true;\n            }\n            else if (_currentMovement == RequestedState.Lower)\n            {\n                InUpPosition = false;\n            }\n\n            _isMoving = false;\n            _currentMovement = RequestedState.None;\n\n            // Execute banked command if one exists\n            if (_requestedState != RequestedState.None)\n            {\n                this.LogDebug(\"Executing next command: {command}\", _requestedState);\n\n                var commandToExecute = _requestedState;\n                _requestedState = RequestedState.None;\n\n                // Check if current state matches what the banked command would do and execute if different\n                switch (commandToExecute)\n                {\n                    case RequestedState.Raise:\n                        Raise();\n                        break;\n\n                    case RequestedState.Lower:\n                        Lower();\n                        break;\n                }\n            }\n        }\n\n        private void PulseOutput(ISwitchedOutput output, int pulseTime)\n        {\n            output.On();\n\n            var timer = new Timer(pulseTime) { AutoReset = false };\n\n            timer.Elapsed += (sender, e) =>\n            {\n                output.Off();\n                timer.Dispose();\n            };\n            timer.Start();\n        }\n\n        private ISwitchedOutput GetSwitchedOutputFromDevice(string relayKey)\n        {\n            var portDevice = DeviceManager.GetDeviceForKey<ISwitchedOutput>(relayKey);\n            if (portDevice != null)\n            {\n                return portDevice;\n            }\n            else\n            {\n                this.LogWarning(\n                    \"Error: Unable to get relay device with key '{relayKey}'\",\n                    relayKey\n                );\n                return null;\n            }\n        }\n\n        private DisplayBase GetDisplayBaseFromDevice(string displayKey)\n        {\n            var displayDevice = DeviceManager.GetDeviceForKey<DisplayBase>(displayKey);\n            if (displayDevice != null)\n            {\n                return displayDevice;\n            }\n            else\n            {\n                this.LogWarning(\n                    \"Error: Unable to get display device with key '{displayKey}'\",\n                    displayKey\n                );\n                return null;\n            }\n        }\n    }\n\n    /// <summary>\n    /// Factory for ScreenLiftController devices\n    /// </summary>\n    public class ScreenLiftControllerFactory : EssentialsDeviceFactory<RelayControlledShade>\n    {\n        /// <summary>\n        /// Constructor for ScreenLiftControllerFactory\n        /// </summary>\n        public ScreenLiftControllerFactory()\n        {\n            TypeNames = new List<string>() { \"screenliftcontroller\" };\n        }\n\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogDebug(\"Factory Attempting to create new ScreenLiftController Device\");\n            var props = dc.Properties.ToObject<ScreenLiftControllerConfigProperties>();\n\n            return new ScreenLiftController(dc.Key, dc.Name, props);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftControllerConfigProperties.cs",
    "content": "using System.Collections.Generic;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.Devices.Common.Shades\n{\n    /// <summary>\n    /// Represents a ScreenLiftControllerConfigProperties\n    /// </summary>\n    public class ScreenLiftControllerConfigProperties\n    {\n        /// <summary>\n        /// Gets or sets the DisplayDeviceKey\n        /// </summary>\n        [JsonProperty(\"displayDeviceKey\")]\n        public string DisplayDeviceKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        [JsonProperty(\"type\")]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public eScreenLiftControlType Type { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Mode\n        /// </summary>\n        [JsonProperty(\"mode\")]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public eScreenLiftControlMode Mode { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Relays\n        /// </summary>\n        [JsonProperty(\"relays\")]\n        public Dictionary<string, ScreenLiftRelaysConfig> Relays { get; set; }\n\n        /// <summary>\n        /// Mutes the display when the screen is in the up position\n        /// </summary>\n        [JsonProperty(\"muteOnScreenUp\")]\n        public bool MuteOnScreenUp { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/ScreenLiftRelaysConfig.cs",
    "content": "using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.Shades\n{\n  /// <summary>\n  /// Represents a ScreenLiftRelaysConfig\n  /// </summary>\n  public class ScreenLiftRelaysConfig\n  {\n    /// <summary>\n    /// Gets or sets the DeviceKey\n    /// </summary>\n    [JsonProperty(\"deviceKey\")]\n    public string DeviceKey { get; set; }\n\n    /// <summary>\n    /// Gets or sets the PulseTimeInMs\n    /// </summary>\n    [JsonProperty(\"pulseTimeInMs\")]\n    public int PulseTimeInMs { get; set; }\n\n    /// <summary>\n    /// Gets or sets the MoveTimeInMs - time in milliseconds for the movement to complete\n    /// </summary>\n    [JsonProperty(\"moveTimeInMs\")]\n    public int MoveTimeInMs { get; set; }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/TwoWayDisplayBase.cs",
    "content": "using System;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Displays\n{\n  /// <summary>\n  /// Abstract base class for two-way display devices that provide feedback capabilities.\n  /// Extends DisplayBase with routing feedback and power control feedback functionality.\n  /// </summary>\n  public abstract class TwoWayDisplayBase : DisplayBase, IRoutingFeedback, IHasPowerControlWithFeedback\n  {\n    /// <summary>\n    /// Gets feedback for the current input selection on the display.\n    /// </summary>\n    public StringFeedback CurrentInputFeedback { get; private set; }\n\n    /// <summary>\n    /// Abstract function that must be implemented by derived classes to provide the current input feedback value.\n    /// Must be implemented by concrete sub-classes.\n    /// </summary>\n    abstract protected Func<string> CurrentInputFeedbackFunc { get; }\n\n    /// <summary>\n    /// Gets feedback indicating whether the display is currently powered on.\n    /// </summary>\n    public BoolFeedback PowerIsOnFeedback { get; protected set; }\n\n    /// <summary>\n    /// Abstract function that must be implemented by derived classes to provide the power state feedback value.\n    /// Must be implemented by concrete sub-classes.\n    /// </summary>\n    abstract protected Func<bool> PowerIsOnFeedbackFunc { get; }\n\n    /// <summary>\n    /// Gets the default mock display instance for testing and development purposes.\n    /// </summary>\n    public static MockDisplay DefaultDisplay\n    {\n      get\n      {\n        if (_DefaultDisplay == null)\n          _DefaultDisplay = new MockDisplay(\"default\", \"Default Display\");\n        return _DefaultDisplay;\n      }\n    }\n    static MockDisplay _DefaultDisplay;\n\n    /// <summary>\n    /// Initializes a new instance of the TwoWayDisplayBase class.\n    /// </summary>\n    /// <param name=\"key\">The unique key identifier for this display device.</param>\n    /// <param name=\"name\">The friendly name for this display device.</param>\n    public TwoWayDisplayBase(string key, string name)\n      : base(key, name)\n    {\n      CurrentInputFeedback = new StringFeedback(\"currentInput\", CurrentInputFeedbackFunc);\n\n      WarmupTime = 7000;\n      CooldownTime = 15000;\n\n      PowerIsOnFeedback = new BoolFeedback(\"PowerOnFeedback\", PowerIsOnFeedbackFunc);\n\n      Feedbacks.Add(CurrentInputFeedback);\n      Feedbacks.Add(PowerIsOnFeedback);\n\n      PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange;\n\n    }\n\n    void PowerIsOnFeedback_OutputChange(object sender, EventArgs e)\n    {\n      if (UsageTracker != null)\n      {\n        if (PowerIsOnFeedback.BoolValue)\n          UsageTracker.StartDeviceUsage();\n        else\n          UsageTracker.EndDeviceUsage();\n      }\n    }\n\n    /// <summary>\n    /// Event that is raised when a numeric switch change occurs on the display.\n    /// </summary>\n    public event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;\n\n    /// <summary>\n    /// Raise an event when the status of a switch object changes.\n    /// </summary>\n    /// <param name=\"e\">Arguments defined as IKeyName sender, output, input, and eRoutingSignalType</param>\n    protected void OnSwitchChange(RoutingNumericEventArgs e)\n    {\n      NumericSwitchChange?.Invoke(this, e);\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Displays/eScreenLiftControlMode.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.Shades\n{\n  /// <summary>\n  /// Enumeration of eScreenLiftControlMode values\n  /// </summary>\n  public enum eScreenLiftControlMode\n  {\n    /// <summary>\n    /// Momentary control mode for screen lift\n    /// </summary>\n    momentary,\n    /// <summary>\n    /// Latched control mode for screen lift\n    /// </summary>\n    latched\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Routing;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Generic\n{\n    /// <summary>\n    /// Represents a GenericSink\n    /// </summary>\n    public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort, ICurrentSources\n    {\n        /// <inheritdoc/> \n\t\tpublic Dictionary<eRoutingSignalType, SourceListItem> CurrentSources { get; private set; }\n\n        /// <inheritdoc/>\n        public Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; private set; }\n\n        /// <inheritdoc />\n        public event EventHandler CurrentSourcesChanged;\n\n        /// <summary>\n        /// Initializes a new instance of the GenericSink class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        public GenericSink(string key, string name) : base(key, name)\n        {\n            InputPorts = new RoutingPortCollection<RoutingInputPort>();\n\n            var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo | eRoutingSignalType.SecondaryAudio, eRoutingPortConnectionType.Hdmi, null, this);\n\n            InputPorts.Add(inputPort);\n\n            CurrentSources = new Dictionary<eRoutingSignalType, SourceListItem>\n            {\n                { eRoutingSignalType.Audio, null },\n                { eRoutingSignalType.Video, null },\n            };\n\n            CurrentSourceKeys = new Dictionary<eRoutingSignalType, string>\n            {\n                { eRoutingSignalType.Audio, string.Empty },\n                { eRoutingSignalType.Video, string.Empty },\n            };\n        }\n\n        /// <inheritdoc />\n\t\tpublic void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)\n        {\n            foreach (eRoutingSignalType type in Enum.GetValues(typeof(eRoutingSignalType)))\n            {\n                var flagValue = Convert.ToInt32(type);\n                // Skip if flagValue is 0 or not a power of two (i.e., not a single-bit flag).\n                // (flagValue & (flagValue - 1)) != 0 checks if more than one bit is set.\n                if (flagValue == 0 || (flagValue & (flagValue - 1)) != 0)\n                {\n                    this.LogDebug(\"Skipping {type}\", type);\n                    continue;\n                }\n\n                this.LogDebug(\"setting {type}\", type);\n\n                if (signalType.HasFlag(type))\n                {\n                    UpdateCurrentSources(type, sourceListKey, sourceListItem);\n                }\n            }\n            // Raise the CurrentSourcesChanged event\n            CurrentSourcesChanged?.Invoke(this, EventArgs.Empty);\n        }\n\n        private void UpdateCurrentSources(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)\n        {\n            CurrentSources[signalType] = sourceListItem;\n            CurrentSourceKeys[signalType] = sourceListKey;\n        }\n\n        /// <summary>\n        /// Gets or sets the InputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingInputPort> InputPorts { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the CurrentSourceInfoKey\n        /// </summary>\n        public string CurrentSourceInfoKey { get; set; }\n\n        private SourceListItem _currentSource;\n        /// <summary>\n        /// Gets or sets the CurrentSourceInfo\n        /// </summary>\n        public SourceListItem CurrentSourceInfo\n        {\n            get => _currentSource;\n            set\n            {\n                if (value == _currentSource)\n                {\n                    return;\n                }\n\n                CurrentSourceChange?.Invoke(_currentSource, ChangeType.WillChange);\n\n                _currentSource = value;\n\n                CurrentSourceChange?.Invoke(_currentSource, ChangeType.DidChange);\n            }\n        }\n\n        /// <summary>\n        /// Gets the current input port\n        /// </summary>\n        public RoutingInputPort CurrentInputPort => InputPorts[0];\n\n        /// <summary>\n        /// Event fired when the current source changes\n        /// </summary>\n        public event SourceInfoChangeHandler CurrentSourceChange;\n\n        /// <inheritdoc />\n        public event InputChangedEventHandler InputChanged;\n\n        /// <inheritdoc />\n        public void ExecuteSwitch(object inputSelector)\n        {\n            this.LogDebug(\"GenericSink Executing Switch to: {inputSelector}\", inputSelector);\n        }\n    }\n\n    /// <summary>\n    /// Represents a GenericSinkFactory\n    /// </summary>\n    public class GenericSinkFactory : EssentialsDeviceFactory<GenericSink>\n    {\n        /// <summary>\n        /// Initializes a new instance of the GenericSinkFactory class\n        /// </summary>\n        public GenericSinkFactory()\n        {\n            TypeNames = new List<string>() { \"genericsink\", \"genericdestination\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Generic Sink Device\");\n            return new GenericSink(dc.Key, dc.Name);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Generic/GenericSource.cs",
    "content": "﻿using System.Collections.Generic;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n    /// <summary>\n    /// Represents a GenericSource\n    /// </summary>\n    public class GenericSource : EssentialsDevice, IUiDisplayInfo, IRoutingSource, IUsageTracking\n    {\n\n        /// <summary>\n        /// Gets or sets the DisplayUiType\n        /// </summary>\n        public uint DisplayUiType { get { return DisplayUiConstants.TypeNoControls; } }\n\n        /// <summary>\n        /// Initializes a new instance of the GenericSource class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        public GenericSource(string key, string name)\n            : base(key, name)\n        {\n\n            AnyOut = new RoutingOutputPort(RoutingPortNames.AnyOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n                eRoutingPortConnectionType.Hdmi, null, this);\n            OutputPorts = new RoutingPortCollection<RoutingOutputPort> { AnyOut };\n        }\n\n        #region IRoutingOutputs Members\n\n        /// <summary>\n        /// Gets or sets the AnyOut\n        /// </summary>\n        public RoutingOutputPort AnyOut { get; private set; }\n        /// <summary>\n        /// Gets or sets the OutputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }\n\n        #endregion\n\n        #region IUsageTracking Members\n\n        /// <summary>\n        /// Gets or sets the UsageTracker\n        /// </summary>\n        public UsageTracking UsageTracker { get; set; }\n\n        #endregion\n    }\n\n    /// <summary>\n    /// Represents a GenericSourceFactory\n    /// </summary>\n    public class GenericSourceFactory : EssentialsDeviceFactory<GenericSource>\n    {\n        /// <summary>\n        /// Initializes a new instance of the GenericSourceFactory class\n        /// </summary>\n        public GenericSourceFactory()\n        {\n            TypeNames = new List<string>() { \"genericsource\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Generic Source Device\");\n            return new GenericSource(dc.Key, dc.Name);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Lighting/LightingBase.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Lighting;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Lighting\n{\n  /// <summary>\n  /// Base class for lighting devices that support scenes\n  /// </summary>\n  public abstract class LightingBase : EssentialsBridgeableDevice, ILightingScenes\n  {\n    #region ILightingScenes Members\n\n    /// <summary>\n    /// Event fired when lighting scene changes\n    /// </summary>\n    public event EventHandler<LightingSceneChangeEventArgs> LightingSceneChange;\n\n    /// <summary>\n    /// Gets or sets the LightingScenes\n    /// </summary>\n    public List<LightingScene> LightingScenes { get; protected set; }\n\n    /// <summary>\n    /// Gets or sets the CurrentLightingScene\n    /// </summary>\n    public LightingScene CurrentLightingScene { get; protected set; }\n\n    /// <summary>\n    /// Gets or sets the CurrentLightingSceneFeedback\n    /// </summary>\n    public IntFeedback CurrentLightingSceneFeedback { get; protected set; }\n\n    #endregion\n\n    /// <summary>\n    /// Initializes a new instance of the LightingBase class\n    /// </summary>\n    /// <param name=\"key\">The device key</param>\n    /// <param name=\"name\">The device name</param>\n    protected LightingBase(string key, string name)\n        : base(key, name)\n    {\n      LightingScenes = new List<LightingScene>();\n\n      CurrentLightingScene = new LightingScene();\n      //CurrentLightingSceneFeedback = new IntFeedback(() => { return int.Parse(this.CurrentLightingScene.ID); });\n    }\n\n    /// <summary>\n    /// Selects the specified lighting scene\n    /// </summary>\n    /// <param name=\"scene\">The lighting scene to select</param>\n    public abstract void SelectScene(LightingScene scene);\n\n    /// <summary>\n    /// SimulateSceneSelect method\n    /// </summary>\n    public void SimulateSceneSelect(string sceneName)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, this, \"Simulating selection of scene '{0}'\", sceneName);\n\n      var scene = LightingScenes.FirstOrDefault(s => s.Name.Equals(sceneName));\n\n      if (scene != null)\n      {\n        CurrentLightingScene = scene;\n        OnLightingSceneChange();\n      }\n    }\n\n    /// <summary>\n    /// Sets the IsActive property on each scene and fires the LightingSceneChange event\n    /// </summary>\n    protected void OnLightingSceneChange()\n    {\n      foreach (var scene in LightingScenes)\n      {\n        if (scene == CurrentLightingScene)\n          scene.IsActive = true;\n\n        else\n          scene.IsActive = false;\n      }\n      LightingSceneChange?.Invoke(this, new LightingSceneChangeEventArgs(CurrentLightingScene));\n    }\n\n    /// <summary>\n    /// Links the lighting device to API with join map configuration\n    /// </summary>\n    /// <param name=\"lightingDevice\">The lighting device to link</param>\n    /// <param name=\"trilist\">The trilist to link to</param>\n    /// <param name=\"joinStart\">The starting join number</param>\n    /// <param name=\"joinMapKey\">The join map key</param>\n    /// <param name=\"bridge\">The EISC API bridge</param>\n    /// <returns>The configured join map</returns>\n    protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, uint joinStart,\n      string joinMapKey, EiscApiAdvanced bridge)\n    {\n      var joinMap = new GenericLightingJoinMap(joinStart);\n\n      var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n      if (!string.IsNullOrEmpty(joinMapSerialized))\n        joinMap = JsonConvert.DeserializeObject<GenericLightingJoinMap>(joinMapSerialized);\n\n      if (bridge != null)\n      {\n        bridge.AddJoinMap(Key, joinMap);\n      }\n      else\n      {\n        Debug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n      }\n\n      return LinkLightingToApi(lightingDevice, trilist, joinMap);\n    }\n\n    /// <summary>\n    /// Links the lighting device to API using an existing join map\n    /// </summary>\n    /// <param name=\"lightingDevice\">The lighting device to link</param>\n    /// <param name=\"trilist\">The trilist to link to</param>\n    /// <param name=\"joinMap\">The join map to use</param>\n    /// <returns>The join map used for linking</returns>\n    protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, GenericLightingJoinMap joinMap)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, \"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n\n      Debug.LogMessage(LogEventLevel.Information, \"Linking to Lighting Type {0}\", lightingDevice.GetType().Name.ToString());\n\n      // GenericLighitng Actions & FeedBack\n      trilist.SetUShortSigAction(joinMap.SelectScene.JoinNumber, u => lightingDevice.SelectScene(lightingDevice.LightingScenes[u]));\n\n      var sceneIndex = 0;\n      foreach (var scene in lightingDevice.LightingScenes)\n      {\n        var index = sceneIndex;\n\n        trilist.SetSigTrueAction((uint)(joinMap.SelectSceneDirect.JoinNumber + index), () => lightingDevice.SelectScene(lightingDevice.LightingScenes[index]));\n        scene.IsActiveFeedback.LinkInputSig(trilist.BooleanInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)]);\n        trilist.StringInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)].StringValue = scene.Name;\n        trilist.BooleanInput[(uint)(joinMap.ButtonVisibility.JoinNumber + index)].BoolValue = true;\n\n        sceneIndex++;\n      }\n\n      trilist.OnlineStatusChange += (sender, args) =>\n      {\n        if (!args.DeviceOnLine) return;\n\n        sceneIndex = 0;\n        foreach (var scene in lightingDevice.LightingScenes)\n        {\n          var index = sceneIndex;\n\n          trilist.StringInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)].StringValue = scene.Name;\n          trilist.BooleanInput[(uint)(joinMap.ButtonVisibility.JoinNumber + index)].BoolValue = true;\n          scene.IsActiveFeedback.FireUpdate();\n\n          sceneIndex++;\n        }\n      };\n\n      return joinMap;\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <Configurations>Debug;Release;Debug 4.7.2</Configurations>\n  </PropertyGroup>\n  <PropertyGroup>\n    <TargetFramework>net472</TargetFramework>\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n    <AssemblyName>Essentials Devices Common</AssemblyName>\n    <RootNamespace>PepperDash.Essentials.Devices.Common</RootNamespace>\n    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>\n    <Title>PepperDash Essentials Devices Common</Title>\n    <PackageId>PepperDash.Essentials.Devices.Common</PackageId>\n    <InformationalVersion>$(Version)</InformationalVersion>\n    <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug 4.7.2|AnyCPU'\">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <DocumentationFile>bin\\$(Configuration)\\Essentials Devices Common.xml</DocumentationFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\PepperDash.Core\\PepperDash.Core.csproj\" />\n    <ProjectReference Include=\"..\\PepperDash.Essentials.Core\\PepperDash.Essentials.Core.csproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Crestron.SimplSharp.SDK.ProgramLibrary\" Version=\"2.21.90\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Properties/ControlSystem.cfg",
    "content": ""
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Room/IEssentialsHuddleSpaceRoom.cs",
    "content": "﻿using PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Room.Config;\n\nnamespace PepperDash.Essentials.Devices.Common.Room\n{\n    /// <summary>\n    /// Defines the contract for IEssentialsHuddleSpaceRoom\n    /// </summary>\n    public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IHasDefaultDisplay, IHasCurrentVolumeControls, IRoomOccupancy,\n        IEmergency, IMicrophonePrivacy\n    {\n        /// <summary>\n        /// Gets whether to exclude this room from global functions\n        /// </summary>\n        bool ExcludeFromGlobalFunctions { get; }\n\n        /// <summary>\n        /// Runs the route action for the given routeKey and sourceListKey\n        /// </summary>\n        /// <param name=\"routeKey\">The route key</param>\n        void RunRouteAction(string routeKey);\n\n        /// <summary>\n        /// Gets the PropertiesConfig\n        /// </summary>\n        EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Room/IEssentialsHuddleVtc1Room.cs",
    "content": "﻿using PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Devices.Common.AudioCodec;\nusing PepperDash.Essentials.Devices.Common.Codec;\nusing PepperDash.Essentials.Devices.Common.VideoCodec;\nusing PepperDash.Essentials.Room.Config;\n\nnamespace PepperDash.Essentials.Devices.Common.Room\n{\n    /// <summary>\n    /// Defines the contract for IEssentialsHuddleVtc1Room\n    /// </summary>\n    public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback,\n        IRoomOccupancy, IEmergency, IMicrophonePrivacy\n    {\n        /// <summary>\n        /// Gets the PropertiesConfig\n        /// </summary>\n        EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; }\n\n        /// <summary>\n        /// Gets whether to exclude this room from global functions\n        /// </summary>\n        bool ExcludeFromGlobalFunctions { get; }\n\n        /// <summary>\n        /// Runs the route action for the given routeKey and sourceListKey\n        /// </summary>\n        /// <param name=\"routeKey\">The route key</param>\n        void RunRouteAction(string routeKey);\n\n        /// <summary>\n        /// Gets the ScheduleSource\n        /// </summary>\n        IHasScheduleAwareness ScheduleSource { get; }\n\n        /// <summary>\n        /// Gets the InCallFeedback\n        /// </summary>\n        new BoolFeedback InCallFeedback { get; }\n\n        /// <summary>\n        /// Gets the PrivacyModeIsOnFeedback\n        /// </summary>\n        new BoolFeedback PrivacyModeIsOnFeedback { get; }\n\n        /// <summary>\n        /// Gets the DefaultCodecRouteString\n        /// </summary>\n        string DefaultCodecRouteString { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Room/IEssentialsRoomPropertiesConfig.cs",
    "content": "﻿using PepperDash.Essentials.Room.Config;\n\nnamespace PepperDash.Essentials.Devices.Common.Room\n{\n    /// <summary>\n    /// Defines the contract for IEssentialsRoomPropertiesConfig\n    /// </summary>\n    public interface IEssentialsRoomPropertiesConfig\n    {\n        /// <summary>\n        /// Gets the PropertiesConfig\n        /// </summary>\n        EssentialsRoomPropertiesConfig PropertiesConfig { get; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Room/IEssentialsTechRoom.cs",
    "content": "﻿using System.Collections.Generic;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Devices.Common.Displays;\nusing PepperDash.Essentials.Room.Config;\n\n\nnamespace PepperDash.Essentials.Devices.Common.Room\n{\n    /// <summary>\n    /// Defines the contract for IEssentialsTechRoom\n    /// </summary>\n    public interface IEssentialsTechRoom : IEssentialsRoom, ITvPresetsProvider, IBridgeAdvanced, IRunDirectRouteAction\n    {\n        /// <summary>\n        /// Gets the PropertiesConfig\n        /// </summary>\n        EssentialsTechRoomConfig PropertiesConfig { get; }\n\n        /// <summary>\n        /// Gets the Tuners\n        /// </summary>\n        Dictionary<string, IRSetTopBoxBase> Tuners { get; }\n\n        /// <summary>\n        /// Gets the Displays\n        /// </summary>\n        Dictionary<string, TwoWayDisplayBase> Displays { get; }\n\n        /// <summary>\n        /// Powers on the room\n        /// </summary>\n        void RoomPowerOn();\n\n        /// <summary>\n        /// Powers off the room\n        /// </summary>\n        void RoomPowerOff();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/SetTopBox/IRSetTopBoxBase.cs",
    "content": "﻿using Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Core.Presets;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n    /// <summary>\n    /// Represents a IRSetTopBoxBase\n    /// Wrapper class for an IR Set Top Box\n    /// </summary>\n    [Description(\"Wrapper class for an IR Set Top Box\")]\n    public class IRSetTopBoxBase : EssentialsBridgeableDevice, ISetTopBoxControls, IRoutingSource, IRoutingOutputs, IUsageTracking, IHasPowerControl, ITvPresetsProvider\n    {\n        /// <summary>\n        /// Gets or sets the IrPort\n        /// </summary>\n\t\tpublic IrOutputPortController IrPort { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the DisplayUiType\n        /// </summary>\n        public uint DisplayUiType { get { return DisplayUiConstants.TypeDirecTv; } }\n        /// <summary>\n        /// Gets or sets the IrPulseTime\n        /// </summary>\n        public ushort IrPulseTime { get; set; }\n\n        /// <summary>\n        /// Gets or sets the HasPresets\n        /// </summary>\n        public bool HasPresets { get; set; }\n        /// <summary>\n        /// Gets or sets the HasDvr\n        /// </summary>\n        public bool HasDvr { get; set; }\n        /// <summary>\n        /// Gets or sets the HasDpad\n        /// </summary>\n        public bool HasDpad { get; set; }\n        /// <summary>\n        /// Gets or sets the HasNumeric\n        /// </summary>\n        public bool HasNumeric { get; set; }\n\n        /// <summary>\n        /// Gets or sets the TvPresets\n        /// </summary>\n        public DevicePresetsModel TvPresets { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"IRSetTopBoxBase\"/> class\n        /// </summary>\n        /// <param name=\"key\">The unique identifier for the device</param>\n        /// <param name=\"name\">The name of the device</param>\n        /// <param name=\"portCont\">The IR output port controller</param>\n        /// <param name=\"props\">The properties configuration</param>\n        public IRSetTopBoxBase(string key, string name, IrOutputPortController portCont,\n            SetTopBoxPropertiesConfig props)\n            : base(key, name)\n        {\n            IrPort = portCont;\n            IrPulseTime = 200;\n\n            if (props.IrPulseTime > 0)\n            {\n                IrPulseTime = (ushort)props.IrPulseTime;\n            }\n\n            DeviceManager.AddDevice(portCont);\n\n            HasPresets = props.HasPresets;\n            HasDvr = props.HasDvr;\n            HasDpad = props.HasDpad;\n            HasNumeric = props.HasNumeric;\n\n            HasKeypadAccessoryButton1 = true;\n            KeypadAccessoryButton1Command = \"Dash\";\n            KeypadAccessoryButton1Label = \"-\";\n\n            HasKeypadAccessoryButton2 = true;\n            KeypadAccessoryButton2Command = \"KEYPAD_ENTER\";\n            KeypadAccessoryButton2Label = \"Enter\";\n\n            AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n                eRoutingPortConnectionType.Hdmi, null, this);\n            AnyAudioOut = new RoutingOutputPort(RoutingPortNames.AnyAudioOut, eRoutingSignalType.Audio,\n                eRoutingPortConnectionType.DigitalAudio, null, this);\n            OutputPorts = new RoutingPortCollection<RoutingOutputPort> { AnyVideoOut, AnyAudioOut };\n        }\n\n        /// <summary>\n        /// LoadPresets method\n        /// </summary>\n        public void LoadPresets(string filePath)\n        {\n            TvPresets = new DevicePresetsModel(Key + \"-presets\", this, filePath);\n            DeviceManager.AddDevice(TvPresets);\n        }\n\n\n        #region ISetTopBoxControls Members\n\n        /// <summary>\n        /// DvrList method\n        /// </summary>\n        public void DvrList(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_DVR, pressRelease);\n        }\n\n        /// <summary>\n        /// Replay method\n        /// </summary>\n        public void Replay(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_REPLAY, pressRelease);\n        }\n\n        #endregion\n\n        #region IDPad Members\n\n        /// <summary>\n        /// Up method\n        /// </summary>\n        public void Up(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_UP_ARROW, pressRelease);\n        }\n\n        /// <summary>\n        /// Down method\n        /// </summary>\n        public void Down(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_DN_ARROW, pressRelease);\n        }\n\n        /// <summary>\n        /// Left method\n        /// </summary>\n        public void Left(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_LEFT_ARROW, pressRelease);\n        }\n\n        /// <summary>\n        /// Right method\n        /// </summary>\n        public void Right(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_RIGHT_ARROW, pressRelease);\n        }\n\n        /// <summary>\n        /// Select method\n        /// </summary>\n        public void Select(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_ENTER, pressRelease);\n        }\n\n        /// <summary>\n        /// Menu method\n        /// </summary>\n        public void Menu(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_MENU, pressRelease);\n        }\n\n        /// <summary>\n        /// Exit method\n        /// </summary>\n        public void Exit(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_EXIT, pressRelease);\n        }\n\n        #endregion\n\n        #region INumericKeypad Members\n\n        /// <summary>\n        /// Digit0 method\n        /// </summary>\n        public void Digit0(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_0, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit1 method\n        /// </summary>\n        public void Digit1(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_1, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit2 method\n        /// </summary>\n        public void Digit2(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_2, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit3 method\n        /// </summary>\n        public void Digit3(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_3, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit4 method\n        /// </summary>\n        public void Digit4(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_4, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit5 method\n        /// </summary>\n        public void Digit5(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_5, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit6 method\n        /// </summary>\n        public void Digit6(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_6, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit7 method\n        /// </summary>\n        public void Digit7(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_7, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit8 method\n        /// </summary>\n        public void Digit8(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_8, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit9 method\n        /// </summary>\n        public void Digit9(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_9, pressRelease);\n        }\n\n        /// <summary>\n        /// Gets or sets the HasKeypadAccessoryButton1\n        /// </summary>\n        public bool HasKeypadAccessoryButton1 { get; set; }\n\n        /// <summary>\n        /// Defaults to \"-\"\n        /// </summary>\n        public string KeypadAccessoryButton1Label { get; set; }\n\n\n        /// <summary>\n        /// Defaults to \"Dash\"\n        /// </summary>\n        public string KeypadAccessoryButton1Command { get; set; }\n\n        /// <summary>\n        /// Presses the KeypadAccessoryButton1\n        /// </summary>\n        public void KeypadAccessoryButton1(bool pressRelease)\n        {\n            IrPort.PressRelease(KeypadAccessoryButton1Command, pressRelease);\n        }\n\n        /// <summary>\n        /// Gets or sets the HasKeypadAccessoryButton2\n        /// </summary>\n        public bool HasKeypadAccessoryButton2 { get; set; }\n\n        /// <summary>\n        /// Defaults to \"Enter\"\n        /// </summary>\n        public string KeypadAccessoryButton2Label { get; set; }\n\n\n        /// <summary>\n        /// Defaults to \"Enter\"\n        /// </summary>\n        public string KeypadAccessoryButton2Command { get; set; }\n\n        /// <summary>\n        /// Presses the KeypadAccessoryButton2\n        /// </summary>\n        public void KeypadAccessoryButton2(bool pressRelease)\n        {\n            IrPort.PressRelease(KeypadAccessoryButton2Command, pressRelease);\n        }\n\n        #endregion\n\n        #region ISetTopBoxNumericKeypad Members\n\n        /// <summary>\n        /// Dash method\n        /// </summary>\n        public void Dash(bool pressRelease)\n        {\n            IrPort.PressRelease(\"dash\", pressRelease);\n        }\n\n        /// <summary>\n        /// Corresponds to \"numericEnter\" IR command\n        /// </summary>\n        public void KeypadEnter(bool pressRelease)\n        {\n            IrPort.PressRelease(\"numericEnter\", pressRelease);\n        }\n\n        #endregion\n\n        #region IChannelFunctions Members\n\n        /// <summary>   \n        /// ChannelUp method\n        /// </summary>\n        public void ChannelUp(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_CH_PLUS, pressRelease);\n        }\n\n        /// <summary>\n        /// ChannelDown method\n        /// </summary>\n        public void ChannelDown(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_CH_MINUS, pressRelease);\n        }\n\n        /// <summary>\n        /// LastChannel method\n        /// </summary>\n        public void LastChannel(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_LAST, pressRelease);\n        }\n\n        /// <summary>\n        /// Guide method\n        /// </summary>\n        public void Guide(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_GUIDE, pressRelease);\n        }\n\n        /// <summary>\n        /// Info method\n        /// </summary>\n        public void Info(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_INFO, pressRelease);\n        }\n\n        #endregion\n\n        #region IColorFunctions Members\n\n        /// <summary>\n        /// Red method\n        /// </summary>\n        public void Red(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_RED, pressRelease);\n        }\n\n        /// <summary>\n        /// Green method\n        /// </summary>\n        public void Green(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_GREEN, pressRelease);\n        }\n\n        /// <summary>\n        /// Yellow method\n        /// </summary>\n        public void Yellow(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_YELLOW, pressRelease);\n        }\n\n        /// <summary>\n        /// Blue method\n        /// </summary>\n        public void Blue(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_BLUE, pressRelease);\n        }\n\n        #endregion\n\n        #region IRoutingOutputs Members\n\n        /// <summary>\n        /// Gets or sets the AnyVideoOut\n        /// </summary>\n        public RoutingOutputPort AnyVideoOut { get; private set; }\n        /// <summary>\n        /// Gets or sets the AnyAudioOut\n        /// </summary>\n        public RoutingOutputPort AnyAudioOut { get; private set; }\n        /// <summary>\n        /// Gets or sets the OutputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }\n\n        #endregion\n\n        #region ITransport Members\n\n        /// <summary>\n        /// ChapMinus method\n        /// </summary>\n        public void ChapMinus(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_REPLAY, pressRelease);\n        }\n\n        /// <summary>\n        /// ChapPlus method\n        /// </summary>\n        public void ChapPlus(bool pressRelease)\n        {\n        }\n\n        /// <summary>\n        /// FFwd method\n        /// </summary>\n        public void FFwd(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_FSCAN, pressRelease);\n        }\n\n        /// <summary>\n        /// Pause method\n        /// </summary>\n        public void Pause(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_RSCAN, pressRelease);\n        }\n\n        /// <summary>\n        /// Play method\n        /// </summary>\n        public void Play(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_PLAY, pressRelease);\n        }\n\n        /// <summary>\n        /// Record method\n        /// </summary>\n        public void Record(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_RECORD, pressRelease);\n        }\n\n        /// <summary>\n        /// Rewind method\n        /// </summary>\n        public void Rewind(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_RSCAN, pressRelease);\n        }\n\n        /// <summary>\n        /// Stop method\n        /// </summary>\n        public void Stop(bool pressRelease)\n        {\n            IrPort.PressRelease(IROutputStandardCommands.IROut_STOP, pressRelease);\n        }\n\n        #endregion\n\n        #region IUsageTracking Members\n\n        /// <summary>\n        /// Gets or sets the UsageTracker\n        /// </summary>\n        public UsageTracking UsageTracker { get; set; }\n\n        #endregion\n\n        #region IPower Members\n\n        /// <summary>\n        /// PowerOn method\n        /// </summary>\n        public void PowerOn()\n        {\n            IrPort.Pulse(IROutputStandardCommands.IROut_POWER_ON, IrPulseTime);\n        }\n\n        /// <summary>\n        /// PowerOff method\n        /// </summary>\n        public void PowerOff()\n        {\n            IrPort.Pulse(IROutputStandardCommands.IROut_POWER_OFF, IrPulseTime);\n        }\n\n        /// <summary>\n        /// PowerToggle method\n        /// </summary>\n        public void PowerToggle()\n        {\n            IrPort.Pulse(IROutputStandardCommands.IROut_POWER, IrPulseTime);\n        }\n\n        #endregion\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        /// <inheritdoc />\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new SetTopBoxControllerJoinMap(joinStart);\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<SetTopBoxControllerJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            Debug.LogMessage(LogEventLevel.Debug, \"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n            Debug.LogMessage(LogEventLevel.Information, \"Linking to SetTopBox: {0}\", Name);\n\n            trilist.OnlineStatusChange += new OnlineStatusChangeEventHandler((o, a) =>\n            {\n                if (a.DeviceOnLine)\n                {\n                    trilist.StringInput[joinMap.Name.JoinNumber].StringValue = Name;\n                }\n            });\n\n\n            if (this is ISetTopBoxControls stbBase)\n            {\n                trilist.BooleanInput[joinMap.HasDpad.JoinNumber].BoolValue = stbBase.HasDpad;\n                trilist.BooleanInput[joinMap.HasNumeric.JoinNumber].BoolValue = stbBase.HasNumeric;\n                trilist.BooleanInput[joinMap.HasDvr.JoinNumber].BoolValue = stbBase.HasDvr;\n                trilist.BooleanInput[joinMap.HasPresets.JoinNumber].BoolValue = stbBase.HasPresets;\n\n                trilist.SetBoolSigAction(joinMap.DvrList.JoinNumber, stbBase.DvrList);\n                trilist.SetBoolSigAction(joinMap.Replay.JoinNumber, stbBase.Replay);\n\n                trilist.SetStringSigAction(joinMap.LoadPresets.JoinNumber, stbBase.LoadPresets);\n            }\n\n            if (this is IHasPowerControl stbPower)\n            {\n                trilist.SetSigTrueAction(joinMap.PowerOn.JoinNumber, stbPower.PowerOn);\n                trilist.SetSigTrueAction(joinMap.PowerOff.JoinNumber, stbPower.PowerOff);\n                trilist.SetSigTrueAction(joinMap.PowerToggle.JoinNumber, stbPower.PowerToggle);\n            }\n\n            if (this is IDPad stbDPad)\n            {\n                trilist.SetBoolSigAction(joinMap.Up.JoinNumber, stbDPad.Up);\n                trilist.SetBoolSigAction(joinMap.Down.JoinNumber, stbDPad.Down);\n                trilist.SetBoolSigAction(joinMap.Left.JoinNumber, stbDPad.Left);\n                trilist.SetBoolSigAction(joinMap.Right.JoinNumber, stbDPad.Right);\n                trilist.SetBoolSigAction(joinMap.Select.JoinNumber, stbDPad.Select);\n                trilist.SetBoolSigAction(joinMap.Menu.JoinNumber, stbDPad.Menu);\n                trilist.SetBoolSigAction(joinMap.Exit.JoinNumber, stbDPad.Exit);\n            }\n\n            if (this is IChannel stbChannel)\n            {\n                trilist.SetBoolSigAction(joinMap.ChannelUp.JoinNumber, stbChannel.ChannelUp);\n                trilist.SetBoolSigAction(joinMap.ChannelDown.JoinNumber, stbChannel.ChannelDown);\n                trilist.SetBoolSigAction(joinMap.LastChannel.JoinNumber, stbChannel.LastChannel);\n                trilist.SetBoolSigAction(joinMap.Guide.JoinNumber, stbChannel.Guide);\n                trilist.SetBoolSigAction(joinMap.Info.JoinNumber, stbChannel.Info);\n                trilist.SetBoolSigAction(joinMap.Exit.JoinNumber, stbChannel.Exit);\n            }\n\n            if (this is IColor stbColor)\n            {\n                trilist.SetBoolSigAction(joinMap.Red.JoinNumber, stbColor.Red);\n                trilist.SetBoolSigAction(joinMap.Green.JoinNumber, stbColor.Green);\n                trilist.SetBoolSigAction(joinMap.Yellow.JoinNumber, stbColor.Yellow);\n                trilist.SetBoolSigAction(joinMap.Blue.JoinNumber, stbColor.Blue);\n            }\n\n            if (this is ISetTopBoxNumericKeypad stbKeypad)\n            {\n                trilist.StringInput[joinMap.KeypadAccessoryButton1Label.JoinNumber].StringValue = stbKeypad.KeypadAccessoryButton1Label;\n                trilist.StringInput[joinMap.KeypadAccessoryButton2Label.JoinNumber].StringValue = stbKeypad.KeypadAccessoryButton2Label;\n\n                trilist.BooleanInput[joinMap.HasKeypadAccessoryButton1.JoinNumber].BoolValue = stbKeypad.HasKeypadAccessoryButton1;\n                trilist.BooleanInput[joinMap.HasKeypadAccessoryButton2.JoinNumber].BoolValue = stbKeypad.HasKeypadAccessoryButton2;\n\n                trilist.SetBoolSigAction(joinMap.Digit0.JoinNumber, stbKeypad.Digit0);\n                trilist.SetBoolSigAction(joinMap.Digit1.JoinNumber, stbKeypad.Digit1);\n                trilist.SetBoolSigAction(joinMap.Digit2.JoinNumber, stbKeypad.Digit2);\n                trilist.SetBoolSigAction(joinMap.Digit3.JoinNumber, stbKeypad.Digit3);\n                trilist.SetBoolSigAction(joinMap.Digit4.JoinNumber, stbKeypad.Digit4);\n                trilist.SetBoolSigAction(joinMap.Digit5.JoinNumber, stbKeypad.Digit5);\n                trilist.SetBoolSigAction(joinMap.Digit6.JoinNumber, stbKeypad.Digit6);\n                trilist.SetBoolSigAction(joinMap.Digit7.JoinNumber, stbKeypad.Digit7);\n                trilist.SetBoolSigAction(joinMap.Digit8.JoinNumber, stbKeypad.Digit8);\n                trilist.SetBoolSigAction(joinMap.Digit9.JoinNumber, stbKeypad.Digit9);\n                trilist.SetBoolSigAction(joinMap.KeypadAccessoryButton1Press.JoinNumber, stbKeypad.KeypadAccessoryButton1);\n                trilist.SetBoolSigAction(joinMap.KeypadAccessoryButton2Press.JoinNumber, stbKeypad.KeypadAccessoryButton1);\n                trilist.SetBoolSigAction(joinMap.Dash.JoinNumber, stbKeypad.Dash);\n                trilist.SetBoolSigAction(joinMap.KeypadEnter.JoinNumber, stbKeypad.KeypadEnter);\n            }\n\n            if (this is ITransport stbTransport)\n            {\n                trilist.SetBoolSigAction(joinMap.Play.JoinNumber, stbTransport.Play);\n                trilist.SetBoolSigAction(joinMap.Pause.JoinNumber, stbTransport.Pause);\n                trilist.SetBoolSigAction(joinMap.Rewind.JoinNumber, stbTransport.Rewind);\n                trilist.SetBoolSigAction(joinMap.FFwd.JoinNumber, stbTransport.FFwd);\n                trilist.SetBoolSigAction(joinMap.ChapMinus.JoinNumber, stbTransport.ChapMinus);\n                trilist.SetBoolSigAction(joinMap.ChapPlus.JoinNumber, stbTransport.ChapPlus);\n                trilist.SetBoolSigAction(joinMap.Stop.JoinNumber, stbTransport.Stop);\n                trilist.SetBoolSigAction(joinMap.Record.JoinNumber, stbTransport.Record);\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/SetTopBox/IRSetTopBoxBaseFactory.cs",
    "content": "using System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n  /// <summary>\n  /// Represents a IRSetTopBoxBaseFactory\n  /// </summary>\n  public class IRSetTopBoxBaseFactory : EssentialsDeviceFactory<IRSetTopBoxBase>\n  {\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"IRSetTopBoxBaseFactory\"/> class\n    /// </summary>\n    public IRSetTopBoxBaseFactory()\n    {\n      TypeNames = new List<string>() { \"settopbox\" };\n    }\n\n    /// <summary>\n    /// BuildDevice method\n    /// </summary>\n    /// <inheritdoc />\n    public override EssentialsDevice BuildDevice(DeviceConfig dc)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new SetTopBox Device\");\n      var irCont = IRPortHelper.GetIrOutputPortController(dc);\n      var config = dc.Properties.ToObject<SetTopBoxPropertiesConfig>();\n      var stb = new IRSetTopBoxBase(dc.Key, dc.Name, irCont, config);\n\n      var listName = dc.Properties.Value<string>(\"presetsList\");\n      if (listName != null)\n        stb.LoadPresets(listName);\n      return stb;\n\n    }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/SetTopBox/SetTopBoxPropertiesConfig.cs",
    "content": "﻿using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n    /// <summary>\n    /// Represents a SetTopBoxPropertiesConfig\n    /// </summary>\n    public class SetTopBoxPropertiesConfig : PepperDash.Essentials.Core.Config.SourceDevicePropertiesConfigBase\n    {\n        /// <summary>\n        /// Gets or sets the HasPresets\n        /// </summary>\n        public bool HasPresets { get; set; }\n        /// <summary>\n        /// Gets or sets the HasDvr\n        /// </summary>\n        public bool HasDvr { get; set; }\n        /// <summary>\n        /// Gets or sets the HasDpad\n        /// </summary>\n        public bool HasDpad { get; set; }\n        /// <summary>\n        /// Gets or sets the HasNumeric\n        /// </summary>\n        public bool HasNumeric { get; set; }\n        /// <summary>\n        /// Gets or sets the IrPulseTime\n        /// </summary>\n        public int IrPulseTime { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Control\n        /// </summary>\n        public ControlPropertiesConfig Control { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Shades/RelayControlledShade.cs",
    "content": "﻿using System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.CrestronIO;\nusing PepperDash.Essentials.Core.Shades;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Shades\n{\n    /// <summary>\n    /// Controls a single shade using three relays\n    /// </summary>\n    public class RelayControlledShade : ShadeBase, IShadesOpenCloseStop\n    {\n        RelayControlledShadeConfigProperties Config;\n\n        ISwitchedOutput OpenRelay;\n        ISwitchedOutput StopOrPresetRelay;\n        ISwitchedOutput CloseRelay;\n\n        int RelayPulseTime;\n\n        /// <summary>\n        /// Gets or sets the StopOrPresetButtonLabel\n        /// </summary>\n        public string StopOrPresetButtonLabel { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the RelayControlledShade class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        /// <param name=\"config\">The relay controlled shade configuration</param>\n        public RelayControlledShade(string key, string name, RelayControlledShadeConfigProperties config)\n            : base(key, name)\n        {\n            Config = config;\n\n            RelayPulseTime = Config.RelayPulseTime;\n\n            StopOrPresetButtonLabel = Config.StopOrPresetLabel;\n\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            //Create ISwitchedOutput objects based on props\n            OpenRelay = GetSwitchedOutputFromDevice(Config.Relays.Open);\n            StopOrPresetRelay = GetSwitchedOutputFromDevice(Config.Relays.StopOrPreset);\n            CloseRelay = GetSwitchedOutputFromDevice(Config.Relays.Close);\n\n\n            return base.CustomActivate();\n        }\n\n        /// <summary>\n        /// Open method\n        /// </summary>\n        /// <inheritdoc />\n        public override void Open()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Opening Shade: '{0}'\", this.Name);\n\n            PulseOutput(OpenRelay, RelayPulseTime);\n        }\n\n        /// <summary>\n        /// Stop method\n        /// </summary>\n        /// <inheritdoc />\n        public override void Stop()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Shade: '{0}'\", this.Name);\n\n            PulseOutput(StopOrPresetRelay, RelayPulseTime);\n        }\n\n        /// <summary>\n        /// Close method\n        /// </summary>\n        /// <inheritdoc />\n        public override void Close()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Closing Shade: '{0}'\", this.Name);\n\n            PulseOutput(CloseRelay, RelayPulseTime);\n        }\n\n        void PulseOutput(ISwitchedOutput output, int pulseTime)\n        {\n            output.On();\n            CTimer pulseTimer = new CTimer(new CTimerCallbackFunction((o) => output.Off()), pulseTime);\n        }\n\n        /// <summary>\n        /// Attempts to get the port on teh specified device from config\n        /// </summary>\n        /// <param name=\"relayConfig\"></param>\n        /// <returns></returns>\n        ISwitchedOutput GetSwitchedOutputFromDevice(IOPortConfig relayConfig)\n        {\n            var portDevice = DeviceManager.GetDeviceForKey(relayConfig.PortDeviceKey);\n\n            if (portDevice != null)\n            {\n                return (portDevice as ISwitchedOutputCollection).SwitchedOutputs[relayConfig.PortNumber];\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Error: Unable to get relay on port '{0}' from device with key '{1}'\", relayConfig.PortNumber, relayConfig.PortDeviceKey);\n                return null;\n            }\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a RelayControlledShadeConfigProperties\n    /// </summary>\n    public class RelayControlledShadeConfigProperties\n    {\n        /// <summary>\n        /// Gets or sets the RelayPulseTime\n        /// </summary>\n        public int RelayPulseTime { get; set; }\n        /// <summary>\n        /// Gets or sets the Relays\n        /// </summary>\n        public ShadeRelaysConfig Relays { get; set; }\n        /// <summary>\n        /// Gets or sets the StopOrPresetLabel\n        /// </summary>\n        public string StopOrPresetLabel { get; set; }\n\n        /// <summary>\n        /// Represents a ShadeRelaysConfig\n        /// </summary>\n        public class ShadeRelaysConfig\n        {\n            /// <summary>\n            /// Gets or sets the Open\n            /// </summary>\n            public IOPortConfig Open { get; set; }\n            /// <summary>\n            /// Gets or sets the StopOrPreset\n            /// </summary>\n            public IOPortConfig StopOrPreset { get; set; }\n            /// <summary>\n            /// Gets or sets the Close\n            /// </summary>\n            public IOPortConfig Close { get; set; }\n        }\n    }\n\n    /// <summary>\n    /// Represents a RelayControlledShadeFactory\n    /// </summary>\n    public class RelayControlledShadeFactory : EssentialsDeviceFactory<RelayControlledShade>\n    {\n        /// <summary>\n        /// Initializes a new instance of the RelayControlledShadeFactory class\n        /// </summary>\n        public RelayControlledShadeFactory()\n        {\n            TypeNames = new List<string>() { \"relaycontrolledshade\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Generic Comm Device\");\n            var props = Newtonsoft.Json.JsonConvert.DeserializeObject<RelayControlledShadeConfigProperties>(dc.Properties.ToString());\n\n            return new RelayControlledShade(dc.Key, dc.Name, props);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Shades/ShadeBase.cs",
    "content": "﻿using PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Shades;\n\nnamespace PepperDash.Essentials.Devices.Common.Shades\n{\n    /// <summary>\n    /// Base class for shade devices\n    /// </summary>\n    public abstract class ShadeBase : EssentialsDevice, IShadesOpenCloseStop\n    {\n        /// <summary>\n        /// Initializes a new instance of the ShadeBase class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        public ShadeBase(string key, string name)\n            : base(key, name)\n        {\n\n        }\n\n        #region iShadesOpenClose Members\n\n        /// <summary>\n        /// Opens the shade\n        /// </summary>\n        public abstract void Open();\n        /// <summary>\n        /// Stops the shade\n        /// </summary>\n        public abstract void Stop();\n        /// <summary>\n        /// Closes the shade\n        /// </summary>\n        public abstract void Close();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Shades/ShadeController.cs",
    "content": "﻿using System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Shades;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Shades\n{\n    /// <summary>\n    /// Class that contains the shades to be controlled in a room\n    /// </summary>\n    public class ShadeController : EssentialsDevice, IShades\n    {\n        ShadeControllerConfigProperties Config;\n\n        /// <summary>\n        /// Gets the collection of shades controlled by this controller\n        /// </summary>\n        public List<IShadesOpenCloseStop> Shades { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the ShadeController class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        /// <param name=\"config\">The shade controller configuration</param>\n        public ShadeController(string key, string name, ShadeControllerConfigProperties config)\n            : base(key, name)\n        {\n            Config = config;\n\n            Shades = new List<IShadesOpenCloseStop>();\n        }\n\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        /// <inheritdoc />\n        public override bool CustomActivate()\n        {\n            foreach (var shadeConfig in Config.Shades)\n            {\n                var shade = DeviceManager.GetDeviceForKey(shadeConfig.Key) as ShadeBase;\n\n                if (shade != null)\n                {\n                    AddShade(shade);\n                }\n            }\n            return base.CustomActivate();\n        }\n\n        void AddShade(IShadesOpenCloseStop shade)\n        {\n            Shades.Add(shade);\n        }\n    }\n\n    /// <summary>\n    /// Represents a ShadeControllerConfigProperties\n    /// </summary>\n    public class ShadeControllerConfigProperties\n    {\n        /// <summary>\n        /// Gets or sets the Shades\n        /// </summary>\n        public List<ShadeConfig> Shades { get; set; }\n\n\n        /// <summary>\n        /// Represents a ShadeConfig\n        /// </summary>\n        public class ShadeConfig\n        {\n            /// <summary>\n            /// Gets or sets the Key\n            /// </summary>\n            public string Key { get; set; }\n        }\n    }\n\n    /// <summary>\n    /// Represents a ShadeControllerFactory\n    /// </summary>\n    public class ShadeControllerFactory : EssentialsDeviceFactory<ShadeController>\n    {\n        /// <summary>\n        /// Initializes a new instance of the ShadeControllerFactory class\n        /// </summary>\n        public ShadeControllerFactory()\n        {\n            TypeNames = new List<string>() { \"shadecontroller\" };\n        }\n\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new ShadeController Device\");\n            var props = Newtonsoft.Json.JsonConvert.DeserializeObject<ShadeControllerConfigProperties>(dc.Properties.ToString());\n\n            return new ShadeController(dc.Key, dc.Name, props);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/SoftCodec/BlueJeansPc.cs",
    "content": "﻿using System;\nusing Crestron.SimplSharp;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Devices.Common.Sources;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Devices.Common.SoftCodec\n{\n    /// <summary>\n    /// Represents a BlueJeansPc\n    /// </summary>\n    public class BlueJeansPc : InRoomPc, IRunRouteAction, IRoutingSink\n    {\n\n        /// <summary>\n        /// Gets or sets the AnyVideoIn\n        /// </summary>\n        public RoutingInputPort AnyVideoIn { get; private set; }\n\n        /// <summary>\n        /// Gets the CurrentInputPort\n        /// </summary>\n        public RoutingInputPort CurrentInputPort => AnyVideoIn;\n\n        #region IRoutingInputs Members\n\n        /// <summary>\n        /// Gets or sets the InputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingInputPort> InputPorts { get; private set; }\n\n        #endregion\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BlueJeansPc\"/> class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        public BlueJeansPc(string key, string name)\n            : base(key, name)\n        {\n            InputPorts = new RoutingPortCollection<RoutingInputPort>\n            {\n                (AnyVideoIn = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.None, 0, this))\n            };\n        }\n\n        #region IRunRouteAction Members\n\n        /// <summary>\n        /// RunRouteAction method\n        /// </summary>\n        public void RunRouteAction(string routeKey, string sourceListKey)\n        {\n            RunRouteAction(routeKey, sourceListKey, null);\n        }\n\n        /// <summary>\n        /// RunRouteAction method\n        /// </summary>\n        public void RunRouteAction(string routeKey, string sourceListKey, Action successCallback)\n        {\n            CrestronInvoke.BeginInvoke(o =>\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Run route action '{0}' on SourceList: {1}\", routeKey, sourceListKey);\n\n                    var dict = ConfigReader.ConfigObject.GetSourceListForKey(sourceListKey);\n                    if (dict == null)\n                    {\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"WARNING: Config source list '{0}' not found\", sourceListKey);\n                        return;\n                    }\n\n                    // Try to get the list item by it's string key\n                    if (!dict.ContainsKey(routeKey))\n                    {\n                        Debug.LogMessage(LogEventLevel.Debug, this, \"WARNING: No item '{0}' found on config list '{1}'\",\n                            routeKey, sourceListKey);\n                        return;\n                    }\n\n                    var item = dict[routeKey];\n\n                    foreach (var route in item.RouteList)\n                    {\n                        DoRoute(route);\n                    }\n\n                    // store the name and UI info for routes\n                    if (item.SourceKey == \"none\")\n                    {\n                        CurrentSourceInfoKey = routeKey;\n                        CurrentSourceInfo = null;\n                    }\n                    else if (item.SourceKey != null)\n                    {\n                        CurrentSourceInfoKey = routeKey;\n                        CurrentSourceInfo = item;\n                    }\n\n                    // report back when done\n                    if (successCallback != null)\n                        successCallback();\n                });\n        }\n\n        #endregion\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"route\"></param>\n        /// <returns></returns>\n        bool DoRoute(SourceRouteListItem route)\n        {\n            IRoutingSink dest = null;\n\n            dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSink;\n\n            if (dest == null)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Cannot route, unknown destination '{0}'\", route.DestinationKey);\n                return false;\n            }\n\n            if (route.SourceKey.Equals(\"$off\", StringComparison.OrdinalIgnoreCase))\n            {\n                dest.ReleaseRoute();\n                if (dest is IHasPowerControl)\n                    (dest as IHasPowerControl).PowerOff();\n            }\n            else\n            {\n                var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs;\n                if (source == null)\n                {\n                    Debug.LogMessage(LogEventLevel.Debug, this, \"Cannot route unknown source '{0}' to {1}\", route.SourceKey, route.DestinationKey);\n                    return false;\n                }\n                dest.ReleaseAndMakeRoute(source, route.Type);\n            }\n            return true;\n        }\n\n\n\n        #region IHasCurrentSourceInfoChange Members\n\n        /// <summary>\n        /// Gets or sets the CurrentSourceInfoKey\n        /// </summary>\n        public string CurrentSourceInfoKey { get; set; }\n\n        /// <summary>\n        /// The SourceListItem last run - containing names and icons \n        /// </summary>\n        public SourceListItem CurrentSourceInfo\n        {\n            get { return _CurrentSourceInfo; }\n            set\n            {\n                if (value == _CurrentSourceInfo) return;\n\n                var handler = CurrentSourceChange;\n                // remove from in-use tracker, if so equipped\n                if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)\n                    (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, \"control\");\n\n                if (handler != null)\n                    handler(_CurrentSourceInfo, ChangeType.WillChange);\n\n                _CurrentSourceInfo = value;\n\n                // add to in-use tracking\n                if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking)\n                    (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, \"control\");\n                if (handler != null)\n                    handler(_CurrentSourceInfo, ChangeType.DidChange);\n            }\n        }\n        SourceListItem _CurrentSourceInfo;\n\n        /// <summary>\n        /// Event fired when the current source changes\n        /// </summary>\n        public event SourceInfoChangeHandler CurrentSourceChange;\n\n        #endregion\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/SoftCodec/BlueJeansPcFactory.cs",
    "content": "using System.Collections.Generic;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\n\nnamespace PepperDash.Essentials.Devices.Common.SoftCodec\n{\n  /// <summary>\n  /// Represents a BlueJeansPcFactory\n  /// </summary>\n  public class BlueJeansPcFactory : EssentialsDeviceFactory<BlueJeansPc>\n  {\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"BlueJeansPcFactory\"/> class\n    /// </summary>\n    public BlueJeansPcFactory()\n    {\n      TypeNames = new List<string>() { \"bluejeanspc\" };\n    }\n\n    /// <summary>\n    /// BuildDevice method\n    /// </summary>\n    /// <inheritdoc />\n    public override EssentialsDevice BuildDevice(DeviceConfig dc)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new BlueJeansPc Device\");\n      return new BlueJeansPc(dc.Key, dc.Name);\n    }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/SoftCodec/GenericSoftCodec.cs",
    "content": "﻿using System.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.SoftCodec\n{\n    /// <summary>\n    /// Represents a GenericSoftCodec\n    /// </summary>\n    public class GenericSoftCodec : EssentialsDevice, IRoutingSource, IRoutingSinkWithSwitchingWithInputPort\n    {\n        private RoutingInputPort _currentInputPort;\n\n        /// <summary>\n        /// Gets or sets the CurrentInputPort\n        /// </summary>\n        public RoutingInputPort CurrentInputPort\n        {\n            get => _currentInputPort;\n            set\n            {\n                _currentInputPort = value;\n\n                InputChanged?.Invoke(this, _currentInputPort);\n            }\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"GenericSoftCodec\"/> class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        /// <param name=\"props\">The device properties</param>\n        public GenericSoftCodec(string key, string name, GenericSoftCodecProperties props) : base(key, name)\n        {\n            InputPorts = new RoutingPortCollection<RoutingInputPort>();\n            OutputPorts = new RoutingPortCollection<RoutingOutputPort>();\n\n            for (var i = 1; i <= props.OutputCount; i++)\n            {\n                var outputPort = new RoutingOutputPort($\"output{i}\", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);\n\n                OutputPorts.Add(outputPort);\n            }\n\n            for (var i = 1; i <= props.ContentInputCount; i++)\n            {\n                var inputPort = new RoutingInputPort($\"contentInput{i}\", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, $\"contentInput{i}\", this);\n\n                InputPorts.Add(inputPort);\n            }\n\n            if (!props.HasCameraInputs)\n            {\n                return;\n            }\n\n            for (var i = 1; i <= props.CameraInputCount; i++)\n            {\n                var cameraPort = new RoutingInputPort($\"cameraInput{i}\", eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, $\"cameraInput{i}\", this);\n\n                InputPorts.Add(cameraPort);\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the InputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingInputPort> InputPorts { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the OutputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }\n        /// <summary>\n        /// Gets or sets the CurrentSourceInfoKey\n        /// </summary>\n        public string CurrentSourceInfoKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the CurrentSourceInfo\n        /// </summary>\n        public SourceListItem CurrentSourceInfo\n        {\n            get\n            {\n                return _CurrentSourceInfo;\n            }\n            set\n            {\n                if (value == _CurrentSourceInfo) return;\n\n                var handler = CurrentSourceChange;\n\n                if (handler != null)\n                    handler(_CurrentSourceInfo, ChangeType.WillChange);\n\n                _CurrentSourceInfo = value;\n\n                if (handler != null)\n                    handler(_CurrentSourceInfo, ChangeType.DidChange);\n            }\n        }\n\n        SourceListItem _CurrentSourceInfo;\n\n        /// <summary>\n        /// Event fired when the current source changes\n        /// </summary>\n        public event SourceInfoChangeHandler CurrentSourceChange;\n\n        /// <summary>\n        /// Event fired when the input changes\n        /// </summary>\n        public event InputChangedEventHandler InputChanged;\n\n        /// <summary>\n        /// ExecuteSwitch method\n        /// </summary>\n        public void ExecuteSwitch(object inputSelector)\n        {\n            var inputPort = InputPorts.FirstOrDefault(p => p.Selector == inputSelector);\n\n            if (inputPort == null)\n            {\n                Debug.LogMessage(LogEventLevel.Warning, \"No input port found for selector {inputSelector}\", inputSelector);\n                return;\n            }\n\n            CurrentInputPort = inputPort;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/SoftCodec/GenericSoftCodecFactory.cs",
    "content": "using System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.SoftCodec\n{\n  /// <summary>\n  /// Represents a GenericSoftCodecFactory\n  /// </summary>\n  public class GenericSoftCodecFactory : EssentialsDeviceFactory<GenericSoftCodec>\n  {\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"GenericSoftCodecFactory\"/> class\n    /// </summary>\n    public GenericSoftCodecFactory()\n    {\n      TypeNames = new List<string> { \"genericsoftcodec\" };\n    }\n\n    /// <summary>\n    /// BuildDevice method\n    /// </summary>\n    /// <inheritdoc />\n    public override EssentialsDevice BuildDevice(DeviceConfig dc)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, \"Attempting to create new Generic SoftCodec Device\");\n\n      var props = dc.Properties.ToObject<GenericSoftCodecProperties>();\n\n      return new GenericSoftCodec(dc.Key, dc.Name, props);\n    }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/SoftCodec/GenericSoftCodecProperties.cs",
    "content": "using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.SoftCodec\n{\n  /// <summary>\n  /// Represents a GenericSoftCodecProperties\n  /// </summary>\n  public class GenericSoftCodecProperties\n  {\n    /// <summary>\n    /// Gets or sets the HasCameraInputs\n    /// </summary>\n    [JsonProperty(\"hasCameraInputs\")]\n    public bool HasCameraInputs { get; set; }\n\n    /// <summary>\n    /// Gets or sets the CameraInputCount\n    /// </summary>\n    [JsonProperty(\"cameraInputCount\")]\n    public int CameraInputCount { get; set; }\n\n    /// <summary>\n    /// Gets or sets the ContentInputCount\n    /// </summary>\n    [JsonProperty(\"contentInputCount\")]\n    public int ContentInputCount { get; set; }\n\n    /// <summary>\n    /// Gets or sets the OutputCount\n    /// </summary>\n    [JsonProperty(\"contentOutputCount\")]\n    public int OutputCount { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Sources\n{\n    /// <summary>\n    /// Represents a InRoomPc\n    /// </summary>\n    public class InRoomPc : EssentialsDevice, IHasFeedback, IRoutingSource, IRoutingOutputs, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking\n    {\n        /// <summary>\n        /// Gets or sets the DisplayUiType\n        /// </summary>\n        public uint DisplayUiType { get { return DisplayUiConstants.TypeLaptop; } }\n        /// <summary>\n        /// Gets or sets the IconName\n        /// </summary>\n        public string IconName { get; set; }\n        /// <summary>\n        /// Gets or sets the HasPowerOnFeedback\n        /// </summary>\n        public BoolFeedback HasPowerOnFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the AnyVideoOut\n        /// </summary>\n        public RoutingOutputPort AnyVideoOut { get; private set; }\n\n        #region IRoutingOutputs Members\n\n        /// <summary>\n        /// Gets or sets the OutputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }\n\n        #endregion\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"InRoomPc\"/> class\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"name\"></param>\n        public InRoomPc(string key, string name)\n            : base(key, name)\n        {\n            IconName = \"PC\";\n            HasPowerOnFeedback = new BoolFeedback(\"HasPowerFeedback\",\n                () => this.GetVideoStatuses() != VideoStatusOutputs.NoStatus);\n\n            OutputPorts = new RoutingPortCollection<RoutingOutputPort>\n            {\n              (AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n                eRoutingPortConnectionType.None, 0, this))\n            };\n        }\n\n        #region IHasFeedback Members\n\n        /// <summary>\n        /// Passes through the VideoStatuses list\n        /// </summary>\n        public FeedbackCollection<Feedback> Feedbacks\n        {\n            get\n            {\n                var newList = new FeedbackCollection<Feedback>();\n                newList.AddRange(this.GetVideoStatuses().ToList());\n                return newList;\n            }\n        }\n\n        #endregion\n\n        #region IUsageTracking Members\n\n        /// <summary>\n        /// Gets or sets the UsageTracker\n        /// </summary>\n        public UsageTracking UsageTracker { get; set; }\n\n        #endregion\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Sources/InRoomPcFactory.cs",
    "content": "using System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Sources\n{\n  /// <summary>\n  /// Represents a InRoomPcFactory\n  /// </summary>\n  public class InRoomPcFactory : EssentialsDeviceFactory<InRoomPc>\n  {\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"InRoomPcFactory\"/> class\n    /// </summary>\n    public InRoomPcFactory()\n    {\n      TypeNames = new List<string>() { \"inroompc\" };\n    }\n\n    /// <summary>\n    /// BuildDevice method\n    /// </summary>\n    /// <inheritdoc />\n    public override EssentialsDevice BuildDevice(DeviceConfig dc)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new InRoomPc Device\");\n      return new InRoomPc(dc.Key, dc.Name);\n    }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.Sources\n{\n    /// <summary>\n    /// Represents a Laptop\n    /// </summary>\n    public class Laptop : EssentialsDevice, IHasFeedback, IRoutingSource, IRoutingOutputs, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking\n    {\n        /// <summary>\n        /// Gets or sets the DisplayUiType\n        /// </summary>\n        public uint DisplayUiType { get { return DisplayUiConstants.TypeLaptop; } }\n        /// <summary>\n        /// Gets or sets the IconName\n        /// </summary>\n        public string IconName { get; set; }\n        /// <summary>\n        /// Gets or sets the HasPowerOnFeedback\n        /// </summary>\n        public BoolFeedback HasPowerOnFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the AnyVideoOut\n        /// </summary>\n        public RoutingOutputPort AnyVideoOut { get; private set; }\n\n        #region IRoutingOutputs Members\n\n        /// <summary>\n        /// Gets or sets the OutputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }\n\n        #endregion\n\n        /// <summary>\n        /// Initializes a new instance of the Laptop class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        public Laptop(string key, string name)\n            : base(key, name)\n        {\n            IconName = \"Laptop\";\n\n            HasPowerOnFeedback = new BoolFeedback(\"HasPowerFeedback\",\n                () => this.GetVideoStatuses() != VideoStatusOutputs.NoStatus);\n\n            OutputPorts = new RoutingPortCollection<RoutingOutputPort>\n            {\n                (AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n                eRoutingPortConnectionType.None, 0, this))\n            };\n        }\n\n        #region IHasFeedback Members\n\n        /// <summary>\n        /// Passes through the VideoStatuses list\n        /// </summary>\n        public FeedbackCollection<Feedback> Feedbacks\n        {\n            get\n            {\n                var newList = new FeedbackCollection<Feedback>();\n                newList.AddRange(this.GetVideoStatuses().ToList());\n                return newList;\n            }\n        }\n\n        #endregion\n\n        #region IUsageTracking Members\n\n        /// <summary>\n        /// Gets or sets the UsageTracker\n        /// </summary>\n        public UsageTracking UsageTracker { get; set; }\n\n        #endregion\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Sources/LaptopFactory.cs",
    "content": "using System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Sources\n{\n  /// <summary>\n  /// Represents a LaptopFactory\n  /// </summary>\n  public class LaptopFactory : EssentialsDeviceFactory<Laptop>\n  {\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"LaptopFactory\"/> class\n    /// </summary>\n    public LaptopFactory()\n    {\n      TypeNames = new List<string>() { \"laptop\" };\n    }\n\n    /// <summary>\n    /// BuildDevice method\n    /// </summary>\n    /// <inheritdoc />\n    public override EssentialsDevice BuildDevice(DeviceConfig dc)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Laptop Device\");\n      return new Laptop(dc.Key, dc.Name);\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Streaming/AppleTV.cs",
    "content": "﻿using System.Linq;\nusing System.Reflection;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n    /// <summary>\n    /// Represents a AppleTV\n    /// Wrapper class for an IR-Controlled AppleTV\n    /// </summary>\n    [Description(\"Wrapper class for an IR-Controlled AppleTV\")]\n    public class AppleTV : EssentialsBridgeableDevice, IDPad, ITransport, IUiDisplayInfo, IRoutingSource, IRoutingOutputs\n\n    {\n        /// <summary>\n        /// Gets or sets the IrPort\n        /// </summary>\n\t\tpublic IrOutputPortController IrPort { get; private set; }\n\n        /// <summary>\n        /// Standard Driver Name\n        /// </summary>\n        public const string StandardDriverName = \"Apple_AppleTV_4th_Gen_Essentials.ir\";\n        /// <summary>\n        /// Gets or sets the DisplayUiType\n        /// </summary>\n        public uint DisplayUiType { get { return DisplayUiConstants.TypeAppleTv; } }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"AppleTV\"/> class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        /// <param name=\"portCont\">The IR output port controller</param>\n        public AppleTV(string key, string name, IrOutputPortController portCont)\n            : base(key, name)\n        {\n            IrPort = portCont;\n            DeviceManager.AddDevice(portCont);\n\n            HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n                eRoutingPortConnectionType.Hdmi, null, this);\n            AnyAudioOut = new RoutingOutputPort(RoutingPortNames.AnyAudioOut, eRoutingSignalType.Audio,\n                eRoutingPortConnectionType.DigitalAudio, null, this);\n            OutputPorts = new RoutingPortCollection<RoutingOutputPort> { HdmiOut, AnyAudioOut };\n\n            PrintExpectedIrCommands();\n        }\n\n        /// <summary>\n        /// PrintExpectedIrCommands method\n        /// </summary>\n        public void PrintExpectedIrCommands()\n        {\n            var cmds = typeof(AppleTvIrCommands).GetFields(BindingFlags.Public | BindingFlags.Static);\n\n            foreach (var value in cmds.Select(cmd => cmd.GetValue(null)).OfType<string>())\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Expected IR Function Name: {0}\", value);\n            }\n        }\n\n        #region IDPad Members\n\n        /// <summary>\n        /// Up method\n        /// </summary>\n        public void Up(bool pressRelease)\n        {\n            IrPort.PressRelease(AppleTvIrCommands.Up, pressRelease);\n        }\n\n        /// <summary>\n        /// Down method\n        /// </summary>\n        public void Down(bool pressRelease)\n        {\n            IrPort.PressRelease(AppleTvIrCommands.Down, pressRelease);\n        }\n\n        /// <summary>\n        /// Left method\n        /// </summary>\n        public void Left(bool pressRelease)\n        {\n            IrPort.PressRelease(AppleTvIrCommands.Left, pressRelease);\n        }\n\n        /// <summary>\n        /// Right method\n        /// </summary>\n        public void Right(bool pressRelease)\n        {\n            IrPort.PressRelease(AppleTvIrCommands.Right, pressRelease);\n        }\n\n        /// <summary>\n        /// Select method\n        /// </summary>\n        public void Select(bool pressRelease)\n        {\n            IrPort.PressRelease(AppleTvIrCommands.Enter, pressRelease);\n        }\n\n        /// <summary>\n        /// Menu method\n        /// </summary>\n        public void Menu(bool pressRelease)\n        {\n            IrPort.PressRelease(AppleTvIrCommands.Menu, pressRelease);\n        }\n\n        /// <summary>\n        /// Exit method\n        /// </summary>\n        public void Exit(bool pressRelease)\n        {\n\n        }\n\n        #endregion\n\n        #region ITransport Members\n\n        /// <summary>\n        /// Play method\n        /// </summary>\n        public void Play(bool pressRelease)\n        {\n            IrPort.PressRelease(AppleTvIrCommands.PlayPause, pressRelease);\n        }\n\n        /// <summary>\n        /// Pause method\n        /// </summary>\n        public void Pause(bool pressRelease)\n        {\n            IrPort.PressRelease(AppleTvIrCommands.PlayPause, pressRelease);\n        }\n\n        /// <summary>\n        /// Not implemented\n        /// </summary>\n        /// <param name=\"pressRelease\"></param>\n        /// <summary>\n        /// Rewind method\n        /// </summary>\n        public void Rewind(bool pressRelease)\n        {\n        }\n\n        /// <summary>\n        /// Not implemented\n        /// </summary>\n        /// <param name=\"pressRelease\"></param>\n        public void FFwd(bool pressRelease)\n        {\n        }\n\n        /// <summary>\n        /// Not implemented\n        /// </summary>\n        /// <param name=\"pressRelease\"></param>\n        public void ChapMinus(bool pressRelease)\n        {\n        }\n\n        /// <summary>\n        /// Not implemented\n        /// </summary>\n        /// <param name=\"pressRelease\"></param>\n        public void ChapPlus(bool pressRelease)\n        {\n        }\n\n        /// <summary>\n        /// Not implemented\n        /// </summary>\n        /// <param name=\"pressRelease\"></param>\n        public void Stop(bool pressRelease)\n        {\n        }\n\n        /// <summary>\n        /// Not implemented\n        /// </summary>\n        /// <param name=\"pressRelease\"></param>\n        public void Record(bool pressRelease)\n        {\n        }\n\n        #endregion\n\n        #region IRoutingOutputs Members\n\n        /// <summary>\n        /// Gets the HdmiOut\n        /// </summary>\n        public RoutingOutputPort HdmiOut { get; private set; }\n\n        /// <summary>\n        /// Gets the AnyAudioOut\n        /// </summary>\n        public RoutingOutputPort AnyAudioOut { get; private set; }\n        /// <summary>\n        /// Gets or sets the OutputPorts\n        /// </summary>\n        public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }\n\n        #endregion\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            var joinMap = new AppleTvJoinMap(joinStart);\n\n            var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);\n\n            if (!string.IsNullOrEmpty(joinMapSerialized))\n                joinMap = JsonConvert.DeserializeObject<AppleTvJoinMap>(joinMapSerialized);\n\n            if (bridge != null)\n            {\n                bridge.AddJoinMap(Key, joinMap);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Information, this, \"Please update config to use 'eiscapiadvanced' to get all join map features for this device.\");\n            }\n\n            Debug.LogMessage(LogEventLevel.Debug, \"Linking to Trilist '{0}'\", trilist.ID.ToString(\"X\"));\n            Debug.LogMessage(LogEventLevel.Information, \"Linking to Bridge Type {0}\", GetType().Name);\n\n            trilist.SetBoolSigAction(joinMap.UpArrow.JoinNumber, Up);\n            trilist.SetBoolSigAction(joinMap.DnArrow.JoinNumber, Down);\n            trilist.SetBoolSigAction(joinMap.LeftArrow.JoinNumber, Left);\n            trilist.SetBoolSigAction(joinMap.RightArrow.JoinNumber, Right);\n            trilist.SetBoolSigAction(joinMap.Select.JoinNumber, Select);\n            trilist.SetBoolSigAction(joinMap.Menu.JoinNumber, Menu);\n            trilist.SetBoolSigAction(joinMap.PlayPause.JoinNumber, Play);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Streaming/AppleTVFactory.cs",
    "content": "using System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n  /// <summary>\n  /// Represents a AppleTVFactory\n  /// </summary>\n  public class AppleTVFactory : EssentialsDeviceFactory<AppleTV>\n  {\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"AppleTVFactory\"/> class\n    /// </summary>\n    public AppleTVFactory()\n    {\n      TypeNames = new List<string>() { \"appletv\" };\n    }\n\n    /// <summary>\n    /// BuildDevice method\n    /// </summary>\n    /// <inheritdoc />\n    public override EssentialsDevice BuildDevice(DeviceConfig dc)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new AppleTV Device\");\n      var irCont = IRPortHelper.GetIrOutputPortController(dc);\n      return new AppleTV(dc.Key, dc.Name, irCont);\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Streaming/AppleTvIrCommands.cs",
    "content": "using Crestron.SimplSharpPro;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n  /// <summary>\n  /// Represents AppleTvIrCommands\n  /// </summary>\n  public static class AppleTvIrCommands\n  {\n\n    /// <summary>\n    /// Up command\n    /// </summary>\n    public static string Up = \"+\";\n\n    /// <summary>\n    /// Down command\n    /// </summary>\n    public static string Down = \"-\";\n\n    /// <summary>\n    /// Left command\n    /// </summary>\n    public static string Left = IROutputStandardCommands.IROut_TRACK_MINUS;\n\n    /// <summary>\n    /// Right command\n    /// </summary>\n    public static string Right = IROutputStandardCommands.IROut_TRACK_PLUS;\n\n    /// <summary>\n    /// Enter command\n    /// </summary>\n    public static string Enter = IROutputStandardCommands.IROut_ENTER;\n\n    /// <summary>\n    /// PlayPause command\n    /// </summary>\n    public static string PlayPause = \"PLAY/PAUSE\";\n\n    /// <summary>\n    /// Rewind command\n    /// </summary>\n    public static string Rewind = \"REWIND\";\n\n    /// <summary>\n    /// Menu command\n    /// </summary>\n    public static string Menu = \"Menu\";\n\n    /// <summary>\n    /// FastForward command\n    /// </summary>\n    public static string FastForward = \"FASTFORWARD\";\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Streaming/Roku.cs",
    "content": "﻿using Crestron.SimplSharpPro;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n\t/// <summary>\n\t/// Represents a Roku2\n\t/// Wrapper class for an IR-Controlled Roku\n\t/// </summary>\n\t[Description(\"Wrapper class for an IR-Controlled Roku\")]\n\tpublic class Roku2 : EssentialsDevice, IDPad, ITransport, IUiDisplayInfo, IRoutingSource, IRoutingOutputs\n\t{\n\t\t/// <summary>\n\t\t/// Gets or sets the IrPort\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic IrOutputPortController IrPort { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Standard Driver Name\n\t\t/// </summary>\n\t\tpublic const string StandardDriverName = \"Roku XD_S.ir\";\n\t\t/// <summary>\n\t\t/// Gets or sets the DisplayUiType\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic uint DisplayUiType { get { return DisplayUiConstants.TypeRoku; } }\n\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the <see cref=\"Roku2\"/> class\n\t\t/// </summary>\n\t\tpublic Roku2(string key, string name, IrOutputPortController portCont)\n\t\t\t: base(key, name)\n\t\t{\n\t\t\tIrPort = portCont;\n\t\t\tDeviceManager.AddDevice(portCont); ;\n\n\t\t\tHdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,\n\t\t\t\teRoutingPortConnectionType.Hdmi, null, this);\n\t\t\tOutputPorts = new RoutingPortCollection<RoutingOutputPort> { HdmiOut };\n\t\t}\n\n\t\t#region IDPad Members\n\n\t\t/// <summary>\n\t\t/// Up method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Up(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_UP_ARROW, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Down method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Down(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_DN_ARROW, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Left method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Left(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_LEFT_ARROW, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Right method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Right(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_RIGHT_ARROW, pressRelease);\n\t\t}\n\n\n\t\t/// <summary>\n\t\t/// Select method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Select(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_ENTER, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Menu method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Menu(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_MENU, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Exit method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Exit(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_EXIT, pressRelease);\n\t\t}\n\n\t\t#endregion\n\n\t\t#region ITransport Members\n\n\t\t/// <summary>\n\t\t/// Play method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Play(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_PLAY, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Pause method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Pause(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_PAUSE, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Rewind method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void Rewind(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_RSCAN, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// FFwd method\n\t\t/// </summary>\n\t\t[Api]\n\t\tpublic void FFwd(bool pressRelease)\n\t\t{\n\t\t\tIrPort.PressRelease(IROutputStandardCommands.IROut_FSCAN, pressRelease);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// ChapMinus method - Not implemented\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\"></param>\n\t\tpublic void ChapMinus(bool pressRelease)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Not implemented\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\"></param>\n\t\tpublic void ChapPlus(bool pressRelease)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Not implemented\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\"></param>\n\t\tpublic void Stop(bool pressRelease)\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Not implemented\n\t\t/// </summary>\n\t\t/// <param name=\"pressRelease\"></param>\n\t\tpublic void Record(bool pressRelease)\n\t\t{\n\t\t}\n\n\t\t#endregion\n\n\t\t#region IRoutingOutputs Members\n\n\t\t/// <summary>\n\t\t/// HdmiOut\n\t\t/// </summary>\n\t\tpublic RoutingOutputPort HdmiOut { get; private set; }\n\n\t\t/// <summary>\n\t\t/// OutputPorts\n\t\t/// </summary>\n\t\tpublic RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }\n\n\t\t#endregion\n\n\t}\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/Streaming/Roku2Factory.cs",
    "content": "using System.Collections.Generic;\n\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common\n{\n  /// <summary>\n  /// Represents a Roku2Factory\n  /// </summary>\n  public class Roku2Factory : EssentialsDeviceFactory<Roku2>\n  {\n    /// <summary>\n    /// Roku2Factory constructor\n    /// </summary>\n    public Roku2Factory()\n    {\n      TypeNames = new List<string>() { \"roku\" };\n    }\n\n    /// <summary>\n    /// BuildDevice method\n    /// </summary>\n    /// <inheritdoc />\n    public override EssentialsDevice BuildDevice(DeviceConfig dc)\n    {\n      Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new Roku Device\");\n      var irCont = IRPortHelper.GetIrOutputPortController(dc);\n      return new Roku2(dc.Key, dc.Name, irCont);\n\n    }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/CiscoCodec/CallHistoryDataClasses.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Represents a CiscoCallHistory\n    /// </summary>\n    public class CiscoCallHistory\n    {\n        /// <summary>\n        /// Represents a CallbackNumber\n        /// </summary>\n        public class CallbackNumber\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a DisplayName\n        /// </summary>\n        public class DisplayName\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a LastOccurrenceStartTime\n        /// </summary>\n        public class LastOccurrenceStartTime\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public DateTime Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a LastOccurrenceDaysAgo\n        /// </summary>\n        public class LastOccurrenceDaysAgo\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a LastOccurrenceHistoryId\n        /// </summary>\n        public class LastOccurrenceHistoryId\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a OccurrenceType\n        /// </summary>\n        public class OccurrenceType\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a IsAcknowledged\n        /// </summary>\n        public class IsAcknowledged\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a OccurrenceCount\n        /// </summary>\n        public class OccurrenceCount\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a Entry\n        /// </summary>\n        public class Entry\n        {\n            /// <summary>\n            /// Gets or sets the id\n            /// </summary>\n            public string id { get; set; }\n            /// <summary>\n            /// Gets or sets the CallbackNumber\n            /// </summary>\n            public CallbackNumber CallbackNumber { get; set; }\n            /// <summary>\n            /// Gets or sets the DisplayName\n            /// </summary>\n            public DisplayName DisplayName { get; set; }\n            /// <summary>\n            /// Gets or sets the LastOccurrenceStartTime\n            /// </summary>\n            public LastOccurrenceStartTime LastOccurrenceStartTime { get; set; }\n            /// <summary>\n            /// Gets or sets the LastOccurrenceDaysAgo\n            /// </summary>\n            public LastOccurrenceDaysAgo LastOccurrenceDaysAgo { get; set; }\n            /// <summary>\n            /// Gets or sets the LastOccurrenceHistoryId\n            /// </summary>\n            public LastOccurrenceHistoryId LastOccurrenceHistoryId { get; set; }\n            /// <summary>\n            /// Gets or sets the OccurrenceType\n            /// </summary>\n            public OccurrenceType OccurrenceType { get; set; }\n            /// <summary>\n            /// Gets or sets the IsAcknowledged\n            /// </summary>\n            public IsAcknowledged IsAcknowledged { get; set; }\n            /// <summary>\n            /// Gets or sets the OccurrenceCount\n            /// </summary>\n            public OccurrenceCount OccurrenceCount { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a Offset\n        /// </summary>\n        public class Offset\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a Limit\n        /// </summary>\n        public class Limit\n        {\n            /// <summary>\n            /// Gets or sets the Value\n            /// </summary>\n            public string Value { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a ResultInfo\n        /// </summary>\n        public class ResultInfo\n        {\n            /// <summary>\n            /// Gets or sets the Offset\n            /// </summary>\n            public Offset Offset { get; set; }\n            /// <summary>\n            /// Gets or sets the Limit\n            /// </summary>\n            public Limit Limit { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a CallHistoryRecentsResult\n        /// </summary>\n        public class CallHistoryRecentsResult\n        {\n            /// <summary>\n            /// Gets or sets the status\n            /// </summary>\n            public string status { get; set; }\n            /// <summary>\n            /// Gets or sets the Entry\n            /// </summary>\n            public List<Entry> Entry { get; set; }\n            /// <summary>\n            /// Gets or sets the ResultInfo\n            /// </summary>\n            public ResultInfo ResultInfo { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a CommandResponse\n        /// </summary>\n        public class CommandResponse\n        {\n            /// <summary>\n            /// Gets or sets the CallHistoryRecentsResult\n            /// </summary>\n            public CallHistoryRecentsResult CallHistoryRecentsResult { get; set; }\n        }\n\n        /// <summary>\n        /// Represents a RootObject\n        /// </summary>\n        public class RootObject\n        {\n            /// <summary>\n            /// Gets or sets the CommandResponse\n            /// </summary>\n            public CommandResponse CommandResponse { get; set; }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/CiscoCodec/RoomPresets.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing PepperDash.Essentials.Core.Presets;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Interface for camera presets\n    /// </summary>\n    public interface IHasCodecRoomPresets\n    {\n        /// <summary>\n        /// Event that is raised when the list of room presets has changed.\n        /// </summary>\n        event EventHandler<EventArgs> CodecRoomPresetsListHasChanged;\n\n        /// <summary>\n        /// List of near end presets that can be recalled.\n        /// </summary>\n        List<CodecRoomPreset> NearEndPresets { get; }\n\n        /// <summary>\n        /// List of far end presets that can be recalled.\n        /// </summary>\n        List<CodecRoomPreset> FarEndRoomPresets { get; }\n\n        /// <summary>\n        /// Selects a near end preset by its ID.\n        /// </summary>\n        /// <param name=\"preset\"></param>\n        void CodecRoomPresetSelect(int preset);\n\n        /// <summary>\n        /// Stores a near end preset with the given ID and description.\n        /// </summary>\n        /// <param name=\"preset\"></param>\n        /// <param name=\"description\"></param>\n        void CodecRoomPresetStore(int preset, string description);\n\n        /// <summary>\n        /// Selects a far end preset by its ID. This is typically used to recall a preset that has been defined on the far end codec.\n        /// </summary>\n        /// <param name=\"preset\"></param>\n        void SelectFarEndPreset(int preset);\n    }\n\n    /// <summary>\n    /// Static class for converting non-generic RoomPresets to generic CameraPresets.\n    /// </summary>\n    public static class RoomPresets\n    {\n        /// <summary>\n        /// Converts non-generic RoomPresets to generic CameraPresets\n        /// </summary>\n        /// <param name=\"presets\"></param>\n        /// <returns></returns>\n        public static List<TDestination> GetGenericPresets<TSource, TDestination>(this List<TSource> presets) where TSource : ConvertiblePreset where TDestination : PresetBase\n        {\n            return\n                presets.Select(preset => preset.ConvertCodecPreset())\n                    .Where(newPreset => newPreset != null)\n                    .Cast<TDestination>()\n                    .ToList();\n        }\n    }\n\n    /// <summary>\n    /// Represents a CodecRoomPreset\n    /// </summary>\n    public class CodecRoomPreset : PresetBase\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"id\"></param>\n        /// <param name=\"description\"></param>\n        /// <param name=\"def\"></param>\n        /// <param name=\"isDef\"></param>\n        public CodecRoomPreset(int id, string description, bool def, bool isDef)\n            : base(id, description, def, isDef)\n        {\n\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/CiscoCodec/eCommandType.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco\n{\n    enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration };\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/CiscoCodec/eExternalSourceMode.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco\n{\n    /// <summary>\n    /// Enumeration of eExternalSourceMode values\n    /// </summary>\n    public enum eExternalSourceMode\n    {\n        /// <summary>\n        /// Ready state\n        /// </summary>\n        Ready,\n        /// <summary>\n        /// Not ready state\n        /// </summary>\n        NotReady,\n        /// <summary>\n        /// Hidden state\n        /// </summary>\n        Hidden,\n        /// <summary>\n        /// Error state\n        /// </summary>\n        Error\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/CiscoCodec/eExternalSourceType.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco\n{\n    /// <summary>\n    /// Enumeration of eExternalSourceType values\n    /// </summary>\n    public enum eExternalSourceType\n    {\n        /// <summary>\n        /// Camera source type\n        /// </summary>\n        camera,\n        /// <summary>\n        /// Desktop source type\n        /// </summary>\n        desktop,\n        /// <summary>\n        /// Document camera source type\n        /// </summary>\n        document_camera,\n        /// <summary>\n        /// Media player source type\n        /// </summary>\n        mediaplayer,\n        /// <summary>\n        /// PC source type\n        /// </summary>\n        PC,\n        /// <summary>\n        /// Whiteboard source type\n        /// </summary>\n        whiteboard,\n        /// <summary>\n        /// Other source type\n        /// </summary>\n        other\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/CodecCommandWithLabel.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n  /// <summary>\n  /// Represents a CodecCommandWithLabel\n  /// </summary>\n  public class CodecCommandWithLabel\n  {\n    /// <summary>\n    /// Gets or sets the Command\n    /// </summary>\n    public string Command { get; private set; }\n    /// <summary>\n    /// Gets or sets the Label\n    /// </summary>\n    public string Label { get; private set; }\n\n    /// <summary>\n    /// Constructor for <see cref=\"CodecCommandWithLabel\"/>\n    /// </summary>\n    /// <param name=\"command\">Command string</param>\n    /// <param name=\"label\">Label string</param>\n    public CodecCommandWithLabel(string command, string label)\n    {\n      Command = command;\n      Label = label;\n    }\n  }\n\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/CodecPhonebookSyncState.cs",
    "content": "using System;\nusing PepperDash.Core;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n  /// <summary>\n  /// Represents a CodecPhonebookSyncState\n  /// </summary>\n  public class CodecPhonebookSyncState : IKeyed\n  {\n    private bool _InitialSyncComplete;\n\n    /// <summary>\n    /// Constructor for CodecPhonebookSyncState\n    /// </summary>\n    /// <param name=\"key\">Key for the codec phonebook sync state</param>\n    public CodecPhonebookSyncState(string key)\n    {\n      Key = key;\n\n      CodecDisconnected();\n    }\n\n    /// <summary>\n    /// Gets or sets the InitialSyncComplete\n    /// </summary>\n    public bool InitialSyncComplete\n    {\n      get { return _InitialSyncComplete; }\n      private set\n      {\n        if (value == true)\n        {\n          InitialSyncCompleted?.Invoke(this, new EventArgs());\n        }\n        _InitialSyncComplete = value;\n      }\n    }\n\n    /// <summary>\n    /// Gets or sets the InitialPhonebookFoldersWasReceived\n    /// </summary>\n    public bool InitialPhonebookFoldersWasReceived { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the NumberOfContactsWasReceived\n    /// </summary>\n    public bool NumberOfContactsWasReceived { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the PhonebookRootEntriesWasRecieved\n    /// </summary>\n    public bool PhonebookRootEntriesWasRecieved { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the PhonebookHasFolders\n    /// </summary>\n    public bool PhonebookHasFolders { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the NumberOfContacts\n    /// </summary>\n    public int NumberOfContacts { get; private set; }\n\n    #region IKeyed Members\n\n    /// <summary>\n    /// Gets or sets the Key\n    /// </summary>\n    public string Key { get; private set; }\n\n    #endregion\n\n    /// <summary>\n    /// Event InitialSyncCompleted\n    /// </summary>\n    public event EventHandler<EventArgs> InitialSyncCompleted;\n\n    /// <summary>\n    /// InitialPhonebookFoldersReceived method\n    /// </summary>\n    public void InitialPhonebookFoldersReceived()\n    {\n      InitialPhonebookFoldersWasReceived = true;\n\n      CheckSyncStatus();\n    }\n\n    /// <summary>\n    /// PhonebookRootEntriesReceived method\n    /// </summary>\n    public void PhonebookRootEntriesReceived()\n    {\n      PhonebookRootEntriesWasRecieved = true;\n\n      CheckSyncStatus();\n    }\n\n    /// <summary>\n    /// SetPhonebookHasFolders method\n    /// </summary>\n    public void SetPhonebookHasFolders(bool value)\n    {\n      PhonebookHasFolders = value;\n\n      Debug.LogMessage(LogEventLevel.Debug, this, \"Phonebook has folders: {0}\", PhonebookHasFolders);\n    }\n\n    /// <summary>\n    /// SetNumberOfContacts method\n    /// </summary>\n    public void SetNumberOfContacts(int contacts)\n    {\n      NumberOfContacts = contacts;\n      NumberOfContactsWasReceived = true;\n\n      Debug.LogMessage(LogEventLevel.Debug, this, \"Phonebook contains {0} contacts.\", NumberOfContacts);\n\n      CheckSyncStatus();\n    }\n\n    /// <summary>\n    /// CodecDisconnected method\n    /// </summary>\n    public void CodecDisconnected()\n    {\n      InitialPhonebookFoldersWasReceived = false;\n      PhonebookHasFolders = false;\n      NumberOfContacts = 0;\n      NumberOfContactsWasReceived = false;\n    }\n\n    private void CheckSyncStatus()\n    {\n      if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved)\n      {\n        InitialSyncComplete = true;\n        Debug.LogMessage(LogEventLevel.Debug, this, \"Initial Phonebook Sync Complete!\");\n      }\n      else\n      {\n        InitialSyncComplete = false;\n      }\n    }\n  }\n\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/ConvertiblePreset.cs",
    "content": "﻿using PepperDash.Essentials.Core.Presets;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Base class for presets that can be converted to PresetBase\n    /// </summary>\n    public abstract class ConvertiblePreset\n    {\n        /// <summary>\n        /// Converts the preset to a PresetBase\n        /// </summary>\n        /// <returns><see cref=\"PresetBase\"/></returns>\n        public abstract PresetBase ConvertCodecPreset();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/CodecParticipants.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n  /// <summary>\n  /// Represents a CodecParticipants\n  /// </summary>\n  public class CodecParticipants\n  {\n    private List<Participant> _currentParticipants;\n\n    /// <summary>\n    /// Gets or sets the CurrentParticipants\n    /// </summary>\n    public List<Participant> CurrentParticipants\n    {\n      get { return _currentParticipants; }\n      set\n      {\n        _currentParticipants = value;\n        OnParticipantsChanged();\n      }\n    }\n\n    /// <summary>\n    /// Gets the Host participant\n    /// </summary>\n    public Participant Host\n    {\n      get\n      {\n        return _currentParticipants.FirstOrDefault(p => p.IsHost);\n      }\n    }\n\n    /// <summary>\n    /// Event fired when the participants list has changed\n    /// </summary>\n    public event EventHandler<EventArgs> ParticipantsListHasChanged;\n\n    /// <summary>\n    /// Initializes a new instance of the CodecParticipants class\n    /// </summary>\n    public CodecParticipants()\n    {\n      _currentParticipants = new List<Participant>();\n    }\n\n    /// <summary>\n    /// OnParticipantsChanged method\n    /// </summary>\n    public void OnParticipantsChanged()\n    {\n      var handler = ParticipantsListHasChanged;\n\n      if (handler == null) return;\n\n      handler(this, new EventArgs());\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasCodecLayouts.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Defines the required elements for layout control\n    /// </summary>\n    public interface IHasCodecLayouts\n    {\n        /// <summary>\n        /// Feedback that indicates the current layout on the local display\n        /// </summary>\n        StringFeedback LocalLayoutFeedback { get; }\n\n        /// <summary>\n        /// Toggles the local layout\n        /// </summary>\n        void LocalLayoutToggle();\n\n        /// <summary>\n        /// Toggles the local layout to single prominent\n        /// </summary>\n        void LocalLayoutToggleSingleProminent();\n\n        /// <summary>\n        /// Toggle the MinMax layout\n        /// </summary>\n        void MinMaxLayoutToggle();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasCodecSelfview.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Defines the requred elements for selfview control\n    /// </summary>\n    public interface IHasCodecSelfView\n    {\n        /// <summary>\n        /// Feedback that indicates whether Selfview is on\n        /// </summary>\n        BoolFeedback SelfviewIsOnFeedback { get; }\n\n        /// <summary>\n        /// Setting that indicates whether the device shows selfview by default\n        /// </summary>\n        bool ShowSelfViewByDefault { get; }\n\n        /// <summary>\n        /// Turns selfview on\n        /// </summary>\n        void SelfViewModeOn();\n\n        /// <summary>\n        /// Turns selfview off\n        /// </summary>\n        void SelfViewModeOff();\n\n        /// <summary>\n        /// Toggles selfview mode\n        /// </summary>\n        void SelfViewModeToggle();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasMeetingInfo.cs",
    "content": "﻿\n\nusing System;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n    /// <summary>\n    /// Describes a device that provides meeting information (like a ZoomRoom)\n    /// </summary>\n    public interface IHasMeetingInfo\n    {\n        /// <summary>\n        /// Raised when meeting info changes\n        /// </summary>\n        event EventHandler<MeetingInfoEventArgs> MeetingInfoChanged;\n\n        /// <summary>\n        /// Gets the current meeting information\n        /// </summary>\n        MeetingInfo MeetingInfo { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasMeetingLock.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n    /// <summary>\n    /// Defines the contract for IHasMeetingLock\n    /// </summary>\n    public interface IHasMeetingLock\n    {\n        /// <summary>\n        /// Feedback that indicates whether the meeting is locked\n        /// </summary>\n        BoolFeedback MeetingIsLockedFeedback { get; }\n\n        /// <summary>\n        /// Locks the meeting\n        /// </summary>\n        void LockMeeting();\n\n        /// <summary>\n        /// Unlocks the meeting\n        /// </summary>\n        void UnLockMeeting();\n\n        /// <summary>\n        /// Toggles the meeting lock state\n        /// </summary>\n        void ToggleMeetingLock();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasMeetingRecording.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n    /// <summary>\n    /// Defines the contract for IHasMeetingRecording\n    /// </summary>\n    public interface IHasMeetingRecording\n    {\n        /// <summary>\n        /// Feedback that indicates whether the meeting is being recorded\n        /// </summary>\n        BoolFeedback MeetingIsRecordingFeedback { get; }\n\n        /// <summary>\n        /// Starts recording the meeting\n        /// </summary>\n        void StartRecording();\n\n        /// <summary>\n        /// Stops recording the meeting\n        /// </summary>\n        void StopRecording();\n\n        /// <summary>\n        /// Toggles recording the meeting\n        /// </summary>\n        void ToggleRecording();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasMeetingRecordingWithPrompt.cs",
    "content": "using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n  /// <summary>\n  /// Defines the contract for IHasMeetingRecordingWithPrompt\n  /// </summary>\n  public interface IHasMeetingRecordingWithPrompt : IHasMeetingRecording\n  {\n    /// <summary>\n    /// Feedback that indicates whether the recording consent prompt is visible\n    /// </summary>\n    BoolFeedback RecordConsentPromptIsVisible { get; }\n\n    /// <summary>\n    /// Used to agree or disagree to the meeting being recorded when prompted\n    /// </summary>\n    /// <param name=\"agree\"></param>\n    void RecordingPromptAcknowledgement(bool agree);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasParticipantAudioMute.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n  /// <summary>\n  /// Defines the contract for IHasParticipantAudioMute\n  /// </summary>\n  public interface IHasParticipantAudioMute : IHasParticipantVideoMute\n  {\n    /// <summary>\n    /// Mute audio of all participants\n    /// </summary>\n    void MuteAudioForAllParticipants();\n\n    /// <summary>\n    /// Mute audio for participant\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to mute</param>\n    void MuteAudioForParticipant(int userId);\n\n    /// <summary>\n    /// Unmute audio for participant\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to unmute</param>\n    void UnmuteAudioForParticipant(int userId);\n\n    /// <summary>\n    /// Toggles audio for participant\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to toggle</param>\n    void ToggleAudioForParticipant(int userId);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasParticipantPinUnpin.cs",
    "content": "using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n  /// <summary>\n  /// Defines the contract for IHasParticipantPinUnpin\n  /// </summary>\n  public interface IHasParticipantPinUnpin : IHasParticipants\n  {\n    /// <summary>\n    /// Feedback that indicates the number of screens available for pinning participants\n    /// </summary>\n    IntFeedback NumberOfScreensFeedback { get; }\n\n    /// <summary>\n    /// Gets the screen index to pin the user to\n    /// </summary>\n    int ScreenIndexToPinUserTo { get; }\n\n    /// <summary>\n    /// Pins a participant to a screen\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to pin</param>\n    /// <param name=\"screenIndex\">The screen index to pin the user to</param>\n    void PinParticipant(int userId, int screenIndex);\n\n    /// <summary>\n    /// Unpins a participant\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to unpin</param>\n    void UnPinParticipant(int userId);\n\n    /// <summary>\n    /// Toggles the pin state of a participant\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to toggle</param>\n    /// <param name=\"screenIndex\">The screen index to pin the user to</param>\n    void ToggleParticipantPinState(int userId, int screenIndex);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasParticipantVideoMute.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n  /// <summary>\n  /// Describes the ability to mute and unmute a participant's video in a meeting\n  /// </summary>\n  public interface IHasParticipantVideoMute : IHasParticipants\n  {\n    /// <summary>\n    /// Mute video of all participants\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to mute</param>\n    void MuteVideoForParticipant(int userId);\n\n    /// <summary>\n    /// Unmute video of all participants\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to unmute</param>\n    void UnmuteVideoForParticipant(int userId);\n\n    /// <summary>\n    /// Toggles video of a participant\n    /// </summary>\n    /// <param name=\"userId\">The user ID of the participant to toggle</param>\n    void ToggleVideoForParticipant(int userId);\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasParticipants.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n\t/// <summary>\n\t/// Describes a device that has call participants\n\t/// </summary>\n\tpublic interface IHasParticipants\n\t{\n\t\t/// <summary>\n\t\t/// Gets the collection of participants\n\t\t/// </summary>\n\t\tCodecParticipants Participants { get; }\n\n\t\t/// <summary>\n\t\t/// Removes the participant from the meeting\n\t\t/// </summary>\n\t\t/// <param name=\"userId\"></param>\n\t\tvoid RemoveParticipant(int userId);\n\n\t\t/// <summary>\n\t\t/// Sets the participant as the new host\n\t\t/// </summary>\n\t\t/// <param name=\"userId\"></param>\n\t\tvoid SetParticipantAsHost(int userId);\n\n\t\t/// <summary>\n\t\t/// Admits a participant from the waiting room\n\t\t/// </summary>\n\t\t/// <param name=\"userId\"></param>\n\t\tvoid AdmitParticipantFromWaitingRoom(int userId);\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n    /// <summary>\n    /// Defines the contract for IHasPresentationOnlyMeeting\n    /// </summary>\n    public interface IHasPresentationOnlyMeeting\n    {\n        /// <summary>\n        /// Starts a presentation only meeting\n        /// </summary>\n        void StartSharingOnlyMeeting();\n\n        /// <summary>\n        /// Starts a presentation only meeting with specified display mode\n        /// </summary>\n        /// <param name=\"displayMode\">The display mode for the meeting</param>\n        void StartSharingOnlyMeeting(eSharingMeetingMode displayMode);\n\n        /// <summary>\n        /// Starts a presentation only meeting with specified display mode and duration\n        /// </summary>\n        /// <param name=\"displayMode\">The display mode for the meeting</param>\n        /// <param name=\"duration\">The duration for the meeting</param>\n        void StartSharingOnlyMeeting(eSharingMeetingMode displayMode, uint duration);\n\n        /// <summary>\n        /// Starts a presentation only meeting with specified display mode, duration, and password\n        /// </summary>\n        /// <param name=\"displayMode\">The display mode for the meeting</param>\n        /// <param name=\"duration\">The duration for the meeting</param>\n        /// <param name=\"password\">The password for the meeting</param>\n        void StartSharingOnlyMeeting(eSharingMeetingMode displayMode, uint duration, string password);\n\n        /// <summary>\n        /// Starts a normal meeting from a sharing only meeting\n        /// </summary>\n        void StartNormalMeetingFromSharingOnlyMeeting();\n    }\n\n    /// <summary>\n    /// Enumeration of eSharingMeetingMode values\n    /// </summary>\n    public enum eSharingMeetingMode\n    {\n        /// <summary>\n        /// No specific sharing mode\n        /// </summary>\n        None,\n        /// <summary>\n        /// Laptop sharing mode\n        /// </summary>\n        Laptop,\n        /// <summary>\n        /// iOS sharing mode\n        /// </summary>\n        Ios,\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasSelfviewPosition.cs",
    "content": "﻿using PepperDash.Essentials.Devices.Common.VideoCodec;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n    /// <summary>\n    /// Defines the contract for IHasSelfviewPosition\n    /// </summary>\n    public interface IHasSelfviewPosition\n    {\n        /// <summary>\n        /// Gets the SelfviewPipPositionFeedback\n        /// </summary>\n        StringFeedback SelfviewPipPositionFeedback { get; }\n\n        /// <summary>\n        /// Sets the selfview position\n        /// </summary>\n        void SelfviewPipPositionSet(CodecCommandWithLabel position);\n\n        /// <summary>\n        /// Toggles the selfview position\n        /// </summary>\n        void SelfviewPipPositionToggle();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasSelfviewSize.cs",
    "content": "﻿using PepperDash.Essentials.Devices.Common.VideoCodec;\n\nnamespace PepperDash.Essentials.Core.DeviceTypeInterfaces\n{\n\t/// <summary>\n\t/// Defines the contract for IHasSelfviewSize\n\t/// </summary>\n\tpublic interface IHasSelfviewSize\n\t{\n\t\t/// <summary>\n\t\t/// Gets the SelfviewPipSizeFeedback\n\t\t/// </summary>\n\t\tStringFeedback SelfviewPipSizeFeedback { get; }\n\n\t\t/// <summary>\n\t\t/// Sets the selfview size\n\t\t/// </summary>\n\t\t/// <param name=\"size\">The new selfview size</param>\n\t\tvoid SelfviewPipSizeSet(CodecCommandWithLabel size);\n\n\t\t/// <summary>\n\t\t/// Toggles the selfview size\n\t\t/// </summary>\n\t\tvoid SelfviewPipSizeToggle();\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasStandbyMode.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Describes a device that has Standby Mode capability\n    /// </summary>\n    public interface IHasStandbyMode\n    {\n        /// <summary>\n        /// Feedback that indicates whether Standby Mode is on\n        /// </summary>\n        BoolFeedback StandbyIsOnFeedback { get; }\n\n        /// <summary>\n        /// Activates Standby Mode\n        /// </summary>\n        void StandbyActivate();\n\n        /// <summary>\n        /// Deactivates Standby Mode\n        /// </summary>\n        void StandbyDeactivate();\n    }\n\n    /// <summary>\n    /// Defines the contract for IHasHalfWakeMode\n    /// </summary>\n    public interface IHasHalfWakeMode : IHasStandbyMode\n    {\n        /// <summary>\n        /// Feedback that indicates whether Half Wake Mode is on\n        /// </summary>\n        BoolFeedback HalfWakeModeIsOnFeedback { get; }\n\n        /// <summary>\n        /// Feedback that indicates whether the device is entering Standby Mode\n        /// </summary>\n        BoolFeedback EnteringStandbyModeFeedback { get; }\n\n        /// <summary>\n        /// Activates Half Wake Mode\n        /// </summary>\n        void HalfwakeActivate();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasStartMeeting.cs",
    "content": "﻿namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n    /// <summary>\n    /// Describes the ability to start an ad-hoc meeting\n    /// </summary>\n    public interface IHasStartMeeting\n    {\n        /// <summary>\n        /// The default meeting duration in minutes\n        /// </summary>\n        uint DefaultMeetingDurationMin { get; }\n\n        /// <summary>\n        /// Start an ad-hoc meeting for the specified duration\n        /// </summary>\n        /// <param name=\"duration\"></param>\n        void StartMeeting(uint duration);\n\n        /// <summary>\n        /// Leaves a meeting without ending it\n        /// </summary>\n        void LeaveMeeting();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IHasVideoCodec.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n\t/// <summary>\n\t/// For rooms that have video codec\n\t/// </summary>\n\tpublic interface IHasVideoCodec : IHasInCallFeedback, IPrivacy\n\t{\n\t\t/// <summary>\n\t\t/// Gets the VideoCodecBase instance\n\t\t/// </summary>\n\t\tVideoCodecBase VideoCodec { get; }\n\n\t\t/// <summary>\n\t\t/// States: 0 for on hook, 1 for video, 2 for audio, 3 for telekenesis\n\t\t/// </summary>\n\t\tIntFeedback CallTypeFeedback { get; }\n\n\t\t/// <summary>\n\t\t/// When something in the room is sharing with the far end or through other means\n\t\t/// </summary>\n\t\tBoolFeedback IsSharingFeedback { get; }\n\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/IJoinCalls.cs",
    "content": "﻿using PepperDash.Essentials.Devices.Common.Codec;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Defines the contract for IJoinCalls\n    /// </summary>\n    public interface IJoinCalls\n    {\n        /// <summary>\n        /// Joins a call\n        /// </summary>\n        /// <param name=\"activeCall\">The active call to join</param>\n        void JoinCall(CodecActiveCallItem activeCall);\n\n        /// <summary>\n        /// Joins all calls\n        /// </summary>\n        void JoinAllCalls();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/MeetingInfo.cs",
    "content": "using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n  /// <summary>\n  /// Represents a MeetingInfo\n  /// </summary>\n  public class MeetingInfo\n  {\n\n    /// <summary>\n    /// Gets or sets the Id\n    /// </summary>\n    [JsonProperty(\"id\", NullValueHandling = NullValueHandling.Ignore)]\n    public string Id { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the Name\n    /// </summary>\n    [JsonProperty(\"name\", NullValueHandling = NullValueHandling.Ignore)]\n    public string Name { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the Host\n    /// </summary>\n    [JsonProperty(\"host\", NullValueHandling = NullValueHandling.Ignore)]\n    public string Host { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the Password\n    /// </summary>\n    [JsonProperty(\"password\", NullValueHandling = NullValueHandling.Ignore)]\n    public string Password { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the ShareStatus\n    /// </summary>\n    [JsonProperty(\"shareStatus\", NullValueHandling = NullValueHandling.Ignore)]\n    public string ShareStatus { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the IsHost\n    /// </summary>\n    [JsonProperty(\"isHost\", NullValueHandling = NullValueHandling.Ignore)]\n    public bool IsHost { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the IsSharingMeeting\n    /// </summary>\n    [JsonProperty(\"isSharingMeeting\", NullValueHandling = NullValueHandling.Ignore)]\n    public bool IsSharingMeeting { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the WaitingForHost\n    /// </summary>\n    [JsonProperty(\"waitingForHost\", NullValueHandling = NullValueHandling.Ignore)]\n    public bool WaitingForHost { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the IsLocked\n    /// </summary>\n    [JsonProperty(\"isLocked\", NullValueHandling = NullValueHandling.Ignore)]\n    public bool IsLocked { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the IsRecording\n    /// </summary>\n    [JsonProperty(\"isRecording\", NullValueHandling = NullValueHandling.Ignore)]\n    public bool IsRecording { get; private set; }\n\n    /// <summary>\n    /// Gets or sets the CanRecord\n    /// </summary>\n    [JsonProperty(\"canRecord\", NullValueHandling = NullValueHandling.Ignore)]\n    public bool CanRecord { get; private set; }\n\n    /// <summary>\n    /// Constructor for MeetingInfo\n    /// </summary>\n    /// <param name=\"id\">The unique identifier for the meeting</param>\n    /// <param name=\"name\">The name of the meeting</param>\n    /// <param name=\"host\">The host of the meeting</param>\n    /// <param name=\"password\">The password for the meeting</param>\n    /// <param name=\"shareStatus\">The share status of the meeting</param>\n    /// <param name=\"isHost\">Indicates whether the current user is the host</param>\n    /// <param name=\"isSharingMeeting\">Indicates whether the meeting is currently being shared</param>\n    /// <param name=\"waitingForHost\">Indicates whether the meeting is waiting for the host to join</param>\n    /// <param name=\"isLocked\">Indicates whether the meeting is locked</param>\n    /// <param name=\"isRecording\">Indicates whether the meeting is being recorded</param>\n    /// <param name=\"canRecord\">Indicates whether the meeting can be recorded</param>\n    public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting, bool waitingForHost, bool isLocked, bool isRecording, bool canRecord)\n    {\n      Id = id;\n      Name = name;\n      Host = host;\n      Password = password;\n      ShareStatus = shareStatus;\n      IsHost = isHost;\n      IsSharingMeeting = isSharingMeeting;\n      WaitingForHost = waitingForHost;\n      IsLocked = isLocked;\n      IsRecording = isRecording;\n      CanRecord = CanRecord;\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/MeetingInfoEventArgs.cs",
    "content": "\n\nusing System;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n  /// <summary>\n  /// Represents a MeetingInfoEventArgs\n  /// </summary>\n  public class MeetingInfoEventArgs : EventArgs\n  {\n    /// <summary>\n    /// Gets or sets the Info\n    /// </summary>\n    public MeetingInfo Info { get; private set; }\n\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"MeetingInfoEventArgs\"/> class.\n    /// </summary>\n    /// <param name=\"info\">The meeting information.</param>\n    public MeetingInfoEventArgs(MeetingInfo info)\n    {\n      Info = info;\n    }\n\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/Participant.cs",
    "content": "namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces\n{\n  /// <summary>\n  /// Represents a Participant\n  /// </summary>\n  public class Participant\n  {\n    /// <summary>\n    /// Gets or sets the UserId\n    /// </summary>\n    public int UserId { get; set; }\n\n    /// <summary>\n    /// Gets or sets the IsHost\n    /// </summary>\n    public bool IsHost { get; set; }\n\n    /// <summary>\n    /// Gets or sets the IsMyself\n    /// </summary>\n    public bool IsMyself { get; set; }\n\n    /// <summary>\n    /// Gets or sets the Name\n    /// </summary>\n    public string Name { get; set; }\n\n    /// <summary>\n    /// Gets or sets the Email\n    /// </summary>\n    public bool CanMuteVideo { get; set; }\n\n    /// <summary>\n    /// Gets or sets the CanUnmuteVideo\n    /// </summary>\n    public bool CanUnmuteVideo { get; set; }\n\n    /// <summary>\n    /// Gets or sets the CanMuteAudio\n    /// </summary>\n    public bool VideoMuteFb { get; set; }\n\n    /// <summary>\n    /// Gets or sets the AudioMuteFb\n    /// </summary>\n    public bool AudioMuteFb { get; set; }\n\n    /// <summary>\n    /// Gets or sets the HandIsRaisedFb\n    /// </summary>\n    public bool HandIsRaisedFb { get; set; }\n\n    /// <summary>\n    /// Gets or sets the IsPinnedFb\n    /// </summary>\n    public bool IsPinnedFb { get; set; }\n\n    /// <summary>\n    /// Gets or sets the ScreenIndexIsPinnedToFb\n    /// </summary>\n    public int ScreenIndexIsPinnedToFb { get; set; }\n\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"Participant\"/> class\n    /// </summary>\n    public Participant()\n    {\n      // Initialize to -1 (no screen)\n      ScreenIndexIsPinnedToFb = -1;\n    }\n  }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/Interfaces/iVideoCodecInfo.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.Devices.Common.Codec\n{\n    /// <summary>\n    /// Implements a common set of data about a codec\n    /// </summary>\n    public interface iVideoCodecInfo\n    {\n        /// <summary>\n        /// Gets the codec information\n        /// </summary>\n        VideoCodecInfo CodecInfo { get; }\n    }\n\n    /// <summary>\n    /// Stores general information about a codec\n    /// </summary>\n    public abstract class VideoCodecInfo\n    {\n        /// <summary>\n        /// Gets a value indicating whether the multi-site option is enabled\n        /// </summary>\n        [JsonProperty(\"multiSiteOptionIsEnabled\", NullValueHandling = NullValueHandling.Ignore)]\n        public abstract bool MultiSiteOptionIsEnabled { get; }\n        /// <summary>\n        /// Gets the IP address of the codec\n        /// </summary>\n        [JsonProperty(\"ipAddress\", NullValueHandling = NullValueHandling.Ignore)]\n        public abstract string IpAddress { get; }\n        /// <summary>\n        /// Gets the SIP phone number for the codec\n        /// </summary>\n        [JsonProperty(\"sipPhoneNumber\", NullValueHandling = NullValueHandling.Ignore)]\n        public abstract string SipPhoneNumber { get; }\n        /// <summary>\n        /// Gets the E164 alias for the codec\n        /// </summary>\n        [JsonProperty(\"e164Alias\", NullValueHandling = NullValueHandling.Ignore)]\n        public abstract string E164Alias { get; }\n        /// <summary>\n        /// Gets the H323 ID for the codec\n        /// </summary>\n        [JsonProperty(\"h323Id\", NullValueHandling = NullValueHandling.Ignore)]\n        public abstract string H323Id { get; }\n        /// <summary>\n        /// Gets the SIP URI for the codec\n        /// </summary>\n        [JsonProperty(\"sipUri\", NullValueHandling = NullValueHandling.Ignore)]\n        public abstract string SipUri { get; }\n        /// <summary>\n        /// Gets a value indicating whether auto-answer is enabled\n        /// </summary>\n        [JsonProperty(\"autoAnswerEnabled\", NullValueHandling = NullValueHandling.Ignore)]\n        public abstract bool AutoAnswerEnabled { get; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/MockVC/MockCodecDirectory.cs",
    "content": "﻿using System.Collections.Generic;\nusing PepperDash.Essentials.Devices.Common.Codec;\n\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Mock video codec directory structure\n    /// </summary>\n    public static class MockVideoCodecDirectory\n    {\n        /// <summary>\n        /// Enumeration of eFolderId values\n        /// </summary>\n        public enum eFolderId\n        {\n            /// <summary>\n            /// The United States folder\n            /// </summary>\n            UnitedStates,\n            /// <summary>\n            /// The Canada folder\n            /// </summary>\n            Canada,\n            /// <summary>\n            /// The New York folder\n            /// </summary>\n            NewYork,\n            /// <summary>\n            /// The Boston folder\n            /// </summary>\n            Boston,\n            /// <summary>\n            /// The San Francisco folder\n            /// </summary>\n            SanFrancisco,\n            /// <summary>\n            /// The Denver folder\n            /// </summary>\n            Denver,\n            /// <summary>\n            /// The Austin folder\n            /// </summary>\n            Austin,\n            /// <summary>\n            /// The Calgary folder\n            /// </summary>\n            Calgary\n        }\n\n\n        /// <summary>\n        /// Aggregates the directory items for all directories into a single directory for searching purposes\n        /// </summary>\n        public static CodecDirectory CompleteDirectory\n        {\n            get\n            {\n                var completeDirectory = new CodecDirectory();\n\n                completeDirectory.AddContactsToDirectory(DirectoryRoot.CurrentDirectoryResults);\n                completeDirectory.AddContactsToDirectory(UnitedStatesFolderContents.CurrentDirectoryResults);\n                completeDirectory.AddContactsToDirectory(CanadaFolderContents.CurrentDirectoryResults);\n                completeDirectory.AddContactsToDirectory(NewYorkFolderContents.CurrentDirectoryResults);\n                completeDirectory.AddContactsToDirectory(BostonFolderContents.CurrentDirectoryResults);\n                completeDirectory.AddContactsToDirectory(DenverFolderContents.CurrentDirectoryResults);\n                completeDirectory.AddContactsToDirectory(AustinFolderContents.CurrentDirectoryResults);\n                completeDirectory.AddContactsToDirectory(CalgaryFolderContents.CurrentDirectoryResults);\n\n                return completeDirectory;\n            }\n        }\n\n        /// <summary>\n        /// The root of the directory structure\n        /// </summary>\n        public static CodecDirectory DirectoryRoot\n        {\n            get\n            {\n                var directory = new CodecDirectory();\n\n                directory.AddFoldersToDirectory\n                (\n                    new List<DirectoryItem>()\n                    {\n                        new DirectoryFolder()\n                        {\n                            FolderId = eFolderId.UnitedStates.ToString(),\n                            Name = \"United States\",\n                            ParentFolderId = \"\",\n                            Contacts = null\n                        },\n                        new DirectoryFolder()\n                        {\n                            FolderId = eFolderId.Canada.ToString(),\n                            Name = \"Canada\",\n                            ParentFolderId = \"\",\n                            Contacts = null\n                        }\n                    }\n                );\n\n                directory.AddContactsToDirectory\n                (\n                    new List<DirectoryItem>()\n                    {\n                        new DirectoryContact()\n                        {\n                            Name = \"Corporate Bridge\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"c_1\",\n                                    Number = \"site.corp.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        }\n                    }\n                );\n\n                return directory;\n            }\n        }\n\n        /// <summary>\n        /// Contents of the United States folder\n        /// </summary>\n        public static CodecDirectory UnitedStatesFolderContents\n        {\n            get\n            {\n                var directory = new CodecDirectory\n                {\n                    ResultsFolderId = eFolderId.UnitedStates.ToString()\n                };\n                directory.AddFoldersToDirectory\n                        (\n                            new List<DirectoryItem>()\n                            {\n                        new DirectoryFolder()\n                        {\n                            FolderId = eFolderId.NewYork.ToString(),\n                            Name = \"New York\",\n                            ParentFolderId = eFolderId.UnitedStates.ToString(),\n                            Contacts = null\n                        },\n                        new DirectoryFolder()\n                        {\n                            FolderId = eFolderId.Boston.ToString(),\n                            Name = \"Boston\",\n                            ParentFolderId = eFolderId.UnitedStates.ToString(),\n                            Contacts = null\n                        },\n                        new DirectoryFolder()\n                        {\n                            FolderId = eFolderId.SanFrancisco.ToString(),\n                            Name = \"San Francisco\",\n                            ParentFolderId = eFolderId.UnitedStates.ToString(),\n                            Contacts = null\n                        },\n                        new DirectoryFolder()\n                        {\n                            FolderId = eFolderId.Denver.ToString(),\n                            Name = \"Denver\",\n                            ParentFolderId = eFolderId.UnitedStates.ToString(),\n                            Contacts = null\n                        },\n                        new DirectoryFolder()\n                        {\n                            FolderId = eFolderId.Austin.ToString(),\n                            Name = \"Austin\",\n                            ParentFolderId = eFolderId.UnitedStates.ToString(),\n                            Contacts = null\n                        }\n                            }\n                        );\n\n                return directory;\n            }\n        }\n\n        /// <summary>\n        /// Contents of the New York folder\n        /// </summary>\n        public static CodecDirectory NewYorkFolderContents\n        {\n            get\n            {\n                var directory = new CodecDirectory\n                {\n                    ResultsFolderId = eFolderId.NewYork.ToString()\n                };\n                directory.AddContactsToDirectory\n                        (\n                            new List<DirectoryItem>()\n                            {\n                        new DirectoryContact()\n                        {\n                            ContactId = \"nyc_1\",\n                            Name = \"Meeting Room\",\n                            Title = @\"\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"cid_1\",\n                                    Number = \"nycmeetingroom.pepperdash.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        },\n                        new DirectoryContact()\n                        {\n                            ContactId = \"nyc_2\",\n                            Name = \"Sumanth Rayancha\",\n                            Title = @\"CTO\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"cid_1\",\n                                    Number = \"srayancha.pepperdash.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        },\n                        new DirectoryContact()\n                        {\n                            ContactId = \"nyc_3\",\n                            Name = \"Justin Gordon\",\n                            Title = @\"Software Developer\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"cid_1\",\n                                    Number = \"jgordon.pepperdash.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        }\n                            }\n                        );\n\n                return directory;\n            }\n        }\n\n        /// <summary>\n        /// Contents of the Boston folder\n        /// </summary>\n        public static CodecDirectory BostonFolderContents\n        {\n            get\n            {\n                var directory = new CodecDirectory\n                {\n                    ResultsFolderId = eFolderId.Boston.ToString()\n                };\n                directory.AddContactsToDirectory\n                        (\n                            new List<DirectoryItem>()\n                            {\n                        new DirectoryContact()\n                        {\n                            ContactId = \"bos_1\",\n                            Name = \"Board Room\",\n                            Title = @\"\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"cid_1\",\n                                    Number = \"bosboardroom.pepperdash.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        }\n                            }\n                        );\n\n                return directory;\n            }\n        }\n\n        /// <summary>\n        /// Contents of the San Francisco folder\n        /// </summary>\n        public static CodecDirectory SanFranciscoFolderContents\n        {\n            get\n            {\n                var directory = new CodecDirectory\n                {\n                    ResultsFolderId = eFolderId.SanFrancisco.ToString()\n                };\n                directory.AddContactsToDirectory\n                        (\n                            new List<DirectoryItem>()\n                            {\n                        new DirectoryContact()\n                        {\n                            ContactId = \"sfo_1\",\n                            Name = \"David Huselid\",\n                            Title = @\"Cive President, COO\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"cid_1\",\n                                    Number = \"dhuselid.pepperdash.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        }\n                            }\n                        );\n\n                return directory;\n            }\n        }\n\n        /// <summary>\n        /// Contents of the Denver folder\n        /// </summary>\n        public static CodecDirectory DenverFolderContents\n        {\n            get\n            {\n                var directory = new CodecDirectory\n                {\n                    ResultsFolderId = eFolderId.Denver.ToString()\n                };\n                directory.AddContactsToDirectory\n                        (\n                            new List<DirectoryItem>()\n                            {\n                        new DirectoryContact()\n                        {\n                            ContactId = \"den_1\",\n                            Name = \"Heath Volmer\",\n                            Title = @\"Software Developer\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"cid_1\",\n                                    Number = \"hvolmer.pepperdash.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        }\n                            }\n                        );\n\n                return directory;\n            }\n        }\n\n        /// <summary>\n        /// Contents of the Austin folder\n        /// </summary>\n        public static CodecDirectory AustinFolderContents\n        {\n            get\n            {\n                var directory = new CodecDirectory\n                {\n                    ResultsFolderId = eFolderId.Austin.ToString()\n                };\n                directory.AddContactsToDirectory\n                        (\n                            new List<DirectoryItem>()\n                            {\n                        new DirectoryContact()\n                        {\n                            ContactId = \"atx_1\",\n                            Name = \"Vincent Longano\",\n                            Title = @\"Product Development Manager\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"cid_1\",\n                                    Number = \"vlongano.pepperdash.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        }\n                            }\n                        );\n\n                return directory;\n            }\n        }\n\n        /// <summary>\n        /// Contents of the Canada folder\n        /// </summary>\n        public static CodecDirectory CanadaFolderContents\n        {\n            get\n            {\n                var directory = new CodecDirectory\n                {\n                    ResultsFolderId = eFolderId.Canada.ToString()\n                };\n                directory.AddFoldersToDirectory\n                        (\n                            new List<DirectoryItem>()\n                            {\n                        new DirectoryFolder()\n                        {\n                            FolderId = eFolderId.Calgary.ToString(),\n                            Name = \"Calgary\",\n                            ParentFolderId = eFolderId.Canada.ToString(),\n                            Contacts = null\n                        }\n                            }\n                        );\n\n                return directory;\n            }\n        }\n\n        /// <summary>\n        /// Contents of the Calgary folder\n        /// </summary>\n        public static CodecDirectory CalgaryFolderContents\n        {\n            get\n            {\n                var directory = new CodecDirectory\n                {\n                    ResultsFolderId = eFolderId.Calgary.ToString()\n                };\n                directory.AddContactsToDirectory\n                        (\n                            new List<DirectoryItem>()\n                            {\n                        new DirectoryContact()\n                        {\n                            ContactId = \"cdn_1\",\n                            Name = \"Neil Dorin\",\n                            Title = @\"Software Developer /SC\",\n                            ContactMethods = new List<ContactMethod>()\n                            {\n                                new ContactMethod()\n                                {\n                                    ContactMethodId = \"cid_1\",\n                                    Number = \"ndorin@pepperdash.com\",\n                                    Device = eContactMethodDevice.Video,\n                                    CallType = eContactMethodCallType.Video\n                                }\n                            }\n                        }\n                            }\n                        );\n\n                return directory;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/MockVC/MockVC.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Routing;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing PepperDash.Essentials.Devices.Common.Codec;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Represents a MockVC\n    /// </summary>\n    public class MockVC : VideoCodecBase, IRoutingSource, IHasCallHistory, IHasScheduleAwareness, IHasCallFavorites, IHasDirectory, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets\n    {\n        /// <summary>\n        /// Gets or sets the PropertiesConfig\n        /// </summary>\n        public MockVcPropertiesConfig PropertiesConfig;\n\n        /// <summary>\n        /// Gets or sets the CodecOsdIn\n        /// </summary>\n        public RoutingInputPort CodecOsdIn { get; private set; }\n        /// <summary>\n        /// Gets or sets the HdmiIn1\n        /// </summary>\n        public RoutingInputPort HdmiIn1 { get; private set; }\n        /// <summary>\n        /// Gets or sets the HdmiIn2\n        /// </summary>\n        public RoutingInputPort HdmiIn2 { get; private set; }\n        /// <summary>\n        /// Gets or sets the HdmiOut\n        /// </summary>\n        public RoutingOutputPort HdmiOut { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the CallFavorites\n        /// </summary>\n        public CodecCallFavorites CallFavorites { get; private set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        public MockVC(DeviceConfig config)\n            : base(config)\n        {\n            PropertiesConfig = JsonConvert.DeserializeObject<VideoCodec.MockVcPropertiesConfig>(config.Properties.ToString());\n\n            CodecInfo = new MockCodecInfo();\n\n            // Get favoritesw\n            if (PropertiesConfig.Favorites != null)\n            {\n                CallFavorites = new CodecCallFavorites();\n                CallFavorites.Favorites = PropertiesConfig.Favorites;\n            }\n\n            DirectoryBrowseHistory = new List<CodecDirectory>();\n\n            // Debug helpers\n            MuteFeedback.OutputChange += (o, a) => Debug.LogMessage(LogEventLevel.Debug, this, \"Mute={0}\", _IsMuted);\n            PrivacyModeIsOnFeedback.OutputChange += (o, a) => Debug.LogMessage(LogEventLevel.Debug, this, \"Privacy={0}\", _PrivacyModeIsOn);\n            SharingSourceFeedback.OutputChange += (o, a) => Debug.LogMessage(LogEventLevel.Debug, this, \"SharingSource={0}\", _SharingSource);\n            VolumeLevelFeedback.OutputChange += (o, a) => Debug.LogMessage(LogEventLevel.Debug, this, \"Volume={0}\", _VolumeLevel);\n\n            CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(\"currentDirectoryResultIsNotDirectoryRoot\", () => DirectoryBrowseHistory.Count > 0);\n\n            CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate();\n\n            CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, 0, this);\n            InputPorts.Add(CodecOsdIn);\n            HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, 1, this);\n            InputPorts.Add(HdmiIn1);\n            HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, 2, this);\n            InputPorts.Add(HdmiIn2);\n            HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, null, this);\n            OutputPorts.Add(HdmiOut);\n\n            CallHistory = new CodecCallHistory();\n            for (int i = 0; i < 10; i++)\n            {\n                var call = new CodecCallHistory.CallHistoryEntry();\n                call.Name = \"Call \" + i;\n                call.Number = i + \"@call.com\";\n                CallHistory.RecentCalls.Add(call);\n            }\n            // eventually fire history event here\n\n            SetupCameras();\n\n            CreateOsdSource();\n\n            SetIsReady();\n        }\n\n        /// <inheritdoc />\n        protected override Func<bool> MuteFeedbackFunc\n        {\n            get { return () => _IsMuted; }\n        }\n        bool _IsMuted;\n\n        /// <inheritdoc />\n        protected override Func<bool> PrivacyModeIsOnFeedbackFunc\n        {\n            get { return () => _PrivacyModeIsOn; }\n        }\n        bool _PrivacyModeIsOn;\n\n        /// <inheritdoc />\n        protected override Func<string> SharingSourceFeedbackFunc\n        {\n            get { return () => _SharingSource; }\n        }\n        string _SharingSource;\n\n        /// <inheritdoc />\n        protected override Func<bool> SharingContentIsOnFeedbackFunc\n        {\n            get { return () => _SharingIsOn; }\n        }\n        bool _SharingIsOn;\n\n        /// <inheritdoc />\n        protected override Func<int> VolumeLevelFeedbackFunc\n        {\n            get { return () => _VolumeLevel; }\n        }\n        int _VolumeLevel;\n\n        /// <inheritdoc />\n        protected override Func<bool> StandbyIsOnFeedbackFunc\n        {\n            get { return () => _StandbyIsOn; }\n        }\n        bool _StandbyIsOn;\n\n        /// <summary>\n        /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input\n        /// to enable routing \n        /// </summary>\n        private void CreateOsdSource()\n        {\n            OsdSource = new DummyRoutingInputsDevice(Key + \"[osd]\");\n            DeviceManager.AddDevice(OsdSource);\n            var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn);\n            TieLineCollection.Default.Add(tl);\n\n            //foreach(var input in Status.Video.\n        }\n\n        /// <inheritdoc />\n        public override void Dial(string number)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Dial: {0}\", number);\n            var call = new CodecActiveCallItem() { Name = number, Number = number, Id = number, Status = eCodecCallStatus.Dialing, Direction = eCodecCallDirection.Outgoing, Type = eCodecCallType.Video };\n            ActiveCalls.Add(call);\n            OnCallStatusChange(call);\n            //ActiveCallCountFeedback.FireUpdate();\n            // Simulate 2-second ring, then connecting, then connected\n            new CTimer(o =>\n            {\n                call.Type = eCodecCallType.Video;\n                SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call);\n                new CTimer(oo => SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connected, call), 1000);\n            }, 2000);\n        }\n\n        /// <inheritdoc />\n        public override void Dial(Meeting meeting)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Dial Meeting: {0}\", meeting.Id);\n            var call = new CodecActiveCallItem() { Name = meeting.Title, Number = meeting.Id, Id = meeting.Id, Status = eCodecCallStatus.Dialing, Direction = eCodecCallDirection.Outgoing, Type = eCodecCallType.Video };\n            ActiveCalls.Add(call);\n            OnCallStatusChange(call);\n\n            //ActiveCallCountFeedback.FireUpdate();\n            // Simulate 2-second ring, then connecting, then connected\n            new CTimer(o =>\n            {\n                call.Type = eCodecCallType.Video;\n                SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call);\n                new CTimer(oo => SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connected, call), 1000);\n            }, 2000);\n\n        }\n\n        /// <inheritdoc />\n        public override void EndCall(CodecActiveCallItem call)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"EndCall\");\n            ActiveCalls.Remove(call);\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call);\n            //ActiveCallCountFeedback.FireUpdate();\n        }\n\n\n        /// <inheritdoc />\n        public override void EndAllCalls()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"EndAllCalls\");\n            for (int i = ActiveCalls.Count - 1; i >= 0; i--)\n            {\n                var call = ActiveCalls[i];\n                ActiveCalls.Remove(call);\n                SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call);\n            }\n            //ActiveCallCountFeedback.FireUpdate();\n        }\n\n\n        /// <inheritdoc />\n        public override void AcceptCall(CodecActiveCallItem call)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"AcceptCall\");\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call);\n            new CTimer(o => SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connected, call), 1000);\n            // should already be in active list\n        }\n\n        /// <inheritdoc />\n        public override void RejectCall(CodecActiveCallItem call)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"RejectCall\");\n            ActiveCalls.Remove(call);\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call);\n            //ActiveCallCountFeedback.FireUpdate();\n        }\n\n        /// <inheritdoc />\n        public override void SendDtmf(string s)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"SendDTMF: {0}\", s);\n        }\n\n        /// <inheritdoc />\n        public override void StartSharing()\n        {\n            _SharingIsOn = true;\n            SharingContentIsOnFeedback.FireUpdate();\n        }\n\n        /// <inheritdoc />\n        public override void StopSharing()\n        {\n            _SharingIsOn = false;\n            SharingContentIsOnFeedback.FireUpdate();\n        }\n\n        /// <inheritdoc />\n        public override void StandbyActivate()\n        {\n            _StandbyIsOn = true;\n        }\n\n        /// <inheritdoc />\n        public override void StandbyDeactivate()\n        {\n            _StandbyIsOn = false;\n        }\n\n        /// <inheritdoc />          \n        public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            throw new NotImplementedException();\n        }\n\n        /// <inheritdoc />\n        public override void ExecuteSwitch(object selector)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"ExecuteSwitch: {0}\", selector);\n            _SharingSource = selector.ToString();\n        }\n\n        /// <inheritdoc />\n        public override void MuteOff()\n        {\n            _IsMuted = false;\n            MuteFeedback.FireUpdate();\n        }\n\n        /// <inheritdoc />\n        public override void MuteOn()\n        {\n            _IsMuted = true;\n            MuteFeedback.FireUpdate();\n        }\n\n        /// <inheritdoc />\n        public override void MuteToggle()\n        {\n            _IsMuted = !_IsMuted;\n            MuteFeedback.FireUpdate();\n        }\n\n        /// <inheritdoc />\n        public override void SetVolume(ushort level)\n        {\n            _VolumeLevel = level;\n            VolumeLevelFeedback.FireUpdate();\n        }\n\n        /// <inheritdoc />\n        public override void VolumeDown(bool pressRelease)\n        {\n        }\n\n\n        /// <inheritdoc />\n        public override void VolumeUp(bool pressRelease)\n        {\n        }\n\n        /// <inheritdoc />\n        public override void PrivacyModeOn()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"PrivacyMuteOn\");\n            if (_PrivacyModeIsOn)\n                return;\n            _PrivacyModeIsOn = true;\n            PrivacyModeIsOnFeedback.FireUpdate();\n        }\n\n\n        /// <inheritdoc />\n        public override void PrivacyModeOff()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"PrivacyMuteOff\");\n            if (!_PrivacyModeIsOn)\n                return;\n            _PrivacyModeIsOn = false;\n            PrivacyModeIsOnFeedback.FireUpdate();\n        }\n\n\n        /// <inheritdoc />\n        public override void PrivacyModeToggle()\n        {\n            _PrivacyModeIsOn = !_PrivacyModeIsOn;\n            Debug.LogMessage(LogEventLevel.Debug, this, \"PrivacyMuteToggle: {0}\", _PrivacyModeIsOn);\n            PrivacyModeIsOnFeedback.FireUpdate();\n        }\n\n        //********************************************************\n        // SIMULATION METHODS\n\n        /// <summary>\n        /// TestIncomingVideoCall method\n        /// </summary>\n        /// <param name=\"url\"></param>        \n        public void TestIncomingVideoCall(string url)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"TestIncomingVideoCall from {0}\", url);\n            var call = new CodecActiveCallItem() { Name = url, Id = url, Number = url, Type = eCodecCallType.Video, Direction = eCodecCallDirection.Incoming };\n            ActiveCalls.Add(call);\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Ringing, call);\n\n            //OnCallStatusChange(eCodecCallStatus.Unknown, eCodecCallStatus.Ringing, call);\n\n        }\n\n        /// <summary>\n        /// TestIncomingAudioCall method\n        /// </summary>\n        /// <param name=\"url\"></param>        \n        public void TestIncomingAudioCall(string url)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"TestIncomingAudioCall from {0}\", url);\n            var call = new CodecActiveCallItem() { Name = url, Id = url, Number = url, Type = eCodecCallType.Audio, Direction = eCodecCallDirection.Incoming };\n            ActiveCalls.Add(call);\n            SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Ringing, call);\n\n            //OnCallStatusChange(eCodecCallStatus.Unknown, eCodecCallStatus.Ringing, call);\n        }\n\n        /// <summary>\n        /// TestFarEndHangup method\n        /// </summary>\n        public void TestFarEndHangup()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"TestFarEndHangup\");\n\n        }\n\n\n        #region IHasCallHistory Members\n\n        /// <summary>\n        /// CallHistory property\n        /// </summary>\n        public CodecCallHistory CallHistory { get; private set; }\n\n        /// <summary>\n        /// RemoveCallHistoryEntry method\n        /// </summary>\n        public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry)\n        {\n\n        }\n\n        #endregion\n\n        #region IHasScheduleAwareness Members\n\n        /// <summary>\n        /// GetSchedule method\n        /// </summary>\n        public void GetSchedule()\n        {\n\n        }\n\n        /// <summary>\n        /// CodecSchedule property\n        /// </summary>\n        public CodecScheduleAwareness CodecSchedule\n        {\n            get\n            {\n                // if the last meeting has past, generate a new list\n                if (_CodecSchedule == null || _CodecSchedule.Meetings.Count == 0\n                    || _CodecSchedule.Meetings[_CodecSchedule.Meetings.Count - 1].StartTime < DateTime.Now)\n                {\n                    _CodecSchedule = new CodecScheduleAwareness(1000);\n                    for (int i = 0; i < 5; i++)\n                    {\n                        var m = new Meeting();\n                        m.MinutesBeforeMeeting = 5;\n                        m.Id = i.ToString();\n                        m.Organizer = \"Employee \" + 1;\n                        m.StartTime = DateTime.Now.AddMinutes(5).AddHours(i);\n                        m.EndTime = DateTime.Now.AddHours(i).AddMinutes(50);\n                        m.Title = \"Meeting \" + i;\n                        m.Calls.Add(new Call() { Number = i + \"meeting@fake.com\" });\n                        _CodecSchedule.Meetings.Add(m);\n                    }\n                }\n                return _CodecSchedule;\n            }\n        }\n        CodecScheduleAwareness _CodecSchedule;\n\n        #endregion\n\n        #region IHasDirectory Members\n\n        /// <summary>\n        /// DirectoryResultReturned event. Fired when the directory result changes\n        /// </summary>\n        public event EventHandler<DirectoryEventArgs> DirectoryResultReturned;\n\n        /// <summary>\n        /// DirectoryRoot property. The root of the directory\n        /// </summary>\n        public CodecDirectory DirectoryRoot\n        {\n            get\n            {\n                return MockVideoCodecDirectory.DirectoryRoot;\n            }\n        }\n\n        /// <summary>\n        /// CurrentDirectoryResult property. The current directory result\n        /// </summary>\n        public CodecDirectory CurrentDirectoryResult\n        {\n            get\n            {\n                if (DirectoryBrowseHistory.Count > 0)\n                    return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1];\n                else\n                    return DirectoryRoot;\n            }\n        }\n\n        /// <summary>\n        /// PhonebookSyncState property. The current state of the phonebook synchronization\n        /// </summary>\n        public CodecPhonebookSyncState PhonebookSyncState\n        {\n            get\n            {\n                var syncState = new CodecPhonebookSyncState(Key + \"PhonebookSync\");\n\n                syncState.InitialPhonebookFoldersReceived();\n                syncState.PhonebookRootEntriesReceived();\n                syncState.SetPhonebookHasFolders(true);\n                syncState.SetNumberOfContacts(0);  // just need to call this method for the sync to complete\n\n                return syncState;\n            }\n        }\n\n        /// <summary>\n        /// Search the directory for contacts that contain the search string\n        /// </summary>\n        /// <param name=\"searchString\">The search string to use</param>\n        public void SearchDirectory(string searchString)\n        {\n            var searchResults = new CodecDirectory();\n\n            searchResults.ResultsFolderId = \"searchResult\";\n\n            // Search mock directory for contacts that contain the search string, ignoring case\n            List<DirectoryItem> matches = MockVideoCodecDirectory.CompleteDirectory.CurrentDirectoryResults.FindAll(\n                s => s is DirectoryContact && s.Name.ToLower().Contains(searchString.ToLower()));\n\n            if (matches != null)\n            {\n                searchResults.AddContactsToDirectory(matches);\n\n                DirectoryBrowseHistory.Add(searchResults);\n            }\n\n            OnDirectoryResultReturned(searchResults);\n        }\n\n        /// <summary>\n        /// Get the contents of the specified folder\n        /// </summary>\n        /// <param name=\"folderId\">The ID of the folder to get the contents of</param>\n        public void GetDirectoryFolderContents(string folderId)\n        {\n            var folderDirectory = new CodecDirectory();\n\n            if (folderId == MockVideoCodecDirectory.eFolderId.UnitedStates.ToString())\n                folderDirectory = MockVideoCodecDirectory.UnitedStatesFolderContents;\n            else if (folderId == MockVideoCodecDirectory.eFolderId.Canada.ToString())\n                folderDirectory = MockVideoCodecDirectory.CanadaFolderContents;\n            else if (folderId == MockVideoCodecDirectory.eFolderId.NewYork.ToString())\n                folderDirectory = MockVideoCodecDirectory.NewYorkFolderContents;\n            else if (folderId == MockVideoCodecDirectory.eFolderId.Boston.ToString())\n                folderDirectory = MockVideoCodecDirectory.BostonFolderContents;\n            else if (folderId == MockVideoCodecDirectory.eFolderId.SanFrancisco.ToString())\n                folderDirectory = MockVideoCodecDirectory.SanFranciscoFolderContents;\n            else if (folderId == MockVideoCodecDirectory.eFolderId.Denver.ToString())\n                folderDirectory = MockVideoCodecDirectory.DenverFolderContents;\n            else if (folderId == MockVideoCodecDirectory.eFolderId.Austin.ToString())\n                folderDirectory = MockVideoCodecDirectory.AustinFolderContents;\n            else if (folderId == MockVideoCodecDirectory.eFolderId.Calgary.ToString())\n                folderDirectory = MockVideoCodecDirectory.CalgaryFolderContents;\n\n            DirectoryBrowseHistory.Add(folderDirectory);\n\n            OnDirectoryResultReturned(folderDirectory);\n        }\n\n        /// <summary>\n        /// Set the current directory to the root\n        /// </summary>\n        public void SetCurrentDirectoryToRoot()\n        {\n            DirectoryBrowseHistory.Clear();\n\n            OnDirectoryResultReturned(DirectoryRoot);\n        }\n\n        /// <summary>\n        /// Get the contents of the parent folder\n        /// </summary>\n        public void GetDirectoryParentFolderContents()\n        {\n            var currentDirectory = new CodecDirectory();\n\n            if (DirectoryBrowseHistory.Count > 0)\n            {\n                var lastItemIndex = DirectoryBrowseHistory.Count - 1;\n                var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex];\n\n                DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]);\n\n                currentDirectory = parentDirectoryContents;\n\n            }\n            else\n            {\n                currentDirectory = DirectoryRoot;\n            }\n\n            OnDirectoryResultReturned(currentDirectory);\n        }\n\n        /// <summary>\n        /// Gets or sets the CurrentDirectoryResultIsNotDirectoryRoot\n        /// </summary>\n        public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the DirectoryBrowseHistory\n        /// </summary>\n        public List<CodecDirectory> DirectoryBrowseHistory { get; private set; }\n\n        /// <summary>\n        /// OnDirectoryResultReturned method\n        /// </summary>\n        public void OnDirectoryResultReturned(CodecDirectory result)\n        {\n            CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate();\n            DirectoryResultReturned?.Invoke(this, new DirectoryEventArgs()\n            {\n                Directory = result,\n                DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue\n            });\n        }\n\n        #endregion\n\n        void SetupCameras()\n        {\n            SupportsCameraAutoMode = true;\n\n            SupportsCameraOff = false;\n\n            Cameras = new List<CameraBase>();\n\n            var internalCamera = new MockVCCamera(Key + \"-camera1\", \"Near End\", this);\n\n            Cameras.Add(internalCamera);\n\n            var farEndCamera = new MockFarEndVCCamera(Key + \"-cameraFar\", \"Far End\", this);\n\n            Cameras.Add(farEndCamera);\n\n            SelectedCameraFeedback = new StringFeedback(\"selectedCamera\", () => SelectedCamera.Key);\n\n            ControllingFarEndCameraFeedback = new BoolFeedback(\"controllingFarEndCamera\", () => SelectedCamera is IAmFarEndCamera);\n\n            CameraAutoModeIsOnFeedback = new BoolFeedback(\"cameraAutoModeIsOn\", () => _CameraAutoModeIsOn);\n\n            SupportsCameraAutoMode = true;\n\n            CameraAutoModeIsOnFeedback.FireUpdate();\n\n            DeviceManager.AddDevice(internalCamera);\n            DeviceManager.AddDevice(farEndCamera);\n\n            NearEndPresets = new List<CodecRoomPreset>(15); // Fix the capacity to emulate Cisco\n\n            if (PropertiesConfig.Presets != null && PropertiesConfig.Presets.Count > 0)\n            {\n                NearEndPresets = PropertiesConfig.Presets;\n            }\n            else\n            {\n                for (int i = 1; i <= NearEndPresets.Capacity; i++)\n                {\n                    var label = string.Format(\"Near End Preset {0}\", i);\n                    NearEndPresets.Add(new CodecRoomPreset(i, label, true, false));\n                }\n            }\n\n            FarEndRoomPresets = new List<CodecRoomPreset>(15); // Fix the capacity to emulate Cisco\n\n            // Add the far end presets\n            for (int i = 1; i <= FarEndRoomPresets.Capacity; i++)\n            {\n                var label = string.Format(\"Far End Preset {0}\", i);\n                FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false));\n            }\n\n            SelectedCamera = internalCamera; ; // call the method to select the camera and ensure the feedbacks get updated.\n        }\n\n        #region IHasCameras Members\n\n        /// <summary>\n        /// CameraSelected event. Fired when a camera is selected\n        /// </summary>\n        public event EventHandler<CameraSelectedEventArgs> CameraSelected;\n\n        /// <summary>\n        /// Gets the list of cameras associated with this codec\n        /// </summary>\n        public List<CameraBase> Cameras { get; private set; }\n\n        private CameraBase _selectedCamera;\n\n        /// <summary>\n        /// Returns the selected camera\n        /// </summary>\n        public CameraBase SelectedCamera\n        {\n            get\n            {\n                return _selectedCamera;\n            }\n            private set\n            {\n                _selectedCamera = value;\n                SelectedCameraFeedback.FireUpdate();\n                ControllingFarEndCameraFeedback.FireUpdate();\n                CameraSelected?.Invoke(this, new CameraSelectedEventArgs(SelectedCamera));\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the SelectedCameraFeedback\n        /// </summary>\n        public StringFeedback SelectedCameraFeedback { get; private set; }\n\n        /// <summary>\n        /// SelectCamera method\n        /// </summary>\n        public void SelectCamera(string key)\n        {\n            var camera = Cameras.FirstOrDefault(c => c.Key.ToLower().IndexOf(key.ToLower()) > -1);\n            if (camera != null)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Selected Camera with key: '{0}'\", camera.Key);\n                SelectedCamera = camera;\n            }\n            else\n                Debug.LogMessage(LogEventLevel.Verbose, this, \"Unable to select camera with key: '{0}'\", key);\n        }\n\n        #endregion\n\n        #region IHasFarEndCameraControl Members\n\n        /// <summary>\n        /// Gets or sets the FarEndCamera\n        /// </summary>\n        public CameraBase FarEndCamera { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the ControllingFarEndCameraFeedback\n        /// </summary>\n        public BoolFeedback ControllingFarEndCameraFeedback { get; private set; }\n\n        #endregion\n\n        #region IHasCameraAutoMode Members\n\n        private bool _CameraAutoModeIsOn;\n\n        /// <summary>\n        /// CameraAutoModeOn method\n        /// </summary>\n        public void CameraAutoModeOn()\n        {\n            _CameraAutoModeIsOn = true;\n            CameraAutoModeIsOnFeedback.FireUpdate();\n        }\n\n        /// <summary>\n        /// CameraAutoModeOff method\n        /// </summary>\n        public void CameraAutoModeOff()\n        {\n            _CameraAutoModeIsOn = false;\n            CameraAutoModeIsOnFeedback.FireUpdate();\n        }\n\n        /// <summary>\n        /// CameraAutoModeToggle method\n        /// </summary>\n        public void CameraAutoModeToggle()\n        {\n            if (_CameraAutoModeIsOn)\n                _CameraAutoModeIsOn = false;\n            else\n                _CameraAutoModeIsOn = true;\n\n            CameraAutoModeIsOnFeedback.FireUpdate();\n\n        }\n\n        /// <summary>\n        /// Gets or sets the CameraAutoModeIsOnFeedback\n        /// </summary>\n        public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; }\n\n        #endregion\n\n        #region IHasCameraPresets Members\n\n        /// <summary>\n        /// CodecRoomPresetsListHasChanged event. Fired when the presets list changes\n        /// </summary>\n        public event EventHandler<EventArgs> CodecRoomPresetsListHasChanged;\n\n        /// <summary>\n        /// Gets or sets the NearEndPresets\n        /// </summary>\n        public List<CodecRoomPreset> NearEndPresets { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the FarEndRoomPresets\n        /// </summary>\n        public List<CodecRoomPreset> FarEndRoomPresets { get; private set; }\n\n        /// <summary>\n        /// CodecRoomPresetSelect method\n        /// </summary>\n        public void CodecRoomPresetSelect(int preset)\n        {\n            if (SelectedCamera is IAmFarEndCamera)\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Selecting Far End Preset: {0}\", preset);\n            }\n            else\n            {\n                Debug.LogMessage(LogEventLevel.Debug, this, \"Selecting Near End Preset: {0}\", preset);\n            }\n        }\n\n        /// <summary>\n        /// CodecRoomPresetStore method\n        /// </summary>\n        public void CodecRoomPresetStore(int preset, string description)\n        {\n            var editPreset = NearEndPresets.FirstOrDefault(p => p.ID.Equals(preset));\n\n            if (editPreset != null)\n            {\n                editPreset.Defined = true;\n                editPreset.Description = description;\n            }\n            else\n                NearEndPresets.Add(new CodecRoomPreset(preset, description, true, true));\n\n            CodecRoomPresetsListHasChanged?.Invoke(this, new EventArgs());\n\n            // Update the config\n            SetConfig(Config);\n        }\n\n        /// <summary>\n        /// SelectFarEndPreset method\n        /// </summary>\n        public void SelectFarEndPreset(int i)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Selecting Far End Preset: {0}\", i);\n        }\n\n        #endregion\n\n        /// <inheritdoc />\n        protected override void CustomSetConfig(DeviceConfig config)\n        {\n            PropertiesConfig.Presets = NearEndPresets;\n\n            Config.Properties = JToken.FromObject(PropertiesConfig);\n\n            ConfigWriter.UpdateDeviceConfig(config);\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a MockCodecInfo\n    /// </summary>\n    public class MockCodecInfo : VideoCodecInfo\n    {\n\n        /// <inheritdoc />\n        public override bool MultiSiteOptionIsEnabled\n        {\n            get { return true; }\n        }\n\n        /// <inheritdoc />\n        public override string E164Alias\n        {\n            get { return \"someE164alias\"; }\n        }\n\n        /// <inheritdoc />\n        public override string H323Id\n        {\n            get { return \"someH323Id\"; }\n        }\n\n        /// <inheritdoc />\n        public override string IpAddress\n        {\n            get { return \"xxx.xxx.xxx.xxx\"; }\n        }\n\n        /// <inheritdoc />\n        public override string SipPhoneNumber\n        {\n            get { return \"333-444-5555\"; }\n        }\n\n        /// <inheritdoc />\n        public override string SipUri\n        {\n            get { return \"mock@someurl.com\"; }\n        }\n\n        /// <inheritdoc />\n        public override bool AutoAnswerEnabled\n        {\n            get { return _AutoAnswerEnabled; }\n        }\n        bool _AutoAnswerEnabled;\n\n        /// <summary>\n        /// SetAutoAnswer method\n        /// </summary>\n        public void SetAutoAnswer(bool value)\n        {\n            _AutoAnswerEnabled = value;\n        }\n    }\n\n    /// <summary>\n    /// Represents a MockVCFactory\n    /// </summary>\n    public class MockVCFactory : EssentialsDeviceFactory<MockVC>\n    {\n        /// <inheritdoc />\n        public MockVCFactory()\n        {\n            TypeNames = new List<string>() { \"mockvc\" };\n        }\n\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            Debug.LogMessage(LogEventLevel.Debug, \"Factory Attempting to create new MockVC Device\");\n            return new VideoCodec.MockVC(dc);\n        }\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/MockVC/MockVCCamera.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Devices.Common.VideoCodec;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.Devices.Common.Cameras\n{\n    /// <summary>\n    /// Represents a MockVCCamera\n    /// </summary>\n    public class MockVCCamera : CameraBase, IHasCameraPtzControl, IHasCameraFocusControl, IBridgeAdvanced\n    {\n        /// <summary>\n        /// Gets the parent video codec\n        /// </summary>\n        protected VideoCodecBase ParentCodec { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the MockVCCamera class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        /// <param name=\"codec\">The parent video codec</param>\n        public MockVCCamera(string key, string name, VideoCodecBase codec)\n            : base(key, name)\n        {\n            Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom | eCameraCapabilities.Focus;\n\n            ParentCodec = codec;\n        }\n\n        #region IHasCameraPtzControl Members\n\n        /// <summary>\n        /// PositionHome method\n        /// </summary>\n        public void PositionHome()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Resetting to home position\");\n        }\n\n        #endregion\n\n        #region IHasCameraPanControl Members\n\n        /// <summary>\n        /// PanLeft method\n        /// </summary>\n        public void PanLeft()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Panning Left\");\n        }\n\n        /// <summary>\n        /// PanRight method\n        /// </summary>\n        public void PanRight()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Panning Right\");\n        }\n\n        /// <summary>\n        /// PanStop method\n        /// </summary>\n        public void PanStop()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Pan\");\n        }\n\n        #endregion\n\n        #region IHasCameraTiltControl Members\n\n        /// <summary>\n        /// TiltDown method\n        /// </summary>\n        public void TiltDown()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Tilting Down\");\n        }\n\n        /// <summary>\n        /// TiltUp method\n        /// </summary>\n        public void TiltUp()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Tilting Up\");\n        }\n\n        /// <summary>\n        /// TiltStop method\n        /// </summary>\n        public void TiltStop()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Tilt\");\n        }\n\n        #endregion\n\n        #region IHasCameraZoomControl Members\n\n        /// <summary>\n        /// ZoomIn method\n        /// </summary>\n        public void ZoomIn()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Zooming In\");\n        }\n\n        /// <summary>\n        /// ZoomOut method\n        /// </summary>\n        public void ZoomOut()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Zooming Out\");\n        }\n\n        /// <summary>\n        /// ZoomStop method\n        /// </summary>\n        public void ZoomStop()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Zoom\");\n        }\n\n        #endregion\n\n        #region IHasCameraFocusControl Members\n\n        /// <summary>\n        /// FocusNear method\n        /// </summary>\n        public void FocusNear()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Focusing Near\");\n        }\n\n        /// <summary>\n        /// FocusFar method\n        /// </summary>\n        public void FocusFar()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Focusing Far\");\n        }\n\n        /// <summary>\n        /// FocusStop method\n        /// </summary>\n        public void FocusStop()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Focus\");\n        }\n\n        /// <summary>\n        /// TriggerAutoFocus method\n        /// </summary>\n        public void TriggerAutoFocus()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"AutoFocus Triggered\");\n        }\n\n        #endregion\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            LinkCameraToApi(this, trilist, joinStart, joinMapKey, bridge);\n        }\n    }\n\n    /// <summary>\n    /// Represents a MockFarEndVCCamera\n    /// </summary>\n    public class MockFarEndVCCamera : CameraBase, IHasCameraPtzControl, IAmFarEndCamera, IBridgeAdvanced\n    {\n        /// <summary>\n        /// Gets the parent video codec\n        /// </summary>\n        protected VideoCodecBase ParentCodec { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the MockFarEndVCCamera class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"name\">The device name</param>\n        /// <param name=\"codec\">The parent video codec</param>\n        public MockFarEndVCCamera(string key, string name, VideoCodecBase codec)\n            : base(key, name)\n        {\n            Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom;\n\n            ParentCodec = codec;\n        }\n\n        #region IHasCameraPtzControl Members\n\n        /// <summary>\n        /// PositionHome method\n        /// </summary>\n        public void PositionHome()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Resetting to home position\");\n        }\n\n        #endregion\n\n        #region IHasCameraPanControl Members\n\n        /// <summary>\n        /// PanLeft method\n        /// </summary>\n        public void PanLeft()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Panning Left\");\n        }\n\n        /// <summary>\n        /// PanRight method\n        /// </summary>\n        public void PanRight()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Panning Right\");\n        }\n\n        /// <summary>\n        /// PanStop method\n        /// </summary>\n        public void PanStop()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Pan\");\n        }\n\n        #endregion\n\n        #region IHasCameraTiltControl Members\n\n        /// <summary>\n        /// TiltDown method\n        /// </summary>\n        public void TiltDown()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Tilting Down\");\n        }\n\n        /// <summary>\n        /// TiltUp method\n        /// </summary>\n        public void TiltUp()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Tilting Up\");\n        }\n\n        /// <summary>\n        /// TiltStop method\n        /// </summary>\n        public void TiltStop()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Tilt\");\n        }\n\n        #endregion\n\n        #region IHasCameraZoomControl Members\n\n        /// <summary>\n        /// ZoomIn method\n        /// </summary>\n        public void ZoomIn()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Zooming In\");\n        }\n\n        /// <summary>\n        /// ZoomOut method\n        /// </summary>\n        public void ZoomOut()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Zooming Out\");\n        }\n\n        /// <summary>\n        /// ZoomStop method\n        /// </summary>\n        public void ZoomStop()\n        {\n            Debug.LogMessage(LogEventLevel.Debug, this, \"Stopping Zoom\");\n        }\n\n        #endregion\n\n        /// <summary>\n        /// LinkToApi method\n        /// </summary>\n        public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)\n        {\n            LinkCameraToApi(this, trilist, joinStart, joinMapKey, bridge);\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/MockVC/MockVcPropertiesConfig.cs",
    "content": "﻿using System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Essentials.Devices.Common.Codec;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n    /// <summary>\n    /// Represents a MockVcPropertiesConfig\n    /// </summary>\n    public class MockVcPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Favorites\n        /// </summary>\n        [JsonProperty(\"favorites\")]\n        public List<CodecActiveCallItem> Favorites { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Presets\n        /// </summary>\n        [JsonProperty(\"presets\")]\n        public List<CodecRoomPreset> Presets { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"MockVcPropertiesConfig\"/> class.\n        /// </summary>\n        public MockVcPropertiesConfig()\n        {\n            Favorites = new List<CodecActiveCallItem>();\n            Presets = new List<CodecRoomPreset>();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing PepperDash.Core;\nusing PepperDash.Core.Intersystem;\nusing PepperDash.Core.Intersystem.Tokens;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.Bridges.JoinMaps;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.Devices;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Core.Routing;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing PepperDash.Essentials.Devices.Common.Codec;\nusing PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces;\nusing Serilog.Events;\nusing Feedback = PepperDash.Essentials.Core.Feedback;\n\nnamespace PepperDash.Essentials.Devices.Common.VideoCodec\n{\n\t/// <summary>\n\t/// Base class for video codec devices\n\t/// </summary>\n\tpublic abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs,\n\t\tIUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced, IHasStandbyMode\n\t{\n\t\tprivate const int XSigEncoding = 28591;\n\n\t\t/// <summary>\n\t\t/// Maximum number of participants\n\t\t/// </summary>\n\t\tprotected const int MaxParticipants = 50;\n\t\tprivate readonly byte[] _clearBytes = XSigHelpers.ClearOutputs();\n\n\t\t/// <summary>\n\t\t/// Time format specifier\n\t\t/// </summary>\n\t\tprotected string _timeFormatSpecifier;\n\n\t\t/// <summary>\n\t\t/// Date format specifier\n\t\t/// </summary>\n\t\tprotected string _dateFormatSpecifier;\n\n\t\t/// <summary>\n\t\t/// Initializes a new instance of the <see cref=\"VideoCodecBase\"/> class.\n\t\t/// </summary>\n\t\tprotected VideoCodecBase(DeviceConfig config)\n\t\t\t: base(config)\n\t\t{\n\n\t\t\tStandbyIsOnFeedback = new BoolFeedback(\"standbyIsOn\", StandbyIsOnFeedbackFunc);\n\t\t\tPrivacyModeIsOnFeedback = new BoolFeedback(\"privacyModeIsOn\", PrivacyModeIsOnFeedbackFunc);\n\t\t\tVolumeLevelFeedback = new IntFeedback(\"volumeLevel\", VolumeLevelFeedbackFunc);\n\t\t\tMuteFeedback = new BoolFeedback(\"mute\", MuteFeedbackFunc);\n\t\t\tSharingSourceFeedback = new StringFeedback(\"sharingSource\", SharingSourceFeedbackFunc);\n\t\t\tSharingContentIsOnFeedback = new BoolFeedback(\"sharingContentIsOn\", SharingContentIsOnFeedbackFunc);\n\n\t\t\t// TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set\n\t\t\tMeetingsToDisplayFeedback = new IntFeedback(\"meetingsToDisplay\", () => MeetingsToDisplay);\n\n\t\t\tInputPorts = new RoutingPortCollection<RoutingInputPort>();\n\t\t\tOutputPorts = new RoutingPortCollection<RoutingOutputPort>();\n\n\t\t\tActiveCalls = new List<CodecActiveCallItem>();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets the Communication\n\t\t/// </summary>\n\t\tpublic IBasicCommunication Communication { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the OsdSource\n\t\t/// </summary>\n\t\tpublic DummyRoutingInputsDevice OsdSource { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the StandbyIsOnFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback StandbyIsOnFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the PrivacyModeIsOnFeedbackFunc\n\t\t/// </summary>\n\t\tprotected abstract Func<bool> PrivacyModeIsOnFeedbackFunc { get; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the VolumeLevelFeedbackFunc\n\t\t/// </summary>\n\t\tprotected abstract Func<int> VolumeLevelFeedbackFunc { get; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the MuteFeedbackFunc\n\t\t/// </summary>\n\t\tprotected abstract Func<bool> MuteFeedbackFunc { get; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the StandbyIsOnFeedbackFunc\n\t\t/// </summary>\n\t\tprotected abstract Func<bool> StandbyIsOnFeedbackFunc { get; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the ActiveCalls\n\t\t/// </summary>\n\t\tpublic List<CodecActiveCallItem> ActiveCalls { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the ShowSelfViewByDefault\n\t\t/// </summary>\n\t\tpublic bool ShowSelfViewByDefault { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the SupportsCameraOff\n\t\t/// </summary>\n\t\tpublic bool SupportsCameraOff { get; protected set; }\n\t\t/// <summary>\n\t\t/// Gets or sets the SupportsCameraAutoMode\n\t\t/// </summary>\n\t\tpublic bool SupportsCameraAutoMode { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the IsReady\n\t\t/// </summary>\n\t\tpublic bool IsReady { get; protected set; }\n\n\t\t/// <inheritdoc />\n\t\tpublic virtual List<Feedback> Feedbacks\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn new List<Feedback>\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tPrivacyModeIsOnFeedback,\n\t\t\t\t\t\t\t\t\t\tSharingSourceFeedback\n\t\t\t\t\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// \tGets or sets the MeetingsToDisplay\n\t\t/// </summary>\n\t\tprotected abstract Func<string> SharingSourceFeedbackFunc { get; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the SharingContentIsOnFeedbackFunc\n\t\t/// </summary>\n\t\tprotected abstract Func<bool> SharingContentIsOnFeedbackFunc { get; }\n\n\t\t#region ICodecAudio Members\n\n\t\t/// <summary>\n\t\t/// Set Privacy Mode On\n\t\t/// </summary>\n\t\tpublic abstract void PrivacyModeOn();\n\n\t\t/// <summary>\n\t\t/// Set Privacy Mode Off\n\t\t/// </summary>\n\t\tpublic abstract void PrivacyModeOff();\n\n\t\t/// <summary>\n\t\t/// Toggles the Privacy Mode\n\t\t/// </summary>\n\t\tpublic abstract void PrivacyModeToggle();\n\n\t\t/// <summary>\n\t\t/// Gets or sets the PrivacyModeIsOnFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback PrivacyModeIsOnFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the MuteFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback MuteFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Sets the Mute state to Off\n\t\t/// </summary>\n\t\tpublic abstract void MuteOff();\n\n\t\t/// <summary>\n\t\t/// Sets the Mute state to On\n\t\t/// </summary>\n\t\tpublic abstract void MuteOn();\n\n\t\t/// <summary>\n\t\t/// Sets the Volume level\n\t\t/// </summary>\n\t\tpublic abstract void SetVolume(ushort level);\n\n\t\t/// <summary>\n\t\t/// Gets or sets the VolumeLevelFeedback\n\t\t/// </summary>\n\t\tpublic IntFeedback VolumeLevelFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Toggles the Mute state\n\t\t/// </summary>\n\t\tpublic abstract void MuteToggle();\n\n\t\t/// <summary>\n\t\t/// Decreases the Volume level\n\t\t/// </summary>\n\t\tpublic abstract void VolumeDown(bool pressRelease);\n\n\t\t/// <summary>\n\t\t/// Increases the Volume level\n\t\t/// </summary>\n\t\tpublic abstract void VolumeUp(bool pressRelease);\n\n\t\t#endregion\n\n\t\t#region IHasContentSharing Members\n\n\t\t/// <summary>\n\t\t/// Starts content sharing\n\t\t/// </summary>\n\t\tpublic abstract void StartSharing();\n\n\t\t/// <summary>\n\t\t/// Stops content sharing\n\t\t/// </summary>\n\t\tpublic abstract void StopSharing();\n\n\t\t/// <summary>\n\t\t/// Gets or sets the AutoShareContentWhileInCall\n\t\t/// </summary>\n\t\tpublic bool AutoShareContentWhileInCall { get; protected set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the SharingSourceFeedback\n\t\t/// </summary>\n\t\tpublic StringFeedback SharingSourceFeedback { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the SharingContentIsOnFeedback\n\t\t/// </summary>\n\t\tpublic BoolFeedback SharingContentIsOnFeedback { get; private set; }\n\n\t\t#endregion\n\n\t\t#region IHasDialer Members\n\n\t\t/// <summary>\n\t\t/// Fires when the status of any active, dialing, or incoming call changes or is new\n\t\t/// </summary>\n\t\tpublic event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;\n\n\t\t/// <summary>\n\t\t/// Returns true when any call is not in state Unknown, Disconnecting, Disconnected\n\t\t/// </summary>\n\t\tpublic bool IsInCall\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\tvar value = ActiveCalls != null && ActiveCalls.Any(c => c.IsActiveCall);\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Dials the specified number\n\t\t/// </summary>\n\t\tpublic abstract void Dial(string number);\n\n\t\t/// <summary>\n\t\t/// Ends the specified call\n\t\t/// </summary>\n\t\tpublic abstract void EndCall(CodecActiveCallItem call);\n\n\t\t/// <summary>\n\t\t/// Ends all active calls\n\t\t/// </summary>\n\t\tpublic abstract void EndAllCalls();\n\n\t\t/// <summary>\n\t\t/// Accepts the specified call\n\t\t/// </summary>\n\t\tpublic abstract void AcceptCall(CodecActiveCallItem call);\n\n\t\t/// <summary>\n\t\t/// Rejects the specified call\n\t\t/// </summary>\n\t\tpublic abstract void RejectCall(CodecActiveCallItem call);\n\n\t\t/// <summary>\n\t\t/// Sends DTMF tones\n\t\t/// </summary>\n\t\tpublic abstract void SendDtmf(string s);\n\n\t\t/// <summary>\n\t\t/// SendDtmf method\n\t\t/// </summary>\t\t\n\t\tpublic virtual void SendDtmf(string s, CodecActiveCallItem call) { }\n\n\t\t#endregion\n\n\t\t#region IRoutingInputsOutputs Members\n\n\t\t/// <summary>\n\t\t/// Gets or sets the InputPorts\n\t\t/// </summary>\n\t\tpublic RoutingPortCollection<RoutingInputPort> InputPorts { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets or sets the OutputPorts\n\t\t/// </summary>\n\t\tpublic RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }\n\n\t\t#endregion\n\n\t\t#region IUsageTracking Members\n\n\t\t/// <summary>\n\t\t/// Gets or sets the UsageTracker\n\t\t/// </summary>\n\t\tpublic UsageTracking UsageTracker { get; set; }\n\n\t\t#endregion\n\n\t\t#region iVideoCodecInfo Members\n\n\t\t/// <summary>\n\t\t/// Gets the CodecInfo\n\t\t/// </summary>\n\t\tpublic VideoCodecInfo CodecInfo { get; protected set; }\n\n\t\t#endregion\n\n\t\t/// <summary>\n\t\t/// Fired when the Codec is ready to be used\n\t\t/// </summary>\n\t\tpublic event EventHandler<EventArgs> IsReadyChange;\n\n\t\t/// <summary>\n\t\t/// Dials the specified meeting\n\t\t/// </summary>\n\t\t/// <param name=\"meeting\"></param>\n\t\tpublic abstract void Dial(Meeting meeting);\n\n\t\t/// <summary>\n\t\t/// Dial a contact\n\t\t/// </summary>\n\t\t/// <param name=\"contact\">The contact to dial</param>\n\t\tpublic virtual void Dial(IInvitableContact contact)\n\t\t{\n\t\t}\n\n\t\t/// <inheritdoc />\n\t\tpublic abstract void ExecuteSwitch(object selector);\n\n\t\t/// <summary>\n\t\t/// Helper method to fire CallStatusChange event with old and new status\n\t\t/// </summary>\n\t\tprotected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call)\n\t\t{\n\t\t\tcall.Status = newStatus;\n\n\t\t\tOnCallStatusChange(call);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets the previous and new status for the call\n\t\t/// </summary>\t\t\n\t\t/// <param name=\"item\"></param>\n\t\tprotected virtual void OnCallStatusChange(CodecActiveCallItem item)\n\t\t{\n\t\t\tCallStatusChange?.Invoke(this, new CodecCallStatusItemChangeEventArgs(item));\n\n\t\t\tPrivacyModeIsOnFeedback.FireUpdate();\n\n\t\t\tif (AutoShareContentWhileInCall)\n\t\t\t{\n\t\t\t\tStartSharing();\n\t\t\t}\n\n\t\t\tif (UsageTracker != null)\n\t\t\t{\n\t\t\t\tif (IsInCall && !UsageTracker.UsageTrackingStarted)\n\t\t\t\t{\n\t\t\t\t\tUsageTracker.StartDeviceUsage();\n\t\t\t\t}\n\t\t\t\telse if (UsageTracker.UsageTrackingStarted && !IsInCall)\n\t\t\t\t{\n\t\t\t\t\tUsageTracker.EndDeviceUsage();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets IsReady property and fires the event. Used for dependent classes to sync up their data.\n\t\t/// </summary>\n\t\tprotected void SetIsReady()\n\t\t{\n\t\t\tCrestronInvoke.BeginInvoke((o) =>\n\t\t\t\t{\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tIsReady = true;\n\t\t\t\t\t\tIsReadyChange?.Invoke(this, new EventArgs());\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e)\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Error in SetIsReady() : {0}\", e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\n\t\t// **** DEBUGGING THINGS ****\n\t\t/// <summary>\n\t\t/// ListCalls method\n\t\t/// </summary>\t\t\n\t\tpublic virtual void ListCalls()\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Active Calls:\");\n\n\t\t\tvar sb = new StringBuilder();\n\t\t\tforeach (var c in ActiveCalls)\n\t\t\t{\n\t\t\t\tsb.AppendFormat(\"id: {0} number: {1} -- name: {2} status: {3} onHold: {4}\\r\\n\", c.Id, c.Number, c.Name, c.Status, c.IsOnHold);\n\t\t\t}\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"\\n{0}\\n\", sb.ToString());\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Activate standby\n\t\t/// </summary>\n\t\tpublic abstract void StandbyActivate();\n\n\t\t/// <summary>\n\t\t/// Deactivate standby\n\t\t/// </summary>\n\t\tpublic abstract void StandbyDeactivate();\n\n\t\t#region Implementation of IBridgeAdvanced\n\n\t\t/// <inheritdoc />\n\t\tpublic abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);\n\n\t\t/// <summary>\n\t\t/// Use this method when using a plain VideoCodecControllerJoinMap\n\t\t/// </summary>\n\t\t/// <param name=\"codec\">codec to link</param>\n\t\t/// <param name=\"trilist\">trilist to link</param>\n\t\t/// <param name=\"joinStart\">join to start with</param>\n\t\t/// <param name=\"joinMapKey\">key for custom join maps</param>\n\t\t/// <param name=\"bridge\">bridge controller</param>\n\t\tprotected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, uint joinStart, string joinMapKey,\n\t\t\tEiscApiAdvanced bridge)\n\t\t{\n\t\t\tvar joinMap = new VideoCodecControllerJoinMap(joinStart);\n\n\t\t\tvar customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey);\n\n\t\t\tif (customJoins != null)\n\t\t\t{\n\t\t\t\tjoinMap.SetCustomJoinData(customJoins);\n\t\t\t}\n\n\t\t\tbridge?.AddJoinMap(Key, joinMap);\n\n\t\t\tLinkVideoCodecToApi(codec, trilist, joinMap);\n\n\t\t\ttrilist.OnlineStatusChange += (device, args) =>\n\t\t\t{\n\t\t\t\tif (!args.DeviceOnLine) return;\n\t\t\t};\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Use this method when you need to pass in a join map that extends VideoCodecControllerJoinMap\n\t\t/// </summary>\n\t\t/// <param name=\"codec\">codec to link</param>\n\t\t/// <param name=\"trilist\">trilist to link</param>\n\t\t/// <param name=\"joinMap\">join map to use</param>\n\t\tprotected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Linking to Trilist {0}\", trilist.ID.ToString(\"X\"));\n\n\t\t\tLinkVideoCodecDtmfToApi(trilist, joinMap);\n\n\t\t\tLinkVideoCodecCallControlsToApi(trilist, joinMap);\n\n\t\t\tLinkVideoCodecContentSharingToApi(trilist, joinMap);\n\n\t\t\tLinkVideoCodecPrivacyToApi(trilist, joinMap);\n\n\t\t\tLinkVideoCodecVolumeToApi(trilist, joinMap);\n\n\t\t\tLinkVideoCodecInfoToApi(trilist, joinMap);\n\n\t\t\t// Register for this event to link any functions that require the codec to be ready first\n\t\t\tcodec.IsReadyChange += (o, a) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tif (codec is IHasCodecCameras)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\tif (codec is ICommunicationMonitor)\n\t\t\t{\n\t\t\t\tLinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap);\n\t\t\t}\n\n\n\t\t\tif (codec is IHasCodecSelfView)\n\t\t\t{\n\t\t\t\tLinkVideoCodecSelfviewToApi(codec as IHasCodecSelfView, trilist, joinMap);\n\t\t\t}\n\n\t\t\tif (codec is IHasCameraAutoMode)\n\t\t\t{\n\t\t\t\ttrilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode);\n\t\t\t\tLinkVideoCodecCameraModeToApi(codec as IHasCameraAutoMode, trilist, joinMap);\n\t\t\t}\n\n\t\t\tif (codec is IHasCameraOff)\n\t\t\t{\n\t\t\t\ttrilist.SetBool(joinMap.CameraSupportsOffMode.JoinNumber, SupportsCameraOff);\n\t\t\t\tLinkVideoCodecCameraOffToApi(codec as IHasCameraOff, trilist, joinMap);\n\t\t\t}\n\n\t\t\tif (codec is IHasCodecLayouts)\n\t\t\t{\n\t\t\t\tLinkVideoCodecCameraLayoutsToApi(codec as IHasCodecLayouts, trilist, joinMap);\n\t\t\t}\n\n\n\t\t\tif (codec is IHasSelfviewPosition)\n\t\t\t{\n\t\t\t\tLinkVideoCodecSelfviewPositionToApi(codec as IHasSelfviewPosition, trilist, joinMap);\n\t\t\t}\n\n\t\t\tif (codec is IHasDirectory)\n\t\t\t{\n\t\t\t\tLinkVideoCodecDirectoryToApi(codec as IHasDirectory, trilist, joinMap);\n\t\t\t}\n\n\t\t\tif (codec is IHasScheduleAwareness)\n\t\t\t{\n\t\t\t\tLinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap);\n\t\t\t}\n\n\t\t\tif (codec is IHasParticipants)\n\t\t\t{\n\t\t\t\tLinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap);\n\t\t\t}\n\n\t\t\tif (codec is IHasFarEndContentStatus)\n\t\t\t{\n\t\t\t\t(codec as IHasFarEndContentStatus).ReceivingContent.LinkInputSig(trilist.BooleanInput[joinMap.RecievingContent.JoinNumber]);\n\t\t\t}\n\n\t\t\tif (codec is IHasPhoneDialing)\n\t\t\t{\n\t\t\t\tLinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap);\n\t\t\t}\n\n\t\t\tif (codec is IHasCallHistory)\n\t\t\t{\n\t\t\t\tLinkVideoCodecCallHistoryToApi(codec as IHasCallHistory, trilist, joinMap);\n\t\t\t}\n\n\t\t\ttrilist.OnlineStatusChange += (device, args) =>\n\t\t\t{\n\t\t\t\tif (!args.DeviceOnLine) return;\n\n\t\t\t\tif (codec is IHasDirectory)\n\t\t\t\t{\n\t\t\t\t\t(codec as IHasDirectory).SetCurrentDirectoryToRoot();\n\t\t\t\t}\n\n\t\t\t\tif (codec is IHasScheduleAwareness)\n\t\t\t\t{\n\t\t\t\t\t(codec as IHasScheduleAwareness).GetSchedule();\n\t\t\t\t}\n\n\t\t\t\tif (codec is IHasParticipants)\n\t\t\t\t{\n\t\t\t\t\tUpdateParticipantsXSig((codec as IHasParticipants).Participants.CurrentParticipants);\n\t\t\t\t}\n\n\t\t\t\tif (codec is IHasCameraAutoMode)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode);\n\n\t\t\t\t\t(codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate();\n\t\t\t\t}\n\n\t\t\t\tif (codec is IHasCodecSelfView)\n\t\t\t\t{\n\t\t\t\t\t(codec as IHasCodecSelfView).SelfviewIsOnFeedback.FireUpdate();\n\t\t\t\t}\n\n\t\t\t\tif (codec is IHasCameraAutoMode)\n\t\t\t\t{\n\t\t\t\t\t(codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate();\n\t\t\t\t}\n\n\t\t\t\tif (codec is IHasCameraOff)\n\t\t\t\t{\n\t\t\t\t\t(codec as IHasCameraOff).CameraIsOffFeedback.FireUpdate();\n\t\t\t\t}\n\n\t\t\t\tif (codec is IHasPhoneDialing)\n\t\t\t\t{\n\t\t\t\t\t(codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate();\n\t\t\t\t}\n\n\t\t\t\tif (codec is IHasCallHistory)\n\t\t\t\t{\n\t\t\t\t\tUpdateCallHistory((codec as IHasCallHistory), trilist, joinMap);\n\t\t\t\t}\n\n\t\t\t\tSharingContentIsOnFeedback.FireUpdate();\n\t\t\t};\n\t\t}\n\n\t\tprivate void LinkVideoCodecInfoToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled);\n\t\t\ttrilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled);\n\t\t\ttrilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress);\n\t\t\ttrilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber);\n\t\t\ttrilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias);\n\t\t\ttrilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id);\n\t\t\ttrilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri);\n\n\t\t\ttrilist.OnlineStatusChange += (o, a) =>\n\t\t\t{\n\t\t\t\tif (a.DeviceOnLine)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled);\n\t\t\t\t\ttrilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled);\n\t\t\t\t\ttrilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress);\n\t\t\t\t\ttrilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber);\n\t\t\t\t\ttrilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias);\n\t\t\t\t\ttrilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id);\n\t\t\t\t\ttrilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tprivate void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tcodec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]);\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.DialPhone.JoinNumber,\n\t\t\t\t() => codec.DialPhoneCall(trilist.StringOutput[joinMap.PhoneDialString.JoinNumber].StringValue));\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.HangUpPhone.JoinNumber, codec.EndPhoneCall);\n\t\t}\n\n\t\tprivate void LinkVideoCodecSelfviewPositionToApi(IHasSelfviewPosition codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetSigFalseAction(joinMap.SelfviewPosition.JoinNumber, codec.SelfviewPipPositionToggle);\n\n\t\t\tcodec.SelfviewPipPositionFeedback.LinkInputSig(trilist.StringInput[joinMap.SelfviewPositionFb.JoinNumber]);\n\t\t}\n\n\t\tprivate void LinkVideoCodecCameraOffToApi(IHasCameraOff codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetSigFalseAction(joinMap.CameraModeOff.JoinNumber, codec.CameraOff);\n\n\t\t\tcodec.CameraIsOffFeedback.OutputChange += (o, a) =>\n\t\t\t{\n\t\t\t\tif (a.BoolValue)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, true);\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, false);\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, false);\n\n\n\t\t\t\tif (!(codec is IHasCameraAutoMode autoCodec)) return;\n\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoCodec.CameraAutoModeIsOnFeedback.BoolValue);\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoCodec.CameraAutoModeIsOnFeedback.BoolValue);\n\t\t\t};\n\n\t\t\tif (codec.CameraIsOffFeedback.BoolValue)\n\t\t\t{\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, true);\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, false);\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, false);\n\n\n\t\t\tif (!(codec is IHasCameraAutoMode autoModeCodec)) return;\n\n\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue);\n\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue);\n\t\t}\n\n\t\tprivate void LinkVideoCodecVolumeToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tMuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]);\n\t\t\tMuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]);\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.VolumeMuteOn.JoinNumber, MuteOn);\n\t\t\ttrilist.SetSigFalseAction(joinMap.VolumeMuteOff.JoinNumber, MuteOff);\n\t\t\ttrilist.SetSigFalseAction(joinMap.VolumeMuteToggle.JoinNumber, MuteToggle);\n\n\t\t\tVolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]);\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, VolumeUp);\n\t\t\ttrilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, VolumeDown);\n\n\t\t\ttrilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, SetVolume);\n\n\t\t}\n\n\t\tprivate void LinkVideoCodecPrivacyToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tPrivacyModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]);\n\t\t\tPrivacyModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]);\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.MicMuteOn.JoinNumber, PrivacyModeOn);\n\t\t\ttrilist.SetSigFalseAction(joinMap.MicMuteOff.JoinNumber, PrivacyModeOff);\n\t\t\ttrilist.SetSigFalseAction(joinMap.MicMuteToggle.JoinNumber, PrivacyModeToggle);\n\t\t}\n\n\t\tprivate void LinkVideoCodecCommMonitorToApi(ICommunicationMonitor codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tcodec.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]);\n\t\t}\n\n\t\tprivate void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\t// make sure to update the values when the EISC comes online\n\t\t\ttrilist.OnlineStatusChange += (sender, args) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tif (sender.IsOnline)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tUpdateParticipantsXSig(codec, trilist, joinMap);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t// set actions and update the values when the list changes\n\t\t\tcodec.Participants.ParticipantsListHasChanged += (sender, args) =>\n\t\t\t{\n\t\t\t\tSetParticipantActions(trilist, joinMap, codec.Participants.CurrentParticipants);\n\n\t\t\t\tUpdateParticipantsXSig(codec, trilist, joinMap);\n\t\t\t};\n\n\t\t\ttrilist.OnlineStatusChange += (device, args) =>\n\t\t\t{\n\t\t\t\tif (!args.DeviceOnLine) return;\n\n\t\t\t\t// TODO [ ] Issue #868\n\t\t\t\ttrilist.SetString(joinMap.CurrentParticipants.JoinNumber, \"\\xFC\");\n\t\t\t\tUpdateParticipantsXSig(codec, trilist, joinMap);\n\t\t\t};\n\t\t}\n\n\t\tprivate void UpdateParticipantsXSig(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tstring participantsXSig;\n\n\t\t\tif (codec.Participants.CurrentParticipants.Count == 0)\n\t\t\t{\n\t\t\t\tparticipantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length);\n\t\t\t\ttrilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig);\n\t\t\t\ttrilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tparticipantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants);\n\n\t\t\ttrilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig);\n\n\t\t\ttrilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets the actions for each participant in the list\n\t\t/// </summary>\n\t\tprivate void SetParticipantActions(BasicTriList trilist, VideoCodecControllerJoinMap joinMap, List<Participant> currentParticipants)\n\t\t{\n\t\t\tuint index = 0; // track the index of the participant in the \n\n\t\t\tforeach (var participant in currentParticipants)\n\t\t\t{\n\t\t\t\tvar p = participant;\n\t\t\t\tif (index > MaxParticipants) break;\n\n\t\t\t\tif (this is IHasParticipantAudioMute audioMuteCodec)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetSigFalseAction(joinMap.ParticipantAudioMuteToggleStart.JoinNumber + index,\n\t\t\t\t\t\t\t() => audioMuteCodec.ToggleAudioForParticipant(p.UserId));\n\n\t\t\t\t\ttrilist.SetSigFalseAction(joinMap.ParticipantVideoMuteToggleStart.JoinNumber + index,\n\t\t\t\t\t\t\t() => audioMuteCodec.ToggleVideoForParticipant(p.UserId));\n\t\t\t\t}\n\n\t\t\t\tif (this is IHasParticipantPinUnpin pinCodec)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetSigFalseAction(joinMap.ParticipantPinToggleStart.JoinNumber + index,\n\t\t\t\t\t\t\t() => pinCodec.ToggleParticipantPinState(p.UserId, pinCodec.ScreenIndexToPinUserTo));\n\t\t\t\t}\n\n\t\t\t\tindex++;\n\t\t\t}\n\n\t\t\t// Clear out any previously set actions\n\t\t\twhile (index < MaxParticipants)\n\t\t\t{\n\t\t\t\ttrilist.ClearBoolSigAction(joinMap.ParticipantAudioMuteToggleStart.JoinNumber + index);\n\t\t\t\ttrilist.ClearBoolSigAction(joinMap.ParticipantVideoMuteToggleStart.JoinNumber + index);\n\t\t\t\ttrilist.ClearBoolSigAction(joinMap.ParticipantPinToggleStart.JoinNumber + index);\n\n\t\t\t\tindex++;\n\t\t\t}\n\t\t}\n\n\t\tprivate string UpdateParticipantsXSig(List<Participant> currentParticipants)\n\t\t{\n\t\t\tconst int maxParticipants = MaxParticipants;\n\t\t\tconst int maxDigitals = 7;\n\t\t\tconst int maxStrings = 1;\n\t\t\tconst int maxAnalogs = 1;\n\t\t\tconst int offset = maxDigitals + maxStrings + maxAnalogs; // 9\n\t\t\tvar digitalIndex = (maxStrings + maxAnalogs) * maxParticipants; // 100\n\t\t\tvar stringIndex = 0;\n\t\t\tvar analogIndex = stringIndex + maxParticipants;\n\t\t\tvar meetingIndex = 0;\n\n\t\t\tvar tokenArray = new XSigToken[maxParticipants * offset];\n\n\t\t\tforeach (var participant in currentParticipants)\n\t\t\t{\n\t\t\t\tif (meetingIndex >= maxParticipants * offset) break;\n\n\t\t\t\t//                Debug.LogMessage(LogEventLevel.Verbose, this,\n\t\t\t\t//@\"Updating Participant on xsig:\n\t\t\t\t//Name: {0} (s{9})\n\t\t\t\t//AudioMute: {1} (d{10})\n\t\t\t\t//VideoMute: {2} (d{11})\n\t\t\t\t//CanMuteVideo: {3} (d{12})\n\t\t\t\t//CanUMuteVideo: {4} (d{13})\n\t\t\t\t//IsHost: {5} (d{14})\n\t\t\t\t//HandIsRaised: {6} (d{15})\n\t\t\t\t//IsPinned: {7} (d{16})\n\t\t\t\t//ScreenIndexIsPinnedTo: {8} (a{17})\n\t\t\t\t//\",\n\t\t\t\t// participant.Name,\n\t\t\t\t// participant.AudioMuteFb,\n\t\t\t\t// participant.VideoMuteFb,\n\t\t\t\t// participant.CanMuteVideo,\n\t\t\t\t// participant.CanUnmuteVideo,\n\t\t\t\t// participant.IsHost,\n\t\t\t\t// participant.HandIsRaisedFb,\n\t\t\t\t// participant.IsPinnedFb,\n\t\t\t\t// participant.ScreenIndexIsPinnedToFb,\n\t\t\t\t// stringIndex + 1,\n\t\t\t\t// digitalIndex + 1,\n\t\t\t\t// digitalIndex + 2,\n\t\t\t\t// digitalIndex + 3,\n\t\t\t\t// digitalIndex + 4,\n\t\t\t\t// digitalIndex + 5,\n\t\t\t\t// digitalIndex + 6,\n\t\t\t\t// digitalIndex + 7,\n\t\t\t\t// analogIndex + 1\n\t\t\t\t// );\n\n\n\t\t\t\t//digitals\n\t\t\t\ttokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb);\n\t\t\t\ttokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb);\n\t\t\t\ttokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo);\n\t\t\t\ttokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo);\n\t\t\t\ttokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost);\n\t\t\t\ttokenArray[digitalIndex + 5] = new XSigDigitalToken(digitalIndex + 6, participant.HandIsRaisedFb);\n\t\t\t\ttokenArray[digitalIndex + 6] = new XSigDigitalToken(digitalIndex + 7, participant.IsPinnedFb);\n\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Index: {0} byte value: {1}\", digitalIndex + 7, ComTextHelper.GetEscapedText(tokenArray[digitalIndex + 6].GetBytes()));\n\n\t\t\t\t//serials\n\t\t\t\ttokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name);\n\n\t\t\t\t//analogs\n\t\t\t\ttokenArray[analogIndex] = new XSigAnalogToken(analogIndex + 1, (ushort)participant.ScreenIndexIsPinnedToFb);\n\n\t\t\t\tdigitalIndex += maxDigitals;\n\t\t\t\tmeetingIndex += offset;\n\t\t\t\tstringIndex += maxStrings;\n\t\t\t\tanalogIndex += maxAnalogs;\n\t\t\t}\n\n\t\t\twhile (meetingIndex < maxParticipants * offset)\n\t\t\t{\n\t\t\t\t//digitals\n\t\t\t\ttokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false);\n\t\t\t\ttokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false);\n\t\t\t\ttokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, false);\n\t\t\t\ttokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, false);\n\t\t\t\ttokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, false);\n\t\t\t\ttokenArray[digitalIndex + 5] = new XSigDigitalToken(digitalIndex + 6, false);\n\t\t\t\ttokenArray[digitalIndex + 6] = new XSigDigitalToken(digitalIndex + 7, false);\n\n\t\t\t\t//serials\n\t\t\t\ttokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty);\n\n\t\t\t\t//analogs\n\t\t\t\ttokenArray[analogIndex] = new XSigAnalogToken(analogIndex + 1, 0);\n\n\t\t\t\tdigitalIndex += maxDigitals;\n\t\t\t\tmeetingIndex += offset;\n\t\t\t\tstringIndex += maxStrings;\n\t\t\t\tanalogIndex += maxAnalogs;\n\t\t\t}\n\n\t\t\tvar returnString = GetXSigString(tokenArray);\n\n\t\t\t//Debug.LogMessage(LogEventLevel.Verbose, this, \"{0}\", ComTextHelper.GetEscapedText(Encoding.GetEncoding(28591).GetBytes(returnString)));\n\n\n\t\t\treturn returnString;\n\t\t}\n\n\t\tprivate void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tSharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]);\n\t\t\tSharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]);\n\n\t\t\tSharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]);\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing);\n\t\t\ttrilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing);\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, b => AutoShareContentWhileInCall = b);\n\t\t}\n\n\t\tprivate List<Meeting> _currentMeetings = new List<Meeting>();\n\n\t\tprivate void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule);\n\n\t\t\ttrilist.SetUShortSigAction(joinMap.MinutesBeforeMeetingStart.JoinNumber, (i) =>\n\t\t\t{\n\t\t\t\tcodec.CodecSchedule.MeetingWarningMinutes = i;\n\t\t\t});\n\n\n\t\t\tfor (uint i = 0; i < joinMap.DialMeetingStart.JoinSpan; i++)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Setting action to Dial Meeting {0} to digital join {1}\", i + 1, joinMap.DialMeetingStart.JoinNumber + i);\n\t\t\t\tvar joinNumber = joinMap.DialMeetingStart.JoinNumber + i;\n\t\t\t\tvar mtg = i + 1;\n\t\t\t\tvar index = (int)i;\n\n\t\t\t\ttrilist.SetSigFalseAction(joinNumber, () =>\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}\",\n\t\t\t\t\t\t\t\t\t\t\tmtg, joinMap.DialMeetingStart.JoinNumber + i, index, _currentMeetings[index].Id, _currentMeetings[index].Title);\n\t\t\t\t\tif (_currentMeetings[index] != null)\n\t\t\t\t\t\tDial(_currentMeetings[index]);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcodec.CodecSchedule.MeetingsListHasChanged += (sender, args) => UpdateMeetingsList(codec, trilist, joinMap);\n\t\t\tcodec.CodecSchedule.MeetingEventChange += (sender, args) =>\n\t\t\t\t{\n\t\t\t\t\tif (args.ChangeType == eMeetingEventChangeType.MeetingStartWarning)\n\t\t\t\t\t{\n\t\t\t\t\t\tUpdateMeetingsList(codec, trilist, joinMap);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\ttrilist.SetUShortSigAction(joinMap.MeetingsToDisplay.JoinNumber, m => MeetingsToDisplay = m);\n\t\t\tMeetingsToDisplayFeedback.LinkInputSig(trilist.UShortInput[joinMap.MeetingsToDisplay.JoinNumber]);\n\n\t\t\ttrilist.OnlineStatusChange += (device, args) =>\n\t\t\t{\n\t\t\t\tif (!args.DeviceOnLine) return;\n\n\t\t\t\t// TODO [ ] Issue #868\n\t\t\t\ttrilist.SetString(joinMap.Schedule.JoinNumber, \"\\xFC\");\n\t\t\t\tUpdateMeetingsList(codec, trilist, joinMap);\n\t\t\t\t// TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set\n\t\t\t\tMeetingsToDisplayFeedback.LinkInputSig(trilist.UShortInput[joinMap.MeetingsToDisplay.JoinNumber]);\n\t\t\t};\n\t\t}\n\n\t\tprivate void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tvar currentTime = DateTime.Now;\n\n\t\t\t_currentMeetings = codec.CodecSchedule.Meetings.Where(m => m.StartTime >= currentTime || m.EndTime >= currentTime).ToList();\n\n\t\t\tif (_currentMeetings.Count == 0)\n\t\t\t{\n\t\t\t\tvar emptyXSigByteArray = XSigHelpers.ClearOutputs();\n\t\t\t\tvar emptyXSigString = Encoding.GetEncoding(XSigEncoding)\n\t\t\t\t\t\t.GetString(emptyXSigByteArray, 0, emptyXSigByteArray.Length);\n\n\t\t\t\ttrilist.SetString(joinMap.Schedule.JoinNumber, emptyXSigString);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar meetingsData = UpdateMeetingsListXSig(_currentMeetings);\n\t\t\ttrilist.SetString(joinMap.Schedule.JoinNumber, meetingsData);\n\t\t\ttrilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)_currentMeetings.Count);\n\n\t\t\ttrilist.OnlineStatusChange += (device, args) =>\n\t\t\t{\n\t\t\t\tif (!args.DeviceOnLine) return;\n\n\t\t\t\t// TODO [ ] Issue #868\n\t\t\t\ttrilist.SetString(joinMap.Schedule.JoinNumber, \"\\xFC\");\n\t\t\t\tUpdateMeetingsListXSig(_currentMeetings);\n\t\t\t};\n\t\t}\n\n\n\n\t\tprivate int _meetingsToDisplay = 3;\n\n\t\t/// <summary>\n\t\t/// Gets or sets the number of meetings to display on the XSIG\n\t\t/// </summary>\n\t\tprotected int MeetingsToDisplay\n\t\t{\n\t\t\tget { return _meetingsToDisplay; }\n\t\t\tset\n\t\t\t{\n\t\t\t\t_meetingsToDisplay = (ushort)(value == 0 ? 3 : value);\n\t\t\t\tMeetingsToDisplayFeedback.FireUpdate();\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets the MeetingsToDisplayFeedback\n\t\t/// </summary>\n\t\tpublic IntFeedback MeetingsToDisplayFeedback { get; set; }\n\n\t\tprivate string UpdateMeetingsListXSig(List<Meeting> meetings)\n\t\t{\n\t\t\t// TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set\n\t\t\t//const int _meetingsToDisplay = 3;            \n\t\t\tconst int maxDigitals = 2;\n\t\t\tconst int maxStrings = 7;\n\t\t\tconst int offset = maxDigitals + maxStrings;\n\t\t\tvar digitalIndex = maxStrings * _meetingsToDisplay; //15\n\t\t\tvar stringIndex = 0;\n\t\t\tvar meetingIndex = 0;\n\n\t\t\tvar tokenArray = new XSigToken[_meetingsToDisplay * offset];\n\t\t\t/* \n\t\t\t * Digitals\n\t\t\t * IsJoinable - 1\n\t\t\t * IsDialable - 2\n\t\t\t * \n\t\t\t * Serials\n\t\t\t * Organizer - 1\n\t\t\t * Title - 2\n\t\t\t * Start Date - 3\n\t\t\t * Start Time - 4\n\t\t\t * End Date - 5\n\t\t\t * End Time - 6\n\t\t\t * Id - 7\n\t\t\t*/\n\n\n\t\t\tforeach (var meeting in meetings)\n\t\t\t{\n\t\t\t\tvar currentTime = DateTime.Now;\n\n\t\t\t\tif (meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue;\n\n\t\t\t\tif (meetingIndex >= _meetingsToDisplay * offset)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Max Meetings reached\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t//digitals\n\t\t\t\ttokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable);\n\t\t\t\ttokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Dialable);\n\n\t\t\t\t//serials\n\t\t\t\ttokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer);\n\t\t\t\ttokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, meeting.Title);\n\t\t\t\ttokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToString(_dateFormatSpecifier.NullIfEmpty() ?? \"d\", Global.Culture));\n\t\t\t\ttokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToString(_timeFormatSpecifier.NullIfEmpty() ?? \"t\", Global.Culture));\n\t\t\t\ttokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToString(_dateFormatSpecifier.NullIfEmpty() ?? \"d\", Global.Culture));\n\t\t\t\ttokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToString(_timeFormatSpecifier.NullIfEmpty() ?? \"t\", Global.Culture));\n\t\t\t\ttokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id);\n\n\t\t\t\tdigitalIndex += maxDigitals;\n\t\t\t\tmeetingIndex += offset;\n\t\t\t\tstringIndex += maxStrings;\n\t\t\t}\n\n\t\t\twhile (meetingIndex < _meetingsToDisplay * offset)\n\t\t\t{\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}\",\n\t\t\t\t\tmeetingIndex, _meetingsToDisplay * offset);\n\n\t\t\t\t//digitals\n\t\t\t\ttokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false);\n\t\t\t\ttokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false);\n\n\t\t\t\t//serials\n\t\t\t\ttokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, String.Empty);\n\n\t\t\t\tdigitalIndex += maxDigitals;\n\t\t\t\tmeetingIndex += offset;\n\t\t\t\tstringIndex += maxStrings;\n\t\t\t}\n\n\t\t\treturn GetXSigString(tokenArray);\n\t\t}\n\n\n\t\tprivate void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tcodec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig(\n\t\t\t\ttrilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]);\n\n\t\t\ttrilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory);\n\n\t\t\ttrilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i, trilist, joinMap));\n\n\t\t\t//Special Change for protected directory clear\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.DirectoryClearSelected.JoinNumber, (b) => SelectDirectoryEntry(codec, 0, trilist, joinMap));\n\n\t\t\t// Report feedback for number of contact methods for selected contact\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot);\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents);\n\n\t\t\tif (codec.DirectoryRoot != null)\n\t\t\t{\n\t\t\t\tvar contactsCount = codec.DirectoryRoot.CurrentDirectoryResults.Where(c => c.ParentFolderId.Equals(\"root\")).ToList().Count;\n\t\t\t\ttrilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)contactsCount);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \">>> contactsCount: {0}\", contactsCount);\n\n\t\t\t\tvar clearBytes = XSigHelpers.ClearOutputs();\n\n\t\t\t\ttrilist.SetString(joinMap.DirectoryEntries.JoinNumber,\n\t\t\t\t\t\tEncoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length));\n\t\t\t\tvar directoryXSig = UpdateDirectoryXSig(codec.DirectoryRoot,\n\t\t\t\t\t\tcodec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false);\n\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Directory XSig Length: {0}\", directoryXSig.Length);\n\n\t\t\t\ttrilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig);\n\t\t\t}\n\n\t\t\tcodec.DirectoryResultReturned += (sender, args) =>\n\t\t\t{\n\t\t\t\tvar isRoot = codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false;\n\t\t\t\tvar argsCount = isRoot\n\t\t\t\t\t? args.Directory.CurrentDirectoryResults.Where(a => a.ParentFolderId.Equals(\"root\")).ToList().Count\n\t\t\t\t\t: args.Directory.CurrentDirectoryResults.Count;\n\n\t\t\t\ttrilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)argsCount);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \">>> argsCount: {0}\", argsCount);\n\n\t\t\t\tvar clearBytes = XSigHelpers.ClearOutputs();\n\n\t\t\t\ttrilist.SetString(joinMap.DirectoryEntries.JoinNumber,\n\t\t\t\t\tEncoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length));\n\t\t\t\tvar directoryXSig = UpdateDirectoryXSig(args.Directory,\n\t\t\t\t\t\tcodec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Directory XSig Length: {0}\", directoryXSig.Length);\n\n\t\t\t\ttrilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig);\n\t\t\t};\n\n\t\t\ttrilist.OnlineStatusChange += (device, args) =>\n\t\t\t{\n\t\t\t\tif (!args.DeviceOnLine) return;\n\n\t\t\t\tvar clearBytes = XSigHelpers.ClearOutputs();\n\t\t\t\ttrilist.SetString(joinMap.DirectoryEntries.JoinNumber,\n\t\t\t\t\t\t\t\t\tEncoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length));\n\t\t\t\tvar directoryXSig = UpdateDirectoryXSig(codec.DirectoryRoot, codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false);\n\t\t\t\ttrilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig);\n\t\t\t};\n\t\t}\n\n\t\tprivate void SelectDirectoryEntry(IHasDirectory codec, ushort i, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tif (i > codec.CurrentDirectoryResult.CurrentDirectoryResults.Count) return;\n\t\t\t_selectedDirectoryItem = i == 0 ? null : codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1];\n\t\t\ttrilist.SetUshort(joinMap.DirectorySelectRowFeedback.JoinNumber, i);\n\n\t\t\tif (_selectedDirectoryItem == null) trilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, false);\n\n\n\t\t\tif (_selectedDirectoryItem is DirectoryFolder)\n\t\t\t{\n\t\t\t\tcodec.GetDirectoryFolderContents(_selectedDirectoryItem.FolderId);\n\t\t\t\ttrilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0);\n\t\t\t\ttrilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, _selectedDirectoryItem.Name);\n\t\t\t\ttrilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty);\n\t\t\t\ttrilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber);\n\t\t\t\ttrilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber);\n\t\t\t\ttrilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber);\n\t\t\t\ttrilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// not a folder.  Clear this value\n\t\t\ttrilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, string.Empty);\n\n\t\t\tvar selectedContact = _selectedDirectoryItem as DirectoryContact;\n\n\t\t\tif (selectedContact != null && selectedContact.ContactMethods.Count >= 1)\n\t\t\t{\n\t\t\t\ttrilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, true);\n\t\t\t}\n\n\t\t\ttrilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber,\n\t\t\t\t\tselectedContact != null ? selectedContact.Name : string.Empty);\n\n\t\t\t// Allow auto dial of selected line.  Always dials first contact method\n\t\t\tif (!trilist.GetBool(joinMap.DirectoryDisableAutoDialSelectedLine.JoinNumber))\n\t\t\t{\n\t\t\t\tif (_selectedDirectoryItem is IInvitableContact invitableEntry)\n\t\t\t\t{\n\t\t\t\t\tDial(invitableEntry);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\n\t\t\t\ttrilist.SetString(joinMap.DirectoryEntrySelectedNumber.JoinNumber,\n\t\t\t\t\t\tselectedContact != null ? selectedContact.ContactMethods[0].Number : string.Empty);\n\n\t\t\t\tif (!(_selectedDirectoryItem is DirectoryContact entryToDial)) return;\n\n\t\t\t\tDial(entryToDial.ContactMethods[0].Number);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// If auto dial is disabled...\n\n\t\t\t\tif (!(_selectedDirectoryItem is DirectoryContact entryToDial))\n\t\t\t\t{\n\t\t\t\t\t// Clear out values and actions from last selected item\n\t\t\t\t\ttrilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0);\n\t\t\t\t\ttrilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty);\n\t\t\t\t\ttrilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber);\n\t\t\t\t\ttrilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber);\n\t\t\t\t\ttrilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\ttrilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, (ushort)entryToDial.ContactMethods.Count);\n\n\t\t\t\t// Update the action to dial the selected contact method\n\t\t\t\ttrilist.SetUShortSigAction(joinMap.SelectContactMethod.JoinNumber, (u) =>\n\t\t\t\t{\n\t\t\t\t\tif (u < 1 || u > entryToDial.ContactMethods.Count) return;\n\n\t\t\t\t\ttrilist.SetSigFalseAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber, () => Dial(entryToDial.ContactMethods[u - 1].Number));\n\t\t\t\t});\n\n\t\t\t\t// Sets DirectoryDialSelectedLine join action to dial first contact method\n\t\t\t\ttrilist.SetSigFalseAction(joinMap.DirectoryDialSelectedLine.JoinNumber, () => Dial(entryToDial.ContactMethods[0].Number));\n\n\t\t\t\tvar clearBytes = XSigHelpers.ClearOutputs();\n\n\t\t\t\ttrilist.SetString(joinMap.ContactMethods.JoinNumber,\n\t\t\t\t\t\tEncoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length));\n\t\t\t\tvar contactMethodsXSig = UpdateContactMethodsXSig(entryToDial);\n\n\t\t\t\ttrilist.SetString(joinMap.ContactMethods.JoinNumber, contactMethodsXSig);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Generates the XSig data representing the available contact methods for the selected DirectoryContact\n\t\t/// </summary>\n\t\t/// <param name=\"contact\"></param>\n\t\t/// <returns></returns>\n\t\tprivate string UpdateContactMethodsXSig(DirectoryContact contact)\n\t\t{\n\t\t\tconst int maxMethods = 10;\n\t\t\tconst int maxStrings = 3;\n\t\t\tconst int offset = maxStrings;\n\t\t\tvar stringIndex = 0;\n\t\t\tvar arrayIndex = 0;\n\t\t\t// Create a new token array and set the size to the number of methods times the total number of signals\n\t\t\tvar tokenArray = new XSigToken[maxMethods * offset];\n\n\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Creating XSIG token array with size {0}\", maxMethods * offset);\n\n\t\t\t// TODO: Add code to generate XSig data\n\t\t\tforeach (var method in contact.ContactMethods)\n\t\t\t{\n\t\t\t\tif (arrayIndex >= maxMethods * offset)\n\t\t\t\t\tbreak;\n\n\t\t\t\t//serials\n\t\t\t\ttokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, method.Number);\n\t\t\t\ttokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, method.ContactMethodId.ToString());\n\t\t\t\ttokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, method.Device.ToString());\n\n\t\t\t\tarrayIndex += offset;\n\t\t\t\tstringIndex += maxStrings;\n\t\t\t}\n\n\t\t\twhile (arrayIndex < maxMethods)\n\t\t\t{\n\t\t\t\ttokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty);\n\t\t\t\ttokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty);\n\t\t\t\ttokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty);\n\n\t\t\t\tarrayIndex += offset;\n\t\t\t\tstringIndex += maxStrings;\n\t\t\t}\n\n\t\t\treturn GetXSigString(tokenArray);\n\t\t}\n\n\t\tprivate string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot)\n\t\t{\n\t\t\tvar xSigMaxIndex = 1023;\n\t\t\tvar tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count > xSigMaxIndex\n\t\t\t\t? xSigMaxIndex\n\t\t\t\t: directory.CurrentDirectoryResults.Count];\n\n\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"IsRoot: {0}, Directory Count: {1}, TokenArray.Length: {2}\", isRoot, directory.CurrentDirectoryResults.Count, tokenArray.Length);\n\n\t\t\tvar contacts = directory.CurrentDirectoryResults.Count > xSigMaxIndex\n\t\t\t\t? directory.CurrentDirectoryResults.Take(xSigMaxIndex)\n\t\t\t\t: directory.CurrentDirectoryResults;\n\n\t\t\tvar contactsToDisplay = isRoot\n\t\t\t\t? contacts.Where(c => c.ParentFolderId == \"root\")\n\t\t\t\t: contacts.Where(c => c.ParentFolderId != \"root\");\n\n\t\t\tvar counterIndex = 1;\n\t\t\tforeach (var entry in contactsToDisplay)\n\t\t\t{\n\t\t\t\tvar arrayIndex = counterIndex - 1;\n\t\t\t\tvar entryIndex = counterIndex;\n\n\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Entry{2:0000} Name: {0}, Folder ID: {1}, Type: {3}, ParentFolderId: {4}\",\n\t\t\t\t\tentry.Name, entry.FolderId, entryIndex, entry.GetType().FullName, entry.ParentFolderId);\n\n\t\t\t\tif (entry is DirectoryFolder)\n\t\t\t\t{\n\t\t\t\t\ttokenArray[arrayIndex] = new XSigSerialToken(entryIndex, String.Format(\"[+] {0}\", entry.Name));\n\n\t\t\t\t\tcounterIndex++;\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttokenArray[arrayIndex] = new XSigSerialToken(entryIndex, entry.Name);\n\n\t\t\t\tcounterIndex++;\n\t\t\t}\n\n\t\t\treturn GetXSigString(tokenArray);\n\t\t}\n\n\t\tprivate void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber,\n\t\t\t\t() => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue));\n\n\t\t\t//End All calls\n\t\t\ttrilist.SetSigFalseAction(joinMap.EndAllCalls.JoinNumber, EndAllCalls);\n\n\t\t\t//End a specific call, specified by index. Maximum 8 calls supported\n\t\t\tfor (int i = 0; i < joinMap.EndCallStart.JoinSpan; i++)\n\t\t\t{\n\t\t\t\tvar callIndex = i;\n\n\t\t\t\ttrilist.SetSigFalseAction((uint)(joinMap.EndCallStart.JoinNumber + i), () =>\n\t\t\t\t\t\t{\n\n\t\t\t\t\t\t\tif (callIndex < 0 || callIndex >= ActiveCalls.Count)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Cannot end call. No call found at index: {0}\", callIndex);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tvar call = ActiveCalls[callIndex];\n\t\t\t\t\t\t\tif (call != null)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tEndCall(call);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"[End Call] Unable to find call at index '{0}'\", i);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t}\n\n\t\t\ttrilist.SetBool(joinMap.HookState.JoinNumber, IsInCall);\n\n\t\t\tCallStatusChange += (sender, args) =>\n\t\t\t{\n\t\t\t\ttrilist.SetBool(joinMap.HookState.JoinNumber, IsInCall);\n\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Call Direction: {0}\", args.CallItem.Direction);\n\t\t\t\tDebug.LogMessage(LogEventLevel.Debug, this, \"Call is incoming: {0}\", args.CallItem.Direction == eCodecCallDirection.Incoming);\n\t\t\t\ttrilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status == eCodecCallStatus.Ringing);\n\n\t\t\t\tif (args.CallItem.Direction == eCodecCallDirection.Incoming)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem));\n\t\t\t\t\ttrilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem));\n\t\t\t\t\ttrilist.SetString(joinMap.IncomingCallName.JoinNumber, args.CallItem.Name);\n\t\t\t\t\ttrilist.SetString(joinMap.IncomingCallNumber.JoinNumber, args.CallItem.Number);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetString(joinMap.IncomingCallName.JoinNumber, string.Empty);\n\t\t\t\t\ttrilist.SetString(joinMap.IncomingCallNumber.JoinNumber, string.Empty);\n\t\t\t\t}\n\n\n\t\t\t\ttrilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig());\n\n\t\t\t\ttrilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count);\n\t\t\t};\n\n\t\t\tif (this is IJoinCalls joinCodec)\n\t\t\t{\n\t\t\t\ttrilist.SetSigFalseAction(joinMap.JoinAllCalls.JoinNumber, () => joinCodec.JoinAllCalls());\n\n\t\t\t\tfor (int i = 0; i < joinMap.JoinCallStart.JoinSpan; i++)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetSigFalseAction((uint)(joinMap.JoinCallStart.JoinNumber + i), () =>\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvar call = ActiveCalls[i];\n\t\t\t\t\t\t\t\t\t\tif (call != null)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tjoinCodec.JoinCall(call);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"[Join Call] Unable to find call at index '{0}'\", i);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this is IHasCallHold holdCodec)\n\t\t\t{\n\t\t\t\ttrilist.SetSigFalseAction(joinMap.HoldAllCalls.JoinNumber, () =>\n\t\t\t\t{\n\t\t\t\t\tforeach (var call in ActiveCalls)\n\t\t\t\t\t{\n\t\t\t\t\t\tholdCodec.HoldCall(call);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tfor (int i = 0; i < joinMap.HoldCallsStart.JoinSpan; i++)\n\t\t\t\t{\n\t\t\t\t\tvar index = i;\n\n\t\t\t\t\ttrilist.SetSigFalseAction((uint)(joinMap.HoldCallsStart.JoinNumber + index), () =>\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif (index < 0 || index >= ActiveCalls.Count) return;\n\n\t\t\t\t\t\t\t\t\t\tvar call = ActiveCalls[index];\n\t\t\t\t\t\t\t\t\t\tif (call != null)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tholdCodec.HoldCall(call);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"[Hold Call] Unable to find call at index '{0}'\", i);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\ttrilist.SetSigFalseAction((uint)(joinMap.ResumeCallsStart.JoinNumber + index), () =>\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif (index < 0 || index >= ActiveCalls.Count) return;\n\n\t\t\t\t\t\t\t\t\t\tvar call = ActiveCalls[index];\n\t\t\t\t\t\t\t\t\t\tif (call != null)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tholdCodec.ResumeCall(call);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"[Resume Call] Unable to find call at index '{0}'\", i);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\n\n\t\t\ttrilist.OnlineStatusChange += (device, args) =>\n{\n\tif (!args.DeviceOnLine) return;\n\n\t// TODO [ ] #983\n\tDebug.LogMessage(LogEventLevel.Information, this, \"LinkVideoCodecCallControlsToApi: device is {0}, IsInCall {1}\", args.DeviceOnLine ? \"online\" : \"offline\", IsInCall);\n\ttrilist.SetBool(joinMap.HookState.JoinNumber, IsInCall);\n\ttrilist.SetString(joinMap.CurrentCallData.JoinNumber, \"\\xFC\");\n\ttrilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig());\n};\n\t\t}\n\n\t\tprivate string UpdateCallStatusXSig()\n\t\t{\n\t\t\tconst int maxCalls = 8;\n\t\t\tconst int maxStrings = 6;\n\t\t\tconst int maxDigitals = 2;\n\t\t\tconst int offset = maxStrings + maxDigitals;\n\t\t\tvar stringIndex = 0;\n\t\t\tvar digitalIndex = maxStrings * maxCalls;\n\t\t\tvar arrayIndex = 0;\n\n\t\t\tvar tokenArray = new XSigToken[maxCalls * offset]; //set array size for number of calls * pieces of info\n\n\t\t\tforeach (var call in ActiveCalls)\n\t\t\t{\n\t\t\t\tif (arrayIndex >= maxCalls * offset)\n\t\t\t\t\tbreak;\n\t\t\t\t//digitals\n\t\t\t\ttokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall);\n\t\t\t\ttokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, call.IsOnHold);\n\n\t\t\t\t//serials\n\t\t\t\ttokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty);\n\t\t\t\ttokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, call.Number ?? String.Empty);\n\t\t\t\ttokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString());\n\t\t\t\ttokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, call.Type.ToString());\n\t\t\t\ttokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, call.Status.ToString());\n\t\t\t\tif (call.Duration != null)\n\t\t\t\t{\n\t\t\t\t\t// May need to verify correct string format here\n\t\t\t\t\tvar dur = string.Format(\"{0:c}\", call.Duration);\n\t\t\t\t\ttokenArray[arrayIndex + 6] = new XSigSerialToken(stringIndex + 6, dur);\n\t\t\t\t}\n\n\t\t\t\tarrayIndex += offset;\n\t\t\t\tstringIndex += maxStrings;\n\t\t\t\tdigitalIndex += maxDigitals;\n\t\t\t}\n\t\t\twhile (arrayIndex < maxCalls * offset)\n\t\t\t{\n\t\t\t\t//digitals\n\t\t\t\ttokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false);\n\t\t\t\ttokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false);\n\n\n\t\t\t\t//serials\n\t\t\t\ttokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty);\n\t\t\t\ttokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty);\n\n\t\t\t\tarrayIndex += offset;\n\t\t\t\tstringIndex += maxStrings;\n\t\t\t\tdigitalIndex += maxDigitals;\n\t\t\t}\n\n\t\t\treturn GetXSigString(tokenArray);\n\t\t}\n\n\t\tprivate void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmfAction(\"0\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmfAction(\"1\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmfAction(\"2\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmfAction(\"3\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmfAction(\"4\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmfAction(\"5\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmfAction(\"6\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmfAction(\"7\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmfAction(\"8\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmfAction(\"9\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmfAction(\"*\", trilist, joinMap));\n\t\t\ttrilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmfAction(\"#\", trilist, joinMap));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sends the specified string as a DTMF command.\n\t\t/// Reads the value of the SendDtmfToSpecificCallInstance digital join and SelectCall analog join to determine\n\t\t/// Whther to send to a specific call index or to the last connected call\n\t\t/// </summary>\n\t\t/// <param name=\"s\"></param>\n\t\t/// <param name=\"trilist\"></param>\n\t\t/// <param name=\"joinMap\"></param>\n\t\tprivate void SendDtmfAction(string s, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tif (!trilist.GetBool(joinMap.SendDtmfToSpecificCallIndex.JoinNumber))\n\t\t\t{\n\t\t\t\tSendDtmf(s);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvar callIndex = trilist.GetUshort(joinMap.SelectCall.JoinNumber);\n\t\t\t\tif (callIndex > 0 && callIndex <= 8)\n\t\t\t\t{\n\t\t\t\t\tvar call = ActiveCalls[callIndex - 1];\n\t\t\t\t\tif (call != null && call.IsActiveCall)\n\t\t\t\t\t{\n\t\t\t\t\t\tSendDtmf(s, call);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"Warning: No call found at index {0} or call is not active.\", callIndex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"Warning: Invalid call index specified.  Please use a value of 1-8.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle);\n\n\t\t\tcodec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentLayoutStringFb.JoinNumber]);\n\t\t}\n\n\t\tprivate void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetSigFalseAction(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeOn);\n\t\t\ttrilist.SetSigFalseAction(joinMap.CameraModeManual.JoinNumber, codec.CameraAutoModeOff);\n\n\t\t\tcodec.CameraAutoModeIsOnFeedback.OutputChange += (o, a) =>\n\t\t\t{\n\t\t\t\tif (codec is IHasCameraOff offCodec)\n\t\t\t\t{\n\t\t\t\t\tif (offCodec.CameraIsOffFeedback.BoolValue)\n\t\t\t\t\t{\n\t\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false);\n\t\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, false);\n\t\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue);\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue);\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue);\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue);\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, false);\n\t\t\t};\n\n\n\t\t\tif (codec is IHasCameraOff offModeCodec)\n\t\t\t{\n\t\t\t\tif (offModeCodec.CameraIsOffFeedback.BoolValue)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false);\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, false);\n\t\t\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue);\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue);\n\t\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttrilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue);\n\t\t\ttrilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue);\n\t\t\ttrilist.SetBool(joinMap.CameraModeOff.JoinNumber, false);\n\t\t}\n\n\t\tprivate void LinkVideoCodecSelfviewToApi(IHasCodecSelfView codec, BasicTriList trilist,\n\t\t\tVideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\ttrilist.SetSigFalseAction(joinMap.CameraSelfView.JoinNumber, codec.SelfViewModeToggle);\n\n\t\t\tcodec.SelfviewIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CameraSelfView.JoinNumber]);\n\t\t}\n\n\t\tprivate void LinkVideoCodecCameraToApi(IHasCodecCameras codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\t//Camera PTZ\n\t\t\ttrilist.SetBoolSigAction(joinMap.CameraTiltUp.JoinNumber, (b) =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return;\n\n\t\t\t\tif (b) camera.TiltUp();\n\t\t\t\telse camera.TiltStop();\n\t\t\t});\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.CameraTiltDown.JoinNumber, (b) =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return;\n\n\t\t\t\tif (b) camera.TiltDown();\n\t\t\t\telse camera.TiltStop();\n\t\t\t});\n\t\t\ttrilist.SetBoolSigAction(joinMap.CameraPanLeft.JoinNumber, (b) =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return;\n\n\t\t\t\tif (b) camera.PanLeft();\n\t\t\t\telse camera.PanStop();\n\t\t\t});\n\t\t\ttrilist.SetBoolSigAction(joinMap.CameraPanRight.JoinNumber, (b) =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return;\n\n\t\t\t\tif (b) camera.PanRight();\n\t\t\t\telse camera.PanStop();\n\t\t\t});\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.CameraZoomIn.JoinNumber, (b) =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return;\n\n\t\t\t\tif (b) camera.ZoomIn();\n\t\t\t\telse camera.ZoomStop();\n\t\t\t});\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.CameraZoomOut.JoinNumber, (b) =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return;\n\n\t\t\t\tif (b) camera.ZoomOut();\n\t\t\t\telse camera.ZoomStop();\n\t\t\t});\n\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.CameraFocusNear.JoinNumber, (b) =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return;\n\n\t\t\t\tif (b) camera.FocusNear();\n\t\t\t\telse camera.FocusStop();\n\t\t\t});\n\n\t\t\ttrilist.SetBoolSigAction(joinMap.CameraFocusFar.JoinNumber, (b) =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return;\n\n\t\t\t\tif (b) camera.FocusFar();\n\t\t\t\telse camera.FocusStop();\n\t\t\t});\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.CameraFocusAuto.JoinNumber, () =>\n\t\t\t{\n\t\t\t\tif (codec.SelectedCamera == null) return;\n\n\t\t\t\tif (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return;\n\n\t\t\t\tcamera.TriggerAutoFocus();\n\t\t\t});\n\n\t\t\t// Camera count\n\t\t\ttrilist.SetUshort(joinMap.CameraCount.JoinNumber, (ushort)codec.Cameras.Count);\n\n\t\t\t// Camera names\n\t\t\tfor (uint i = 0; i < joinMap.CameraNamesFb.JoinSpan; i++)\n\t\t\t{\n\t\t\t\t//Check the count first\n\t\t\t\tif (i < codec.Cameras.Count && codec.Cameras[(int)i] != null)\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, codec.Cameras[(int)i].Name);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttrilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, \"\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//Camera Select\n\t\t\ttrilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) =>\n\t\t\t{\n\t\t\t\tif (i > 0 && i <= codec.Cameras.Count)\n\t\t\t\t{\n\t\t\t\t\tcodec.SelectCamera(codec.Cameras[i - 1].Key);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDebug.LogMessage(LogEventLevel.Information, this, \"Unable to select.  No camera found at index {0}\", i);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set initial selected camera feedback\n\t\t\tif (codec.SelectedCamera != null)\n\t\t\t{\n\t\t\t\ttrilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)codec.Cameras.FindIndex((c) => c.Key == codec.SelectedCamera.Key));\n\t\t\t}\n\n\t\t\tcodec.CameraSelected += (sender, args) =>\n\t\t\t{\n\t\t\t\tvar i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key);\n\n\t\t\t\ttrilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)(i + 1));\n\n\t\t\t\tif (codec is IHasCodecRoomPresets)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!(args.SelectedCamera is IHasCameraPresets))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tvar cam = args.SelectedCamera as IHasCameraPresets;\n\t\t\t\tSetCameraPresetNames(cam.Presets);\n\n\t\t\t\t(args.SelectedCamera as IHasCameraPresets).PresetsListHasChanged += (o, eventArgs) => SetCameraPresetNames(cam.Presets);\n\n\t\t\t\ttrilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber,\n\t\t\t\t\t\t(a) =>\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcam.PresetSelect(a);\n\t\t\t\t\t\t\ttrilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, a);\n\t\t\t\t\t\t});\n\n\t\t\t\ttrilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber,\n\t\t\t\t\t() =>\n\t\t\t\t\t{\n\t\t\t\t\t\tcam.PresetStore(trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue,\n\t\t\t\t\t\t\tString.Empty);\n\t\t\t\t\t\ttrilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000);\n\t\t\t\t\t});\n\t\t\t};\n\n\t\t\tif (!(codec is IHasCodecRoomPresets)) return;\n\n\t\t\tvar presetCodec = codec as IHasCodecRoomPresets;\n\n\t\t\tpresetCodec.CodecRoomPresetsListHasChanged +=\n\t\t\t\t(sender, args) => SetCameraPresetNames(presetCodec.NearEndPresets);\n\n\t\t\t//Camera Presets\n\t\t\ttrilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) =>\n\t\t\t{\n\t\t\t\tpresetCodec.CodecRoomPresetSelect(i);\n\t\t\t});\n\n\n\t\t\t// Far End Presets\n\t\t\ttrilist.SetUShortSigAction(joinMap.FarEndPresetSelect.JoinNumber, (i) =>\n\t\t\t{\n\t\t\t\tpresetCodec.SelectFarEndPreset(i);\n\t\t\t});\n\n\n\t\t\ttrilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber,\n\t\t\t\t\t() =>\n\t\t\t\t\t{\n\t\t\t\t\t\tpresetCodec.CodecRoomPresetStore(\n\t\t\t\t\t\t\ttrilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty);\n\t\t\t\t\t\ttrilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000);\n\t\t\t\t\t});\n\n\t\t\ttrilist.OnlineStatusChange += (device, args) =>\n\t\t\t{\n\t\t\t\tif (!args.DeviceOnLine) return;\n\n\t\t\t\t// TODO [ ] Issue #868\n\t\t\t\ttrilist.SetString(joinMap.CameraPresetNames.JoinNumber, \"\\xFC\");\n\t\t\t\tSetCameraPresetNames(presetCodec.NearEndPresets);\n\t\t\t};\n\t\t}\n\n\t\t// Following fields only used for Bridging\n\t\tprivate int _selectedRecentCallItemIndex;\n\t\tprivate DirectoryItem _selectedDirectoryItem;\n\n\t\tprivate void LinkVideoCodecCallHistoryToApi(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\tcodec.CallHistory.RecentCallsListHasChanged += (o, a) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tUpdateCallHistory(codec, trilist, joinMap);\n\t\t\t\t\t};\n\n\t\t\t// Selected item action and feedback\n\t\t\ttrilist.SetUShortSigAction(joinMap.SelectRecentCallItem.JoinNumber, (u) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tif (u == 0 || u > codec.CallHistory.RecentCalls.Count)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tDebug.LogMessage(LogEventLevel.Verbose, this, \"Recent Call History index out of range\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t_selectedRecentCallItemIndex = (int)(u - 1);\n\t\t\t\t\t\ttrilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, u);\n\n\t\t\t\t\t\tvar _selectedRecentCallItem = codec.CallHistory.RecentCalls[_selectedRecentCallItemIndex];\n\n\t\t\t\t\t\tif (_selectedRecentCallItem != null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttrilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, _selectedRecentCallItem.Name);\n\t\t\t\t\t\t\ttrilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, _selectedRecentCallItem.Number);\n\t\t\t\t\t\t\ttrilist.SetSigFalseAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber, () => codec.RemoveCallHistoryEntry(_selectedRecentCallItem));\n\t\t\t\t\t\t\ttrilist.SetSigFalseAction(joinMap.DialSelectedRecentCallItem.JoinNumber, () => this.Dial(_selectedRecentCallItem.Number));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttrilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty);\n\t\t\t\t\t\t\ttrilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty);\n\t\t\t\t\t\t\ttrilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber);\n\t\t\t\t\t\t\ttrilist.ClearBoolSigAction(joinMap.DialSelectedRecentCallItem.JoinNumber);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t}\n\n\n\n\t\tprivate void UpdateCallHistory(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)\n\t\t{\n\t\t\t// Clear out selected item\n\t\t\t_selectedRecentCallItemIndex = 0;\n\n\t\t\ttrilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, 0);\n\t\t\ttrilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty);\n\t\t\ttrilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty);\n\t\t\ttrilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber);\n\t\t\t//\n\n\t\t\ttrilist.SetUshort(joinMap.RecentCallCount.JoinNumber, (ushort)codec.CallHistory.RecentCalls.Count);\n\n\t\t\t// Update the call history joins\n\t\t\tvar maxItems = joinMap.RecentCallNamesStart.JoinSpan;\n\n\t\t\t// Create history\n\t\t\tuint index = 0;\n\t\t\tfor (uint i = 0; i < maxItems && i < codec.CallHistory.RecentCalls.Count; i++)\n\t\t\t{\n\t\t\t\ttrilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].Name);\n\t\t\t\ttrilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].StartTime.ToShortTimeString());\n\t\t\t\ttrilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)codec.CallHistory.RecentCalls[(int)i].OccurrenceType);\n\t\t\t\t//i++;\n\t\t\t\tindex = i;\n\t\t\t}\n\n\t\t\t//foreach(var item in codec.CallHistory.RecentCalls)\n\t\t\t//{\n\t\t\t//    trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, item.Name);\n\t\t\t//    trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, item.StartTime.ToShortTimeString());\n\t\t\t//    trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)item.OccurrenceType);\n\t\t\t//    i++;\n\t\t\t//}\n\n\t\t\t// Clears existing items \n\t\t\tfor (uint j = index; j < maxItems; j++)\n\t\t\t{\n\t\t\t\ttrilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + j, string.Empty);\n\t\t\t\ttrilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + j, string.Empty);\n\t\t\t\ttrilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + j, 0);\n\t\t\t}\n\t\t}\n\n\t\tprivate string SetCameraPresetNames(IEnumerable<CodecRoomPreset> presets)\n\t\t{\n\t\t\treturn SetCameraPresetNames(presets.Select(p => p.Description).ToList());\n\t\t}\n\n\t\tprivate string SetCameraPresetNames(IEnumerable<CameraPreset> presets)\n\t\t{\n\t\t\treturn SetCameraPresetNames(presets.Select(p => p.Description).ToList());\n\t\t}\n\n\t\tprivate string SetCameraPresetNames(ICollection<string> presets)\n\t\t{\n\t\t\tvar i = 1; //start index for xsig;\n\n\t\t\tvar tokenArray = new XSigToken[presets.Count];\n\n\t\t\tforeach (var preset in presets)\n\t\t\t{\n\t\t\t\tvar cameraPreset = new XSigSerialToken(i, preset);\n\t\t\t\ttokenArray[i - 1] = cameraPreset;\n\t\t\t\ti++;\n\t\t\t}\n\n\t\t\treturn GetXSigString(tokenArray);\n\t\t}\n\n\t\tprivate string GetXSigString(XSigToken[] tokenArray)\n\t\t{\n\t\t\tstring returnString;\n\t\t\tusing (var s = new MemoryStream())\n\t\t\t{\n\t\t\t\tusing (var tw = new XSigTokenStreamWriter(s, true))\n\t\t\t\t{\n\t\t\t\t\ttw.WriteXSigData(tokenArray);\n\t\t\t\t}\n\n\t\t\t\tvar xSig = s.ToArray();\n\n\t\t\t\treturnString = Encoding.GetEncoding(XSigEncoding).GetString(xSig, 0, xSig.Length);\n\t\t\t}\n\n\t\t\treturn returnString;\n\t\t}\n\n\t\t#endregion\n\t}\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/AuthorizationResponse.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Represents a AuthorizationResponse\n    /// </summary>\n    public class AuthorizationResponse\n    {\n\n        /// <summary>\n        /// Gets or sets the Authorized\n        /// </summary>\n        [JsonProperty(\"authorized\")]\n        public bool Authorized { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Reason\n        /// </summary>\n        [JsonProperty(\"reason\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Reason { get; set; } = null;\n    }\n\n    /// <summary>\n    /// Represents a AuthorizationRequest\n    /// </summary>\n    public class AuthorizationRequest\n    {\n\n        /// <summary>\n        /// Gets or sets the GrantCode\n        /// </summary>\n        [JsonProperty(\"grantCode\")]\n        public string GrantCode { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/ClientSpecificUpdateRequest.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials\n{\n  /// <summary>\n  /// Send an update request for a specific client\n  /// </summary>\n  [Obsolete]\n  public class ClientSpecificUpdateRequest\n  {\n    /// <summary>\n    /// Initialize an instance of the <see cref=\"ClientSpecificUpdateRequest\"/> class.\n    /// </summary>\n    /// <param name=\"action\"></param>\n    public ClientSpecificUpdateRequest(Action<string> action)\n    {\n      ResponseMethod = action;\n    }\n\n    /// <summary>\n    /// Gets or sets the ResponseMethod\n    /// </summary>\n    public Action<string> ResponseMethod { get; private set; }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/IDelayedConfiguration.cs",
    "content": "﻿using System;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Defines the contract for IDelayedConfiguration\n    /// </summary>\n    public interface IDelayedConfiguration\n    {\n        /// <summary>\n        /// Event triggered when the configuration is ready. Used when Mobile Control is interacting with a SIMPL program.\n        /// </summary>\n        event EventHandler<EventArgs> ConfigurationIsReady;\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/MessageToClients.cs",
    "content": "using System;\nusing System.Threading;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core.Queues;\nusing PepperDash.Essentials.WebSocketServer;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials\n{\n  /// <summary>\n  /// Represents a MessageToClients\n  /// </summary>\n  public class MessageToClients : IQueueMessage\n  {\n    private readonly MobileControlWebsocketServer _server;\n    private readonly object msgToSend;\n\n    /// <summary>\n    /// Message to send to Direct Server Clients\n    /// </summary>\n    /// <param name=\"msg\">message object to send</param>\n    /// <param name=\"server\">WebSocket server instance</param>\n    public MessageToClients(object msg, MobileControlWebsocketServer server)\n    {\n      _server = server;\n      msgToSend = msg;\n    }\n\n    /// <summary>\n    /// Message to send to Direct Server Clients\n    /// </summary>\n    /// <param name=\"msg\">message object to send</param>\n    /// <param name=\"server\">WebSocket server instance</param>\n    public MessageToClients(DeviceStateMessageBase msg, MobileControlWebsocketServer server)\n    {\n      _server = server;\n      msgToSend = msg;\n    }\n\n    #region Implementation of IQueueMessage\n\n    /// <summary>\n    /// Dispatch method\n    /// </summary>\n    public void Dispatch()\n    {\n      try\n      {\n        if (_server == null)\n        {\n          Debug.LogMessage(LogEventLevel.Warning, \"Cannot send message. Server is null\");\n          return;\n        }\n\n        var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,\n        new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });\n\n        var clientSpecificMessage = msgToSend as MobileControlMessage;\n        if (clientSpecificMessage.ClientId != null)\n        {\n          var clientId = clientSpecificMessage.ClientId;\n\n          _server.LogVerbose(\"Message TX To client {clientId}: {message}\", clientId, message);\n\n          _server.SendMessageToClient(clientId, message);\n\n          return;\n        }\n\n        _server.SendMessageToAllClients(message);\n\n        _server.LogVerbose(\"Message TX To all clients: {message}\", message);\n      }\n      catch (ThreadAbortException)\n      {\n        //Swallowing this exception, as it occurs on shutdown and there's no need to print out a scary stack trace\n      }\n      catch (Exception ex)\n      {\n        Debug.LogMessage(ex, \"Caught an exception in the Transmit Processor\");\n      }\n    }\n    #endregion\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/MobileControlAction.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Represents a MobileControlAction\n    /// </summary>\n    public class MobileControlAction : IMobileControlAction\n    {\n        /// <summary>\n        /// Gets the Messenger\n        /// </summary>\n        public IMobileControlMessenger Messenger { get; private set; }\n\n        /// <summary>\n        /// Action to execute when this path is matched\n        /// </summary>\n        public Action<string, string, JToken> Action { get; private set; }\n\n        /// <summary>\n        /// Initialize an instance of the <see cref=\"MobileControlAction\"/> class\n        /// </summary>\n        /// <param name=\"messenger\">Messenger associated with this action</param>\n        /// <param name=\"handler\">Action to take when this path is matched</param>\n        public MobileControlAction(IMobileControlMessenger messenger, Action<string, string, JToken> handler)\n        {\n            Messenger = messenger;\n            Action = handler;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs",
    "content": "﻿using System.Collections.Generic;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Represents a MobileControlConfig\n    /// </summary>\n    public class MobileControlConfig\n    {\n        /// <summary>\n        /// Gets or sets the ServerUrl\n        /// </summary>\n        [JsonProperty(\"serverUrl\")]\n        public string ServerUrl { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ClientAppUrl\n        /// </summary>\n        [JsonProperty(\"clientAppUrl\")]\n        public string ClientAppUrl { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DirectServer\n        /// </summary>\n        [JsonProperty(\"directServer\")]\n        public MobileControlDirectServerPropertiesConfig DirectServer { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ApplicationConfig\n        /// </summary>\n        [JsonProperty(\"applicationConfig\")]\n        public MobileControlApplicationConfig ApplicationConfig { get; set; } = null;\n\n        /// <summary>\n        /// Gets or sets the EnableApiServer\n        /// </summary>\n        [JsonProperty(\"enableApiServer\")]\n        public bool EnableApiServer { get; set; } = true;\n\n        /// <summary>\n        /// Enable subscriptions for Messengers\n        /// </summary>\n        [JsonProperty(\"enableMessengerSubscriptions\")]\n        public bool EnableMessengerSubscriptions { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlDirectServerPropertiesConfig\n    /// </summary>\n    public class MobileControlDirectServerPropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the EnableDirectServer\n        /// </summary>\n        [JsonProperty(\"enableDirectServer\")]\n        public bool EnableDirectServer { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        [JsonProperty(\"port\")]\n        public int Port { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Logging\n        /// </summary>\n        [JsonProperty(\"logging\")]\n        public MobileControlLoggingConfig Logging { get; set; }\n\n        /// <summary>\n        /// Gets or sets the AutomaticallyForwardPortToCSLAN\n        /// </summary>\n        [JsonProperty(\"automaticallyForwardPortToCSLAN\")]\n        public bool? AutomaticallyForwardPortToCSLAN { get; set; }\n\n        /// <summary>\n        /// Gets or sets the CSLanUiDeviceKeys\n        /// </summary>\n        /// <remarks>\n        /// A list of device keys for the CS LAN UI. These devices will get the CS LAN IP address instead of the LAN IP Address\n        /// </remarks>\n        [JsonProperty(\"csLanUiDeviceKeys\")]\n        public List<string> CSLanUiDeviceKeys { get; set; }\n\n        /// <summary>\n        /// Get or set the Secure property\n        /// </summary>\n        /// <remarks>\n        /// Indicates whether the connection is secure (HTTPS).\n        /// </remarks>\n        [JsonProperty(\"Secure\")]\n        public bool Secure { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the MobileControlDirectServerPropertiesConfig class.\n        /// </summary>\n        public MobileControlDirectServerPropertiesConfig()\n        {\n            Logging = new MobileControlLoggingConfig();\n        }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlLoggingConfig\n    /// </summary>\n    public class MobileControlLoggingConfig\n    {\n\n        /// <summary>\n        /// Gets or sets the EnableRemoteLogging\n        /// </summary>\n        [JsonProperty(\"enableRemoteLogging\")]\n        public bool EnableRemoteLogging { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Host\n        /// </summary>\n        [JsonProperty(\"host\")]\n        public string Host { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        [JsonProperty(\"port\")]\n        public int Port { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlRoomBridgePropertiesConfig\n    /// </summary>\n    public class MobileControlRoomBridgePropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        [JsonProperty(\"key\")]\n        public string Key { get; set; }\n\n        /// <summary>\n        /// Gets or sets the RoomKey\n        /// </summary>\n        [JsonProperty(\"roomKey\")]\n        public string RoomKey { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlSimplRoomBridgePropertiesConfig\n    /// </summary>\n    public class MobileControlSimplRoomBridgePropertiesConfig\n    {\n        /// <summary>\n        /// Gets or sets the EiscId\n        /// </summary>\n        [JsonProperty(\"eiscId\")]\n        public string EiscId { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlApplicationConfig\n    /// </summary>\n    public class MobileControlApplicationConfig\n    {\n        /// <summary>\n        /// Gets or sets the ApiPath\n        /// </summary>\n        [JsonProperty(\"apiPath\")]\n        public string ApiPath { get; set; }\n\n        /// <summary>\n        /// Gets or sets the GatewayAppPath\n        /// </summary>\n        [JsonProperty(\"gatewayAppPath\")]\n        public string GatewayAppPath { get; set; }\n\n        /// <summary>\n        /// Gets or sets the EnableDev\n        /// </summary>\n        [JsonProperty(\"enableDev\")]\n        public bool? EnableDev { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LogoPath\n        /// </summary>\n        [JsonProperty(\"logoPath\")]\n        public string LogoPath { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IconSet\n        /// </summary>\n        [JsonProperty(\"iconSet\")]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public MCIconSet? IconSet { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LoginMode\n        /// </summary>\n        [JsonProperty(\"loginMode\")]\n        public string LoginMode { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Modes\n        /// </summary>\n        [JsonProperty(\"modes\")]\n        public Dictionary<string, McMode> Modes { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Logging\n        /// </summary>\n        [JsonProperty(\"enableRemoteLogging\")]\n        public bool Logging { get; set; }\n\n        /// <summary>\n        /// Gets or sets the PartnerMetadata\n        /// </summary>\n        [JsonProperty(\"partnerMetadata\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<MobileControlPartnerMetadata> PartnerMetadata { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlPartnerMetadata\n    /// </summary>\n    public class MobileControlPartnerMetadata\n    {\n        /// <summary>\n        /// Gets or sets the Role\n        /// </summary>\n        [JsonProperty(\"role\")]\n        public string Role { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Description\n        /// </summary>\n        [JsonProperty(\"description\")]\n        public string Description { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LogoPath\n        /// </summary>\n        [JsonProperty(\"logoPath\")]\n        public string LogoPath { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a McMode\n    /// </summary>\n    public class McMode\n    {\n        /// <summary>\n        /// Gets or sets the ListPageText\n        /// </summary>\n        [JsonProperty(\"listPageText\")]\n        public string ListPageText { get; set; }\n\n        /// <summary>\n        /// Gets or sets the LoginHelpText\n        /// </summary>\n        [JsonProperty(\"loginHelpText\")]\n        public string LoginHelpText { get; set; }\n\n        /// <summary>\n        /// Gets or sets the PasscodePageText\n        /// </summary>\n        [JsonProperty(\"passcodePageText\")]\n        public string PasscodePageText { get; set; }\n    }\n\n    /// <summary>\n    /// Enumeration of MCIconSet values\n    /// </summary>\n    public enum MCIconSet\n    {\n        /// <summary>\n        /// Google icon set\n        /// </summary>\n        GOOGLE,\n\n        /// <summary>\n        /// Habanero icon set\n        /// </summary>\n        HABANERO,\n\n        /// <summary>\n        /// Neo icon set\n        /// </summary>\n        NEO\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/MobileControlDeviceFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\n\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Factory to create a Mobile Control System Controller\n    /// </summary>\n    public class MobileControlDeviceFactory : EssentialsDeviceFactory<MobileControlSystemController>\n    {\n        /// <summary>\n        /// Create the factory for a Mobile Control System Controller\n        /// </summary>\n        public MobileControlDeviceFactory()\n        {\n            TypeNames = new List<string> { \"appserver\", \"mobilecontrol\", \"webserver\" };\n        }\n\n        /// <inheritdoc />\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            try\n            {\n                var props = dc.Properties.ToObject<MobileControlConfig>();\n                return new MobileControlSystemController(dc.Key, dc.Name, props);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(e, \"Error building Mobile Control System Controller\");\n                return null;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs",
    "content": "﻿using System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Essentials.Core.Config;\n\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Configuration class for sending data to Mobile Control Edge or a client using the Direct Server\n    /// </summary>\n    public class MobileControlEssentialsConfig : EssentialsConfig\n    {\n        /// <summary>\n        /// Current versions for the system\n        /// </summary>\n        [JsonProperty(\"runtimeInfo\")]\n        public MobileControlRuntimeInfo RuntimeInfo { get; set; }\n\n        /// <summary>\n        /// Create Configuration for Mobile Control. Used as part of the data sent to a client\n        /// </summary>\n        /// <param name=\"config\">The base configuration</param>\n        public MobileControlEssentialsConfig(EssentialsConfig config)\n            : base()\n        {\n            Devices = config.Devices;\n            Info = config.Info;\n            JoinMaps = config.JoinMaps;\n            Rooms = config.Rooms;\n            SourceLists = config.SourceLists;\n            DestinationLists = config.DestinationLists;\n            SystemUrl = config.SystemUrl;\n            TemplateUrl = config.TemplateUrl;\n            TieLines = config.TieLines;\n\n            if (Info == null)\n                Info = new InfoConfig();\n\n            RuntimeInfo = new MobileControlRuntimeInfo();\n        }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlRuntimeInfo\n    /// </summary>\n    public class MobileControlRuntimeInfo\n    {\n\n        /// <summary>\n        /// Gets or sets the PluginVersion\n        /// </summary>\n        [JsonProperty(\"pluginVersion\")]\n        public string PluginVersion { get; set; }\n\n        /// <summary>\n        /// Essentials Version\n        /// </summary>\n        [JsonProperty(\"essentialsVersion\")]\n        public string EssentialsVersion { get; set; }\n\n        /// <summary>\n        /// PepperDash Core Version\n        /// </summary>\n        [JsonProperty(\"pepperDashCoreVersion\")]\n        public string PepperDashCoreVersion { get; set; }\n\n\n        /// <summary>\n        /// List of Plugins loaded on this system\n        /// </summary>\n        [JsonProperty(\"essentialsPlugins\")]\n        public List<LoadedAssembly> EssentialsPlugins { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Reflection;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Factory class for the Mobile Control App Controller\n    /// </summary>\n    public class MobileControlFactory\n    {\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileControlFactory\"/> class.\n        /// </summary>\n        public MobileControlFactory()\n        {\n            var assembly = Assembly.GetExecutingAssembly();\n\n            PluginLoader.SetEssentialsAssembly(assembly.GetName().Name, assembly);\n\n            var types = assembly.GetTypes().Where(t => typeof(IDeviceFactory).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);\n\n            if (types == null)\n            {\n                return;\n            }\n\n            foreach (var type in types)\n            {\n                try\n                {\n                    var factory = (IDeviceFactory)Activator.CreateInstance(type);\n\n                    LoadDeviceFactories(factory);\n                }\n                catch (Exception ex)\n                {\n                    Debug.LogMessage(ex, \"Unable to load type '{type}' DeviceFactory: {factory}\", null, type.Name);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Loads device factories from the specified plugin device factory and registers them for use.\n        /// </summary>\n        /// <remarks>This method retrieves metadata from the provided <paramref name=\"deviceFactory\"/>, including\n        /// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type\n        /// names are converted to lowercase for registration.</remarks>\n        /// <param name=\"deviceFactory\">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>\n        private static void LoadDeviceFactories(IDeviceFactory deviceFactory)\n        {\n            foreach (var typeName in deviceFactory.TypeNames)\n            {\n                string description = (deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0)\n                    ? descriptionAttribute[0].Description\n                    : \"No description available\"; // Default value if no DescriptionAttribute is found\n\n                DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/MobileControlSimplDeviceBridge.cs",
    "content": "﻿using Crestron.SimplSharpPro.EthernetCommunication;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing System;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a MobileControlSimplDeviceBridge\n    /// </summary>\n    public class MobileControlSimplDeviceBridge : Device, IChannel, INumericKeypad\n    {\n        /// <summary>\n        /// EISC used to talk to Simpl\n        /// </summary>\n        private readonly ThreeSeriesTcpIpEthernetIntersystemCommunications _eisc;\n\n        public MobileControlSimplDeviceBridge(string key, string name,\n            ThreeSeriesTcpIpEthernetIntersystemCommunications eisc)\n            : base(key, name)\n        {\n            _eisc = eisc;\n        }\n\n        #region IChannel Members\n\n        /// <summary>\n        /// ChannelUp method\n        /// </summary>\n        public void ChannelUp(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// ChannelDown method\n        /// </summary>\n        public void ChannelDown(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// LastChannel method\n        /// </summary>\n        public void LastChannel(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Guide method\n        /// </summary>\n        public void Guide(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Info method\n        /// </summary>\n        public void Info(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Exit method\n        /// </summary>\n        public void Exit(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        #endregion\n\n        #region INumericKeypad Members\n\n        /// <summary>\n        /// Digit0 method\n        /// </summary>\n        public void Digit0(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit1 method\n        /// </summary>\n        public void Digit1(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit2 method\n        /// </summary>\n        public void Digit2(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit3 method\n        /// </summary>\n        public void Digit3(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit4 method\n        /// </summary>\n        public void Digit4(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit5 method\n        /// </summary>\n        public void Digit5(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit6 method\n        /// </summary>\n        public void Digit6(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit7 method\n        /// </summary>\n        public void Digit7(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit8 method\n        /// </summary>\n        public void Digit8(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        /// <summary>\n        /// Digit9 method\n        /// </summary>\n        public void Digit9(bool pressRelease)\n        {\n            _eisc.SetBool(1111, pressRelease);\n        }\n\n        public bool HasKeypadAccessoryButton1\n        {\n            get { throw new NotImplementedException(); }\n        }\n\n        public string KeypadAccessoryButton1Label\n        {\n            get { throw new NotImplementedException(); }\n        }\n\n        /// <summary>\n        /// KeypadAccessoryButton1 method\n        /// </summary>\n        public void KeypadAccessoryButton1(bool pressRelease)\n        {\n            throw new NotImplementedException();\n        }\n\n        public bool HasKeypadAccessoryButton2\n        {\n            get { throw new NotImplementedException(); }\n        }\n\n        public string KeypadAccessoryButton2Label\n        {\n            get { throw new NotImplementedException(); }\n        }\n\n        /// <summary>\n        /// KeypadAccessoryButton2 method\n        /// </summary>\n        public void KeypadAccessoryButton2(bool pressRelease)\n        {\n            throw new NotImplementedException();\n        }\n\n        #endregion\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text.RegularExpressions;\nusing System.Threading.Tasks;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.CrestronIO;\nusing Crestron.SimplSharp.Net.Http;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.CrestronIO;\nusing PepperDash.Essentials.Core.DeviceInfo;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Core.Lighting;\nusing PepperDash.Essentials.Core.Monitoring;\nusing PepperDash.Essentials.Core.Queues;\nusing PepperDash.Essentials.Core.Routing;\nusing PepperDash.Essentials.Core.Shades;\nusing PepperDash.Essentials.Core.Web;\nusing PepperDash.Essentials.Devices.Common.AudioCodec;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing PepperDash.Essentials.Devices.Common.Displays;\nusing PepperDash.Essentials.Devices.Common.Lighting;\nusing PepperDash.Essentials.Devices.Common.SoftCodec;\nusing PepperDash.Essentials.Devices.Common.VideoCodec;\nusing PepperDash.Essentials.Room.MobileControl;\nusing PepperDash.Essentials.RoomBridges;\nusing PepperDash.Essentials.Services;\nusing PepperDash.Essentials.WebApiHandlers;\nusing PepperDash.Essentials.WebSocketServer;\nusing WebSocketSharp;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Represents a MobileControlSystemController\n    /// </summary>\n    public class MobileControlSystemController : EssentialsDevice, IMobileControl\n    {\n        private bool _initialized = false;\n        private const long ServerReconnectInterval = 5000;\n        private const long PingInterval = 25000;\n\n        private readonly Dictionary<string, List<IMobileControlAction>> _actionDictionary =\n            new Dictionary<string, List<IMobileControlAction>>(\n                StringComparer.InvariantCultureIgnoreCase\n            );\n\n        /// <summary>\n        /// Actions\n        /// </summary>\n        public ReadOnlyDictionary<string, List<IMobileControlAction>> ActionDictionary => new ReadOnlyDictionary<string, List<IMobileControlAction>>(_actionDictionary);\n\n        private readonly GenericQueue _receiveQueue;\n        private readonly List<MobileControlBridgeBase> _roomBridges =\n            new List<MobileControlBridgeBase>();\n\n        private readonly Dictionary<string, IMobileControlMessenger> _messengers =\n            new Dictionary<string, IMobileControlMessenger>();\n\n        private readonly Dictionary<string, IMobileControlMessenger> _defaultMessengers =\n            new Dictionary<string, IMobileControlMessenger>();\n\n        /// <summary>\n        /// Get the custom messengers with subscriptions\n        /// </summary>\n        public ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions> Messengers => new ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions>(_messengers.Values.OfType<IMobileControlMessengerWithSubscriptions>().ToDictionary(k => k.Key, v => v));\n\n        /// <summary>\n        /// Get the default messengers\n        /// </summary>\n        public ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions> DefaultMessengers => new ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions>(_defaultMessengers.Values.OfType<IMobileControlMessengerWithSubscriptions>().ToDictionary(k => k.Key, v => v));\n\n        private readonly GenericQueue _transmitToServerQueue;\n\n        private readonly GenericQueue _transmitToClientsQueue;\n\n        private bool _disableReconnect;\n        private WebSocket _wsClient2;\n\n        /// <summary>\n        /// Gets or sets the ApiService\n        /// </summary>\n        public MobileControlApiService ApiService { get; private set; }\n\n        /// <summary>\n        /// Get Room Bridges associated with this controller\n        /// </summary>\n        public List<MobileControlBridgeBase> RoomBridges => _roomBridges;\n\n        private readonly MobileControlWebsocketServer _directServer;\n\n        /// <summary>\n        /// Get the Direct Server instance associated with this controller\n        /// </summary>\n        public MobileControlWebsocketServer DirectServer => _directServer;\n\n        private readonly CCriticalSection _wsCriticalSection = new CCriticalSection();\n\n        /// <summary>\n        /// Gets or sets the SystemUrl\n        /// </summary>\n        public string SystemUrl; //set only from SIMPL Bridge!\n\n        /// <summary>\n        /// True if the Mobile Control Edge Server Websocket is connected\n        /// </summary>\n        public bool Connected => _wsClient2 != null && _wsClient2.IsAlive;\n\n        private IEssentialsRoomCombiner _roomCombiner;\n\n        /// <summary>\n        /// Gets the SystemUuid from configuration or SIMPL Bridge\n        /// </summary>\n        public string SystemUuid\n        {\n            get\n            {\n                // Check to see if the SystemUuid value is populated. If not populated from configuration, check for value from SIMPL bridge.\n                if (\n                    !string.IsNullOrEmpty(ConfigReader.ConfigObject.SystemUuid)\n                    && ConfigReader.ConfigObject.SystemUuid != \"missing url\"\n                )\n                {\n                    return ConfigReader.ConfigObject.SystemUuid;\n                }\n\n                this.LogWarning(\n                    \"No system_url value defined in config.  Checking for value from SIMPL Bridge.\"\n                );\n\n                if (string.IsNullOrEmpty(SystemUrl))\n                {\n                    this.LogError(\n                        \"No system_url value defined in config or SIMPL Bridge.  Unable to connect to Mobile Control.\"\n                    );\n                    return string.Empty;\n                }\n\n                var result = Regex.Match(SystemUrl, @\"https?:\\/\\/.*\\/systems\\/(.*)\\/#.*\");\n                string uuid = result.Groups[1].Value;\n                return uuid;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the ApiOnlineAndAuthorized\n        /// </summary>\n        public BoolFeedback ApiOnlineAndAuthorized { get; private set; }\n\n        /// <summary>\n        /// Used for tracking HTTP debugging\n        /// </summary>\n        private bool _httpDebugEnabled;\n\n        private bool _isAuthorized;\n\n        /// <summary>\n        /// Tracks if the system is authorized to the API server\n        /// </summary>\n        public bool IsAuthorized\n        {\n            get { return _isAuthorized; }\n            private set\n            {\n                if (value == _isAuthorized)\n                    return;\n\n                _isAuthorized = value;\n                ApiOnlineAndAuthorized.FireUpdate();\n            }\n        }\n\n        private DateTime _lastAckMessage;\n\n        /// <summary>\n        /// Gets the LastAckMessage timestamp\n        /// </summary>\n        public DateTime LastAckMessage => _lastAckMessage;\n\n        private CTimer _pingTimer;\n\n        private CTimer _serverReconnectTimer;\n        private LogLevel _wsLogLevel = LogLevel.Error;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"MobileControlSystemController\"/> class.\n        /// </summary>\n        /// <param name=\"key\">The unique key for this controller.</param>\n        /// <param name=\"name\">The name of the controller.</param>\n        /// <param name=\"config\">The configuration settings for the controller.</param>\n        public MobileControlSystemController(string key, string name, MobileControlConfig config)\n            : base(key, name)\n        {\n            Config = config;\n\n            // The queue that will collect the incoming messages in the order they are received\n            //_receiveQueue = new ReceiveQueue(key, ParseStreamRx);\n            _receiveQueue = new GenericQueue(\n                key + \"-rxqueue\",\n                Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority.HighPriority,\n                25\n            );\n\n            // The queue that will collect the outgoing messages in the order they are received\n            _transmitToServerQueue = new GenericQueue(\n                key + \"-txqueue\",\n                Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority.HighPriority,\n                25\n            );\n\n            if (Config.DirectServer != null && Config.DirectServer.EnableDirectServer)\n            {\n                _directServer = new MobileControlWebsocketServer(\n                    Key + \"-directServer\",\n                    Config.DirectServer.Port,\n                    this\n                );\n                DeviceManager.AddDevice(_directServer);\n\n                _transmitToClientsQueue = new GenericQueue(\n                    key + \"-clienttxqueue\",\n                    Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority.HighPriority,\n                    25\n                );\n            }\n\n            Host = config.ServerUrl;\n            if (!Host.StartsWith(\"http\"))\n            {\n                Host = \"https://\" + Host;\n            }\n\n            ApiService = new MobileControlApiService(Host);\n\n            this.LogInformation(\n                \"Mobile UI controller initializing for server:{0}\",\n                config.ServerUrl\n            );\n\n            if (Global.Platform == eDevicePlatform.Appliance)\n            {\n                AddConsoleCommands();\n            }\n\n            AddPreActivationAction(() => LinkSystemMonitorToAppServer());\n\n            AddPreActivationAction(() => SetupDefaultDeviceMessengers());\n\n            AddPreActivationAction(() => SetupDefaultRoomMessengers());\n\n            AddPreActivationAction(() => AddWebApiPaths());\n\n            AddPreActivationAction(() =>\n            {\n                _roomCombiner = DeviceManager.AllDevices.OfType<IEssentialsRoomCombiner>().FirstOrDefault();\n\n                if (_roomCombiner == null)\n                    return;\n\n                _roomCombiner.RoomCombinationScenarioChanged += OnRoomCombinationScenarioChanged;\n            });\n\n            CrestronEnvironment.ProgramStatusEventHandler +=\n                CrestronEnvironment_ProgramStatusEventHandler;\n\n            ApiOnlineAndAuthorized = new BoolFeedback(\"apiOnlineAndAuthorized\", () =>\n            {\n                if (_wsClient2 == null)\n                    return false;\n\n                return _wsClient2.IsAlive && IsAuthorized;\n            });\n        }\n\n        private void SetupDefaultRoomMessengers()\n        {\n            this.LogVerbose(\"Setting up room messengers\");\n\n            foreach (var room in DeviceManager.AllDevices.OfType<IEssentialsRoom>())\n            {\n                this.LogVerbose(\n                    \"Setting up room messengers for room: {key}\",\n                    room.Key\n                );\n\n                var messenger = new MobileControlEssentialsRoomBridge(room);\n\n                messenger.AddParent(this);\n\n                _roomBridges.Add(messenger);\n\n                AddDefaultDeviceMessenger(messenger);\n\n                this.LogVerbose(\n                    \"Attempting to set up default room messengers for room: {0}\",\n                    room.Key\n                );\n\n                if (room is IRoomEventSchedule)\n                {\n                    this.LogInformation(\"Setting up event schedule messenger for room: {key}\", room.Key);\n\n                    var scheduleMessenger = new RoomEventScheduleMessenger(\n                        $\"{room.Key}-schedule-{Key}\",\n                        string.Format(\"/room/{0}\", room.Key),\n                        room as IRoomEventSchedule\n                    );\n\n                    AddDefaultDeviceMessenger(scheduleMessenger);\n                }\n\n                if (room is ITechPassword)\n                {\n                    this.LogInformation(\"Setting up tech password messenger for room: {key}\", room.Key);\n\n                    var techPasswordMessenger = new ITechPasswordMessenger(\n                        $\"{room.Key}-techPassword-{Key}\",\n                        string.Format(\"/room/{0}\", room.Key),\n                        room as ITechPassword\n                    );\n\n                    AddDefaultDeviceMessenger(techPasswordMessenger);\n                }\n\n                if (room is IShutdownPromptTimer)\n                {\n                    this.LogInformation(\"Setting up shutdown prompt timer messenger for room: {key}\", this, room.Key);\n\n                    var shutdownPromptTimerMessenger = new IShutdownPromptTimerMessenger(\n                        $\"{room.Key}-shutdownPromptTimer-{Key}\",\n                        string.Format(\"/room/{0}\", room.Key),\n                        room as IShutdownPromptTimer\n                    );\n\n                    AddDefaultDeviceMessenger(shutdownPromptTimerMessenger);\n                }\n\n                if (room is ILevelControls levelControls)\n                {\n                    this.LogInformation(\"Setting up level controls messenger for room: {key}\", this, room.Key);\n\n                    var levelControlsMessenger = new ILevelControlsMessenger(\n                        $\"{room.Key}-levelControls-{Key}\",\n                        $\"/room/{room.Key}\",\n                        levelControls\n                    );\n\n                    AddDefaultDeviceMessenger(levelControlsMessenger);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Set up the messengers for each device type\n        /// </summary>\n        private void SetupDefaultDeviceMessengers()\n        {\n            bool messengerAdded = false;\n\n            var allDevices = DeviceManager.AllDevices.Where((d) => !(d is IEssentialsRoom));\n\n            this.LogInformation(\n                \"All Devices that aren't rooms count: {0}\",\n                allDevices?.Count()\n            );\n\n            var count = allDevices.Count();\n\n            foreach (var device in allDevices)\n            {\n                try\n                {\n                    this.LogVerbose(\n                        \"Attempting to set up device messengers for {deviceKey}\",\n                        device.Key\n                    );\n\n                    // StatusMonitorBase which is prop of ICommunicationMonitor is not a PepperDash.Core.Device, but is in the device array\n                    if (device is ICommunicationMonitor)\n                    {\n                        this.LogVerbose(\n                            \"Checking if {deviceKey} implements ICommunicationMonitor\",\n                            device.Key\n                        );\n\n                        if (!(device is ICommunicationMonitor commMonitor))\n                        {\n                            this.LogDebug(\n                                \"{deviceKey} does not implement ICommunicationMonitor. Skipping CommunicationMonitorMessenger\",\n                                device.Key\n                            );\n\n                            this.LogDebug(\"Created all messengers for {deviceKey}. Devices Left: {deviceCount}\", device.Key, --count);\n\n                            continue;\n                        }\n\n                        this.LogDebug(\n                            \"Adding CommunicationMonitorMessenger for {deviceKey}\",\n                            device.Key\n                        );\n\n                        var commMessenger = new ICommunicationMonitorMessenger(\n                            $\"{device.Key}-commMonitor-{Key}\",\n                            string.Format(\"/device/{0}\", device.Key),\n                            commMonitor\n                        );\n\n                        AddDefaultDeviceMessenger(commMessenger);\n\n                        messengerAdded = true;\n                    }\n\n                    // Default to IHasCameraControls if CameraBase and IHasCameraControls\n                    if (device is CameraBase cameraDevice && !(device is IHasCameraControls))\n                    {\n                        this.LogVerbose(\n                            \"Adding CameraBaseMessenger for {deviceKey}\",\n                            device.Key\n                        );\n\n                        var cameraMessenger = new CameraBaseMessenger<CameraBase>(\n                            $\"{device.Key}-cameraBase-{Key}\",\n                            cameraDevice,\n                            $\"/device/{device.Key}\"\n                        );\n\n                        AddDefaultDeviceMessenger(cameraMessenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasCameraControls cameraControlDev)\n                    {\n                        this.LogVerbose(\n                            \"Adding IHasCamerasWithControlMessenger for {deviceKey}\",\n                            device.Key\n                        );\n                        var cameraControlMessenger = new CameraBaseMessenger<IHasCameraControls>(\n                            $\"{device.Key}-hasCamerasWithControls-{Key}\",\n                            cameraControlDev,\n                            $\"/device/{device.Key}\"\n                        );\n                        AddDefaultDeviceMessenger(cameraControlMessenger);\n                        messengerAdded = true;\n                    }\n\n                    if (device is BlueJeansPc)\n                    {\n                        this.LogVerbose(\n                            \"Adding IRunRouteActionMessnger for {deviceKey}\",\n                            device.Key\n                        );\n\n                        var routeMessenger = new RunRouteActionMessenger(\n                            $\"{device.Key}-runRouteAction-{Key}\",\n                            device as BlueJeansPc,\n                            $\"/device/{device.Key}\"\n                        );\n\n                        AddDefaultDeviceMessenger(routeMessenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is ITvPresetsProvider)\n                    {\n                        this.LogVerbose(\n                            \"Trying to cast to ITvPresetsProvider for {deviceKey}\",\n                            device.Key\n                        );\n\n                        var presetsDevice = device as ITvPresetsProvider;\n\n\n                        this.LogVerbose(\n                            \"Adding ITvPresetsProvider for {deviceKey}\",\n                            device.Key\n                        );\n\n                        var presetsMessenger = new DevicePresetsModelMessenger(\n                            $\"{device.Key}-presets-{Key}\",\n                            $\"/device/{device.Key}\",\n                            presetsDevice\n                        );\n\n                        AddDefaultDeviceMessenger(presetsMessenger);\n\n                        messengerAdded = true;\n                    }\n\n\n                    if (device is DisplayBase)\n                    {\n                        this.LogVerbose(\"Adding actions for device: {0}\", device.Key);\n\n                        var dbMessenger = new DisplayBaseMessenger(\n                            $\"{device.Key}-displayBase-{Key}\",\n                            $\"/device/{device.Key}\",\n                            device as DisplayBase\n                        );\n\n                        AddDefaultDeviceMessenger(dbMessenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is TwoWayDisplayBase twoWayDisplay)\n                    {\n                        this.LogVerbose(\n                            \"Adding TwoWayDisplayBase for {deviceKey}\",\n                            device.Key\n                        );\n                        var twoWayDisplayMessenger = new TwoWayDisplayBaseMessenger(\n                            $\"{device.Key}-twoWayDisplay-{Key}\",\n                            string.Format(\"/device/{0}\", device.Key),\n                            twoWayDisplay\n                        );\n                        AddDefaultDeviceMessenger(twoWayDisplayMessenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IBasicVolumeControls)\n                    {\n                        var deviceKey = device.Key;\n                        this.LogVerbose(\n                            \"Adding IBasicVolumeControls for {deviceKey}\",\n                            deviceKey\n                        );\n\n                        var volControlDevice = device as IBasicVolumeControls;\n                        var messenger = new DeviceVolumeMessenger(\n                            $\"{device.Key}-volume-{Key}\",\n                            string.Format(\"/device/{0}\", deviceKey),\n                            volControlDevice\n                        );\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IBasicVideoMuteWithFeedback)\n                    {\n                        var deviceKey = device.Key;\n                        this.LogVerbose(\n                            \"Adding IBasicVideoMuteWithFeedback for {deviceKey}\",\n                            deviceKey\n                        );\n\n                        var videoMuteControlDevice = device as IBasicVideoMuteWithFeedback;\n                        var messenger = new IBasicVideoMuteWithFeedbackMessenger(\n                            $\"{device.Key}-videoMute-{Key}\",\n                            string.Format(\"/device/{0}\", deviceKey),\n                            videoMuteControlDevice\n                        );\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is ILightingScenes || device is LightingBase)\n                    {\n                        var deviceKey = device.Key;\n\n                        this.LogVerbose(\n                            \"Adding LightingBaseMessenger for {deviceKey}\",\n                            deviceKey\n                        );\n\n                        var lightingDevice = device as ILightingScenes;\n                        var messenger = new ILightingScenesMessenger(\n                            $\"{device.Key}-lighting-{Key}\",\n                            lightingDevice,\n                            string.Format(\"/device/{0}\", deviceKey)\n                        );\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IShadesOpenCloseStop)\n                    {\n                        var deviceKey = device.Key;\n                        var shadeDevice = device as IShadesOpenCloseStop;\n\n                        this.LogVerbose(\n                            \"Adding ShadeBaseMessenger for {deviceKey}\",\n                            deviceKey\n                        );\n\n                        var messenger = new IShadesOpenCloseStopMessenger(\n                            $\"{device.Key}-shades-{Key}\",\n                            shadeDevice,\n                            string.Format(\"/device/{0}\", deviceKey)\n                        );\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is VideoCodecBase codec)\n                    {\n                        this.LogVerbose(\n                            \"Adding VideoCodecBaseMessenger for {deviceKey}\", codec.Key);\n\n                        var messenger = new VideoCodecBaseMessenger(\n                            $\"{codec.Key}-videoCodec-{Key}\",\n                            codec,\n                            $\"/device/{codec.Key}\"\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is AudioCodecBase audioCodec)\n                    {\n                        this.LogVerbose(\n                            \"Adding AudioCodecBaseMessenger for {deviceKey}\", audioCodec.Key\n                        );\n\n                        var messenger = new AudioCodecBaseMessenger(\n                            $\"{audioCodec.Key}-audioCodec-{Key}\",\n                            audioCodec,\n                            $\"/device/{audioCodec.Key}\"\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is ISetTopBoxControls stbDevice)\n                    {\n                        this.LogVerbose(\n                            \"Adding ISetTopBoxControlMessenger for {deviceKey}\"\n                        );\n\n                        var messenger = new ISetTopBoxControlsMessenger(\n                            $\"{device.Key}-stb-{Key}\",\n                            $\"/device/{device.Key}\",\n                            stbDevice\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IChannel channelDevice)\n                    {\n                        this.LogVerbose(\n                            \"Adding IChannelMessenger for {deviceKey}\", device.Key\n                        );\n\n                        var messenger = new IChannelMessenger(\n                            $\"{device.Key}-channel-{Key}\",\n                            $\"/device/{device.Key}\",\n                            channelDevice\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IColor colorDevice)\n                    {\n                        this.LogVerbose(\"Adding IColorMessenger for {deviceKey}\", device.Key);\n\n                        var messenger = new IColorMessenger(\n                            $\"{device.Key}-color-{Key}\",\n                            $\"/device/{device.Key}\",\n                            colorDevice\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IDPad dPadDevice)\n                    {\n                        this.LogVerbose(\"Adding IDPadMessenger for {deviceKey}\", device.Key);\n\n                        var messenger = new IDPadMessenger(\n                            $\"{device.Key}-dPad-{Key}\",\n                            $\"/device/{device.Key}\",\n                            dPadDevice\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is INumericKeypad nkDevice)\n                    {\n                        this.LogVerbose(\"Adding INumericKeyapdMessenger for {deviceKey}\", device.Key);\n\n                        var messenger = new INumericKeypadMessenger(\n                            $\"{device.Key}-numericKeypad-{Key}\",\n                            $\"/device/{device.Key}\",\n                            nkDevice\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasPowerControl pcDevice)\n                    {\n                        this.LogVerbose(\"Adding IHasPowerControlMessenger for {deviceKey}\", device.Key);\n\n                        var messenger = new IHasPowerMessenger(\n                            $\"{device.Key}-powerControl-{Key}\",\n                            $\"/device/{device.Key}\",\n                            pcDevice\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasPowerControlWithFeedback powerControl)\n                    {\n                        var deviceKey = device.Key;\n                        this.LogVerbose(\n                            \"Adding IHasPowerControlWithFeedbackMessenger for {deviceKey}\",\n                            deviceKey\n                        );\n\n                        var messenger = new IHasPowerControlWithFeedbackMessenger(\n                            $\"{device.Key}-powerFeedback-{Key}\",\n                            string.Format(\"/device/{0}\", deviceKey),\n                            powerControl\n                        );\n                        AddDefaultDeviceMessenger(messenger);\n                        messengerAdded = true;\n                    }\n\n                    if (device is ITransport transportDevice)\n                    {\n                        this.LogVerbose(\n                            \"Adding ITransportMessenger for {deviceKey}\", device.Key\n                        );\n\n                        var messenger = new ITransportMessenger(\n                            $\"{device.Key}-transport-{Key}\",\n                            $\"/device/{device.Key}\",\n                            transportDevice\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasCurrentSourceInfoChange csiChange)\n                    {\n                        this.LogVerbose(\"Adding IHasCurrentSourceInfoMessenger for {deviceKey}\", device.Key);\n\n                        var messenger = new IHasCurrentSourceInfoMessenger(\n                            $\"{device.Key}-currentSource-{Key}\",\n                            $\"/device/{device.Key}\",\n                            csiChange\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is ICurrentSources currentSources)\n                    {\n                        this.LogVerbose(\"Adding CurrentSourcesMessenger for {deviceKey}\", device.Key);\n\n                        var messenger = new CurrentSourcesMessenger($\"{device.Key}-currentSources-{Key}\", $\"/device/{device.Key}\", currentSources);\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is ISwitchedOutput switchedDevice)\n                    {\n                        this.LogVerbose(\n                            \"Adding ISwitchedOutputMessenger for {deviceKey}\", device.Key\n                        );\n\n                        var messenger = new ISwitchedOutputMessenger(\n                            $\"{device.Key}-switchedOutput-{Key}\",\n                            switchedDevice,\n                            $\"/device/{device.Key}\"\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IDeviceInfoProvider provider)\n                    {\n                        this.LogVerbose(\"Adding IHasDeviceInfoMessenger for {deviceKey}\", device.Key\n                        );\n\n                        var messenger = new DeviceInfoMessenger(\n                            $\"{device.Key}-deviceInfo-{Key}\",\n                            $\"/device/{device.Key}\",\n                            provider\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is ILevelControls levelControls)\n                    {\n                        this.LogVerbose(\n                            \"Adding LevelControlsMessenger for {deviceKey}\", device.Key\n                        );\n\n                        var messenger = new ILevelControlsMessenger(\n                            $\"{device.Key}-levelControls-{Key}\",\n                            $\"/device/{device.Key}\",\n                            levelControls\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasInputs<string> stringInputs)\n                    {\n                        this.LogVerbose(\"Adding InputsMessenger<string> for {deviceKey}\", device.Key);\n\n                        var messenger = new IHasInputsMessenger<string>(\n                            $\"{device.Key}-inputs-{Key}\",\n                            $\"/device/{device.Key}\",\n                            stringInputs\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasInputs<byte> byteInputs)\n                    {\n                        this.LogVerbose(\"Adding InputsMessenger for {deviceKey}\", device.Key);\n\n                        var messenger = new IHasInputsMessenger<byte>(\n                            $\"{device.Key}-inputs-{Key}\",\n                            $\"/device/{device.Key}\",\n                            byteInputs\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasInputs<int> intInputs)\n                    {\n                        this.LogVerbose(\"Adding InputsMessenger for {deviceKey}\", device.Key);\n\n                        var messenger = new IHasInputsMessenger<int>(\n                            $\"{device.Key}-inputs-{Key}\",\n                            $\"/device/{device.Key}\",\n                            intInputs\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IMatrixRouting matrix)\n                    {\n                        this.LogVerbose(\n                            \"Adding IMatrixRoutingMessenger for {deviceKey}\",\n                            device.Key\n                        );\n\n                        var messenger = new IMatrixRoutingMessenger(\n                            $\"{device.Key}-matrixRouting\",\n                            $\"/device/{device.Key}\",\n                            matrix\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is ITemperatureSensor tempSensor)\n                    {\n                        this.LogVerbose(\n                            \"Adding ITemperatureSensor for {deviceKey}\",\n                            device.Key\n                        );\n\n                        var messenger = new ITemperatureSensorMessenger(\n                            $\"{device.Key}-tempSensor\",\n                            tempSensor,\n                            $\"/device/{device.Key}\"\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHumiditySensor humSensor)\n                    {\n                        this.LogVerbose(\n                            \"Adding IHumiditySensor for {deviceKey}\",\n                            device.Key\n                        );\n\n                        var messenger = new IHumiditySensorMessenger(\n                            $\"{device.Key}-humiditySensor\",\n                            humSensor,\n                            $\"/device/{device.Key}\"\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IEssentialsRoomCombiner roomCombiner)\n                    {\n                        this.LogVerbose(\n                            \"Adding IEssentialsRoomCombinerMessenger for {deviceKey}\", device.Key\n                        );\n\n                        var messenger = new IEssentialsRoomCombinerMessenger(\n                            $\"{device.Key}-roomCombiner-{Key}\",\n                            $\"/device/{device.Key}\",\n                            roomCombiner\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IProjectorScreenLiftControl screenLiftControl)\n                    {\n                        this.LogVerbose(\"Adding IProjectorScreenLiftControlMessenger for {deviceKey}\", device.Key\n                        );\n\n                        var messenger = new IProjectorScreenLiftControlMessenger(\n                            $\"{device.Key}-screenLiftControl-{Key}\",\n                            $\"/device/{device.Key}\",\n                            screenLiftControl\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IDspPresets dspPresets)\n                    {\n                        this.LogVerbose(\"Adding IDspPresetsMessenger for {deviceKey}\", device.Key\n                        );\n\n                        var messenger = new IDspPresetsMessenger(\n                            $\"{device.Key}-dspPresets-{Key}\",\n                            $\"/device/{device.Key}\",\n                            dspPresets\n                        );\n\n                        AddDefaultDeviceMessenger(messenger);\n\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasCameras cameras)\n                    {\n                        this.LogVerbose(\"Adding IHasCamerasMessenger for {deviceKey}\", device.Key\n                        );\n                        var messenger = new IHasCamerasMessenger(\n                            $\"{device.Key}-cameras-{Key}\",\n                            $\"/device/{device.Key}\",\n                            cameras\n                        );\n                        AddDefaultDeviceMessenger(messenger);\n                        messengerAdded = true;\n                    }\n\n                    if (device is IHasCamerasWithControls cameras2)\n                    {\n                        this.LogVerbose(\"Adding IHasCamerasWithControlsMessenger for {deviceKey}\", device.Key\n                        );\n                        var messenger = new IHasCamerasWithControlMessenger(\n                            $\"{device.Key}-cameras-{Key}\",\n                            $\"/device/{device.Key}\",\n                            cameras2\n                        );\n                        AddDefaultDeviceMessenger(messenger);\n                        messengerAdded = true;\n                    }\n\n                    this.LogVerbose(\"Trying to cast to generic device for device: {key}\", device.Key);\n\n                    if (device is EssentialsDevice)\n                    {\n                        if (!(device is EssentialsDevice genericDevice) || messengerAdded)\n                        {\n                            this.LogVerbose(\n                                \"Skipping GenericMessenger for {deviceKey}. Messenger(s) Added: {messengersAdded}.\",\n                                device.Key,\n                                messengerAdded\n                            );\n                            this.LogDebug(\n                                \"AllDevices Completed a device. Devices Left: {count}\",\n                                --count\n                            );\n                            continue;\n                        }\n\n                        this.LogDebug(\n                            \"Adding GenericMessenger for {deviceKey}\",\n                            this,\n                            genericDevice?.Key\n                        );\n\n                        AddDefaultDeviceMessenger(\n                            new GenericMessenger(\n                                genericDevice.Key + \"-\" + Key + \"-generic\",\n                                genericDevice,\n                                string.Format(\"/device/{0}\", genericDevice.Key)\n                            )\n                        );\n                    }\n                    else\n                    {\n                        this.LogVerbose(\n                            \"Not Essentials Device. Skipping GenericMessenger for {deviceKey}\",\n                            device.Key\n                        );\n                    }\n                    this.LogDebug(\n                        \"AllDevices Completed a device. Devices Left: {count}\",\n                        --count\n                    );\n                }\n\n                catch (Exception ex)\n                {\n                    this.LogException(ex, \"Exception setting up default device messengers\");\n                }\n            }\n        }\n\n        private void AddWebApiPaths()\n        {\n            var apiServer = DeviceManager\n                .AllDevices.OfType<EssentialsWebApi>()\n                .FirstOrDefault(d => d.Key == \"essentialsWebApi\");\n\n            if (apiServer == null)\n            {\n                this.LogWarning(\"No API Server available\");\n                return;\n            }\n\n            // TODO: Add routes for the rest of the MC console commands\n            var routes = new List<HttpCwsRoute>\n            {\n                new HttpCwsRoute($\"device/{Key}/authorize\")\n                {\n                    Name = \"MobileControlAuthorize\",\n                    RouteHandler = new MobileAuthRequestHandler(this)\n                },\n                new HttpCwsRoute($\"device/{Key}/info\")\n                {\n                    Name = \"MobileControlInformation\",\n                    RouteHandler = new MobileInfoHandler(this)\n                },\n                new HttpCwsRoute($\"device/{Key}/actionPaths\")\n                {\n                    Name = \"MobileControlActionPaths\",\n                    RouteHandler = new ActionPathsHandler(this)\n                }\n            };\n\n            apiServer.AddRoute(routes);\n        }\n\n        private void AddConsoleCommands()\n        {\n            CrestronConsole.AddNewConsoleCommand(\n                AuthorizeSystem,\n                \"mobileauth\",\n                \"Authorizes system to talk to Mobile Control server\",\n                ConsoleAccessLevelEnum.AccessOperator\n            );\n            CrestronConsole.AddNewConsoleCommand(\n                s => ShowInfo(),\n                \"mobileinfo\",\n                \"Shows information for current mobile control session\",\n                ConsoleAccessLevelEnum.AccessOperator\n            );\n            CrestronConsole.AddNewConsoleCommand(\n                s =>\n                {\n                    s = s.Trim();\n                    if (!string.IsNullOrEmpty(s))\n                    {\n                        _httpDebugEnabled = (s.Trim() != \"0\");\n                    }\n                    CrestronConsole.ConsoleCommandResponse(\n                        \"HTTP Debug {0}\",\n                        _httpDebugEnabled ? \"Enabled\" : \"Disabled\"\n                    );\n                },\n                \"mobilehttpdebug\",\n                \"1 enables more verbose HTTP response debugging\",\n                ConsoleAccessLevelEnum.AccessOperator\n            );\n            CrestronConsole.AddNewConsoleCommand(\n                TestHttpRequest,\n                \"mobilehttprequest\",\n                \"Tests an HTTP get to URL given\",\n                ConsoleAccessLevelEnum.AccessOperator\n            );\n\n            CrestronConsole.AddNewConsoleCommand(\n                PrintActionDictionaryPaths,\n                \"mobileshowactionpaths\",\n                \"Prints the paths in the Action Dictionary\",\n                ConsoleAccessLevelEnum.AccessOperator\n            );\n            CrestronConsole.AddNewConsoleCommand(\n                s =>\n                {\n                    _disableReconnect = false;\n\n                    CrestronConsole.ConsoleCommandResponse(\n                        $\"Connecting to MC API server\"\n                    );\n\n                    ConnectWebsocketClient();\n                },\n                \"mobileconnect\",\n                \"Forces connect of websocket\",\n                ConsoleAccessLevelEnum.AccessOperator\n            );\n\n            CrestronConsole.AddNewConsoleCommand(\n                s =>\n                {\n                    _disableReconnect = true;\n\n                    CleanUpWebsocketClient();\n\n                    CrestronConsole.ConsoleCommandResponse(\n                        $\"Disonnected from MC API server\"\n                    );\n                },\n                \"mobiledisco\",\n                \"Disconnects websocket\",\n                ConsoleAccessLevelEnum.AccessOperator\n            );\n\n            CrestronConsole.AddNewConsoleCommand(\n                ParseStreamRx,\n                \"mobilesimulateaction\",\n                \"Simulates a message from the server\",\n                ConsoleAccessLevelEnum.AccessOperator\n            );\n\n            CrestronConsole.AddNewConsoleCommand(\n                SetWebsocketDebugLevel,\n                \"mobilewsdebug\",\n                \"Set Websocket debug level\",\n                ConsoleAccessLevelEnum.AccessProgrammer\n            );\n        }\n\n        /// <summary>\n        /// Gets or sets the Config\n        /// </summary>\n        public MobileControlConfig Config { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the Host\n        /// </summary>\n        public string Host { get; private set; }\n\n        /// <summary>\n        /// Gets the configured Client App URL\n        /// </summary>\n        public string ClientAppUrl => Config.ClientAppUrl;\n\n        private void OnRoomCombinationScenarioChanged(\n            object sender,\n            EventArgs eventArgs\n        )\n        {\n            SendMessageObject(new MobileControlMessage { Type = \"/system/roomCombinationChanged\" });\n        }\n\n        /// <summary>\n        /// Checks if a device messenger exists for the given key.\n        /// </summary>\n        public bool CheckForDeviceMessenger(string key)\n        {\n            return _messengers.ContainsKey(key);\n        }\n\n        /// <summary>\n        /// Add the provided messenger to the messengers collection\n        /// </summary>\n        public void AddDeviceMessenger(IMobileControlMessenger messenger)\n        {\n            if (_messengers.ContainsKey(messenger.Key))\n            {\n                this.LogWarning(\"Messenger with key {messengerKey} already added\", messenger.Key);\n                return;\n            }\n\n            if (messenger is IDelayedConfiguration simplMessenger)\n            {\n                simplMessenger.ConfigurationIsReady += Bridge_ConfigurationIsReady;\n            }\n\n            if (messenger is MobileControlBridgeBase roomBridge)\n            {\n                _roomBridges.Add(roomBridge);\n            }\n\n            this.LogVerbose(\n                \"Adding messenger with key {messengerKey} for path {messengerPath}\",\n                messenger.Key,\n                messenger.MessagePath\n            );\n\n            _messengers.Add(messenger.Key, messenger);\n\n            if (_initialized)\n            {\n                RegisterMessengerWithServer(messenger);\n            }\n        }\n\n        private void AddDefaultDeviceMessenger(IMobileControlMessenger messenger)\n        {\n            if (_defaultMessengers.ContainsKey(messenger.Key))\n            {\n                this.LogWarning(\n                    \"Default messenger with key {messengerKey} already added\",\n                    messenger.Key\n                );\n                return;\n            }\n\n            if (messenger is IDelayedConfiguration simplMessenger)\n            {\n                simplMessenger.ConfigurationIsReady += Bridge_ConfigurationIsReady;\n            }\n            this.LogVerbose(\n                \"Adding default messenger with key {messengerKey} for path {messengerPath}\",\n                messenger.Key,\n                messenger.MessagePath\n            );\n\n            _defaultMessengers.Add(messenger.Key, messenger);\n\n            if (_initialized)\n            {\n                RegisterMessengerWithServer(messenger);\n            }\n        }\n\n        private void RegisterMessengerWithServer(IMobileControlMessenger messenger)\n        {\n            this.LogVerbose(\n                \"Registering messenger with key {messengerKey} for path {messengerPath}\",\n                messenger.Key,\n                messenger.MessagePath\n            );\n\n            if (messenger is IMobileControlMessengerWithSubscriptions subMessenger)\n            {\n                subMessenger.RegisterWithAppServer(this, Config.EnableMessengerSubscriptions);\n                return;\n            }\n\n            messenger.RegisterWithAppServer(this);\n        }\n\n        /// <inheritdoc />\n        public override void Initialize()\n        {\n            if (!Config.EnableMessengerSubscriptions)\n            {\n                this.LogWarning(\"Messenger subscriptions disabled. add \\\"enableMessengerSubscriptions\\\": true to config for {key} to enable.\", Key);\n            }\n\n            foreach (var messenger in _messengers)\n            {\n                try\n                {\n                    RegisterMessengerWithServer(messenger.Value);\n                }\n                catch (Exception ex)\n                {\n                    this.LogException(ex, \"Exception registering custom messenger {messengerKey}\", messenger.Key);\n                    continue;\n                }\n            }\n\n            foreach (var messenger in _defaultMessengers)\n            {\n                try\n                {\n                    RegisterMessengerWithServer(messenger.Value);\n                }\n                catch (Exception ex)\n                {\n                    this.LogException(ex, \"Exception registering default messenger {messengerKey}\", messenger.Key);\n                    continue;\n                }\n            }\n\n            var simplMessengers = _messengers.OfType<IDelayedConfiguration>().ToList();\n\n            if (simplMessengers.Count > 0)\n            {\n                return;\n            }\n\n            _initialized = true;\n\n            RegisterSystemToServer();\n        }\n\n        #region IMobileControl Members\n\n        /// <summary>\n        /// Gets the App Server instance\n        /// </summary>\n        public static IMobileControl GetAppServer()\n        {\n            try\n            {\n                var appServer =\n                    DeviceManager.GetDevices().SingleOrDefault(s => s is IMobileControl)\n                    as MobileControlSystemController;\n                return appServer;\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(e, \"Unable to find MobileControlSystemController in Devices\");\n                return null;\n            }\n        }\n\n        private bool CreateWebsocket()\n        {\n            _wsClient2?.Close();\n            _wsClient2 = null;\n\n            if (string.IsNullOrEmpty(SystemUuid))\n            {\n                this.LogError(\n                    \"System UUID not defined. Unable to connect to Mobile Control\"\n                );\n                return false;\n            }\n\n            var wsHost = Host.Replace(\"http\", \"ws\");\n            var url = string.Format(\"{0}/system/join/{1}\", wsHost, SystemUuid);\n\n            _wsClient2 = new WebSocket(url)\n            {\n                Log =\n                {\n                    Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this)\n                }\n            };\n\n            // setting to trace to let level be controlled by appdebug\n            _wsClient2.Log.Level = LogLevel.Trace;\n\n            _wsClient2.SslConfiguration.EnabledSslProtocols =\n                System.Security.Authentication.SslProtocols.Tls11\n                | System.Security.Authentication.SslProtocols.Tls12;\n\n            _wsClient2.OnMessage += HandleMessage;\n            _wsClient2.OnOpen += HandleOpen;\n            _wsClient2.OnError += HandleError;\n            _wsClient2.OnClose += HandleClose;\n\n            return true;\n        }\n\n        /// <summary>\n        /// Link the System Monitor to this App server\n        /// </summary>\n        public void LinkSystemMonitorToAppServer()\n        {\n            if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Appliance)\n            {\n                this.LogWarning(\n                    \"System Monitor does not exist for this platform. Skipping...\"\n                );\n                return;\n            }\n\n            if (!(DeviceManager.GetDeviceForKey(\"systemMonitor\") is SystemMonitorController sysMon))\n            {\n                return;\n            }\n\n            var key = sysMon.Key + \"-\" + Key;\n            var messenger = new SystemMonitorMessenger(key, sysMon, \"/device/systemMonitor\");\n\n            AddDeviceMessenger(messenger);\n        }\n\n        #endregion\n\n        private void SetWebsocketDebugLevel(string cmdparameters)\n        {\n            if (string.IsNullOrEmpty(cmdparameters))\n            {\n                this.LogInformation(\"Current Websocket debug level: {webSocketDebugLevel}\", _wsLogLevel);\n                return;\n            }\n\n            if (cmdparameters.ToLower().Contains(\"help\") || cmdparameters.ToLower().Contains(\"?\"))\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    $\"valid options are:\\r\\n{LogLevel.Trace}\\r\\n{LogLevel.Debug}\\r\\n{LogLevel.Info}\\r\\n{LogLevel.Warn}\\r\\n{LogLevel.Error}\\r\\n{LogLevel.Fatal}\\r\\n\"\n                );\n            }\n\n            try\n            {\n                var debugLevel = (LogLevel)Enum.Parse(typeof(LogLevel), cmdparameters, true);\n\n                _wsLogLevel = debugLevel;\n\n                if (_wsClient2 != null)\n                {\n                    _wsClient2.Log.Level = _wsLogLevel;\n                }\n\n                _directServer?.SetWebsocketLogLevel(_wsLogLevel);\n\n                CrestronConsole.ConsoleCommandResponse($\"Websocket log level set to {debugLevel}\");\n            }\n            catch\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    $\"{cmdparameters} is not a valid debug level. Valid options are:\\r\\n{LogLevel.Trace}\\r\\n{LogLevel.Debug}\\r\\n{LogLevel.Info}\\r\\n{LogLevel.Warn}\\r\\n{LogLevel.Error}\\r\\n{LogLevel.Fatal}\\r\\n\"\n                );\n\n            }\n        }\n\n        private void CrestronEnvironment_ProgramStatusEventHandler(\n            eProgramStatusEventType programEventType\n        )\n        {\n            if (\n                programEventType != eProgramStatusEventType.Stopping\n                || _wsClient2 == null\n                || !_wsClient2.IsAlive\n            )\n            {\n                return;\n            }\n\n            _disableReconnect = true;\n\n            StopServerReconnectTimer();\n            CleanUpWebsocketClient();\n        }\n\n        /// <summary>\n        /// PrintActionDictionaryPaths method\n        /// </summary>\n        public void PrintActionDictionaryPaths(object o)\n        {\n            CrestronConsole.ConsoleCommandResponse(\"ActionDictionary Contents:\\r\\n\");\n\n            foreach (var (messengerKey, actionPath) in GetActionDictionaryPaths())\n            {\n                CrestronConsole.ConsoleCommandResponse($\"<{messengerKey}> {actionPath}\\r\\n\");\n            }\n        }\n\n        /// <summary>\n        /// Get action paths for the current actions\n        /// </summary>\n        public List<(string, string)> GetActionDictionaryPaths()\n        {\n            var paths = new List<(string, string)>();\n\n            foreach (var item in _actionDictionary)\n            {\n                var messengers = item.Value.Select(a => a.Messenger).Cast<MessengerBase>();\n                foreach (var messenger in messengers)\n                {\n                    foreach (var actionPath in messenger.GetActionPaths())\n                    {\n                        paths.Add((messenger.Key, $\"{item.Key}{actionPath}\"));\n                    }\n                }\n            }\n\n            return paths;\n        }\n\n        /// <summary>\n        /// Adds an action to the dictionary\n        /// </summary>\n        /// <param name=\"messenger\">The messenger for the API command</param>\n        /// <param name=\"action\">The action to be triggered by the commmand</param>\n        public void AddAction<T>(T messenger, Action<string, string, JToken> action)\n            where T : IMobileControlMessenger\n        {\n            if (\n                _actionDictionary.TryGetValue(\n                    messenger.MessagePath,\n                    out List<IMobileControlAction> actionList\n                )\n            )\n            {\n                if (\n                    actionList.Any(a =>\n                        a.Messenger.GetType() == messenger.GetType()\n                        && a.Messenger.DeviceKey == messenger.DeviceKey\n                    )\n                )\n                {\n                    this.LogWarning(\"Messenger of type {messengerType} already exists. Skipping actions for {messengerKey}\", messenger.GetType().Name, messenger.Key);\n                    return;\n                }\n\n                actionList.Add(new MobileControlAction(messenger, action));\n                return;\n            }\n\n            actionList = new List<IMobileControlAction>\n            {\n                new MobileControlAction(messenger, action)\n            };\n\n            _actionDictionary.Add(messenger.MessagePath, actionList);\n        }\n\n        /// <summary>\n        /// Removes an action from the dictionary\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <summary>\n        /// RemoveAction method\n        /// </summary>\n        public void RemoveAction(string key)\n        {\n            if (_actionDictionary.ContainsKey(key))\n            {\n                _actionDictionary.Remove(key);\n            }\n        }\n\n        /// <summary>\n        /// Get the room bridge with the provided key\n        /// </summary>\n        /// <param name=\"key\">The key of the room bridge</param>\n        public MobileControlBridgeBase GetRoomBridge(string key)\n        {\n            return _roomBridges.FirstOrDefault((r) => r.RoomKey.Equals(key));\n        }\n\n        /// <summary>\n        /// Get the room messenger with the provided key\n        /// </summary>\n        /// <param name=\"key\">The Key of the rooom messenger</param>\n        public IMobileControlRoomMessenger GetRoomMessenger(string key)\n        {\n            return _roomBridges.FirstOrDefault((r) => r.RoomKey.Equals(key));\n        }\n\n        private void Bridge_ConfigurationIsReady(object sender, EventArgs e)\n        {\n            this.LogDebug(\"Bridge ready.  Registering\");\n\n            // send the configuration object to the server\n\n            if (_wsClient2 == null)\n            {\n                RegisterSystemToServer();\n            }\n            else if (!_wsClient2.IsAlive)\n            {\n                ConnectWebsocketClient();\n            }\n            else\n            {\n                SendInitialMessage();\n            }\n        }\n\n        private void ReconnectToServerTimerCallback(object o)\n        {\n            this.LogDebug(\"Attempting to reconnect to server...\");\n\n            ConnectWebsocketClient();\n        }\n\n        private void AuthorizeSystem(string code)\n        {\n            if (\n                string.IsNullOrEmpty(SystemUuid)\n                || SystemUuid.Equals(\"missing url\", StringComparison.OrdinalIgnoreCase)\n            )\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"System does not have a UUID. Please ensure proper configuration is loaded and restart.\"\n                );\n                return;\n            }\n            if (string.IsNullOrEmpty(code))\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"Please enter a grant code to authorize a system\"\n                );\n                return;\n            }\n            if (string.IsNullOrEmpty(Config.ServerUrl))\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"Mobile control API address is not set. Check portal configuration\"\n                );\n                return;\n            }\n\n            var authTask = ApiService.SendAuthorizationRequest(Host, code, SystemUuid);\n\n            authTask.ContinueWith(t =>\n            {\n                var response = t.Result;\n\n                if (response.Authorized)\n                {\n                    this.LogDebug(\"System authorized, sending config.\");\n                    RegisterSystemToServer();\n                    return;\n                }\n\n                this.LogInformation(response.Reason);\n            });\n        }\n\n        private void ShowInfo()\n        {\n            var url = Config != null ? Host : \"No config\";\n            string name;\n            string code;\n            if (_roomBridges != null && _roomBridges.Count > 0)\n            {\n                name = _roomBridges[0].RoomName;\n                code = _roomBridges[0].UserCode;\n            }\n            else\n            {\n                name = \"No config\";\n                code = \"Not available\";\n            }\n            var conn = _wsClient2 == null ? \"No client\" : (_wsClient2.IsAlive ? \"Yes\" : \"No\");\n\n            var secSinceLastAck = DateTime.Now - _lastAckMessage;\n\n            if (Config.EnableApiServer)\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"Mobile Control Edge Server API Information:\\r\\n\\r\\n\" +\n                    \"\\tServer address: {0}\\r\\n\" +\n                    \"\\tSystem Name: {1}\\r\\n\" +\n                    \"\\tSystem URL: {2}\\r\\n\" +\n                    \"\\tSystem UUID: {3}\\r\\n\" +\n                    \"\\tSystem User code: {4}\\r\\n\" +\n                    \"\\tConnected?: {5}\\r\\n\" +\n                    \"\\tSeconds Since Last Ack: {6}\\r\\n\",\n                    url,\n                    name,\n                    ConfigReader.ConfigObject.SystemUrl,\n                    SystemUuid,\n                    code,\n                    conn,\n                    secSinceLastAck.Seconds\n                );\n            }\n            else\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"\\r\\nMobile Control Edge Server API Information:\\r\\n\" +\n                    \"    Not Enabled in Config.\\r\\n\"\n                );\n            }\n\n            if (\n                Config.DirectServer != null\n                && Config.DirectServer.EnableDirectServer\n                && _directServer != null\n            )\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"\\r\\nMobile Control Direct Server Information:\\r\\n\" +\n                    \"    User App URL: {0}\\r\\n\" +\n                    \"    Server port: {1}\\r\\n\",\n                    string.Format(\"{0}[insert_client_token]\", _directServer.UserAppUrlPrefix),\n                    _directServer.Port\n                );\n\n                CrestronConsole.ConsoleCommandResponse(\n                    \"\\r\\n    UI Client Info:\\r\\n\" +\n                    \"    Tokens Defined: {0}\\r\\n\" +\n                    \"    Clients Connected: {1}\\r\\n\",\n                    _directServer.UiClientContexts.Count,\n                    _directServer.ConnectedUiClientsCount\n                );\n\n                var clientNo = 1;\n                foreach (var clientContext in _directServer.UiClientContexts)\n                {\n                    var clients = _directServer.UiClients.Values.Where(c => c.TokenKey == clientContext.Key);\n\n                    CrestronConsole.ConsoleCommandResponse(\n                        $\"\\r\\nClient {clientNo}:\\r\\n\" +\n                        $\"  Room Key: {clientContext.Value.Token.RoomKey}\\r\\n\" +\n                        $\"  Touchpanel Key: {clientContext.Value.Token.TouchpanelKey}\\r\\n\" +\n                        $\"  Token: {clientContext.Key}\\r\\n\" +\n                        $\"  Client URL: {_directServer.UserAppUrlPrefix}{clientContext.Key}\\r\\n\" +\n                        $\"  Clients:\\r\\n\"\n                    );\n\n                    if (!clients.Any())\n                    {\n                        CrestronConsole.ConsoleCommandResponse(\"    No clients connected\");\n                    }\n                    foreach (var client in clients)\n                    {\n                        CrestronConsole.ConsoleCommandResponse(\n                            $\"    ID: {client.Id}\\r\\n\" +\n                            $\"    Connected: {client.Context.WebSocket.IsAlive}\\r\\n\" +\n                            $\"    Duration: {(client.Context.WebSocket.IsAlive ? client.ConnectedDuration.TotalSeconds.ToString() : \"Not Connected\")}\\r\\n\"\n                        );\n                    }\n\n                    clientNo++;\n                }\n            }\n            else\n            {\n                CrestronConsole.ConsoleCommandResponse(\n                    \"\\r\\nMobile Control Direct Server Information:\\r\\n\" +\n                    \"    Not Enabled in Config.\\r\\n\"\n                );\n            }\n        }\n\n        /// <summary>\n        /// Register this system to the Mobile Control Edge Server\n        /// </summary>\n        public void RegisterSystemToServer()\n        {\n\n            if (!Config.EnableApiServer)\n            {\n                this.LogInformation(\n                    \"ApiServer disabled via config.  Cancelling attempt to register to server.\"\n                );\n                return;\n            }\n\n            var result = CreateWebsocket();\n\n            if (!result)\n            {\n                this.LogFatal(\"Unable to create websocket.\");\n                return;\n            }\n\n            ConnectWebsocketClient();\n        }\n\n        private void ConnectWebsocketClient()\n        {\n            try\n            {\n                _wsCriticalSection.Enter();\n\n                // set to 99999 to let things work on 4-Series\n                if (\n                    (CrestronEnvironment.ProgramCompatibility & eCrestronSeries.Series4)\n                    == eCrestronSeries.Series4\n                )\n                {\n                    _wsClient2.Log.Level = (LogLevel)99999;\n                }\n                else if (\n                    (CrestronEnvironment.ProgramCompatibility & eCrestronSeries.Series3)\n                    == eCrestronSeries.Series3\n                )\n                {\n                    _wsClient2.Log.Level = _wsLogLevel;\n                }\n\n                //This version of the websocket client is TLS1.2 ONLY\n\n                //Fires OnMessage event when PING is received.\n                _wsClient2.EmitOnPing = true;\n\n                this.LogDebug(\n                    \"Connecting mobile control client to {mobileControlUrl}\",\n                    _wsClient2.Url\n                );\n\n                TryConnect();\n            }\n            finally\n            {\n                _wsCriticalSection.Leave();\n            }\n        }\n\n        private void TryConnect()\n        {\n            try\n            {\n                IsAuthorized = false;\n                _wsClient2.Connect();\n            }\n            catch (InvalidOperationException)\n            {\n                this.LogError(\n                    \"Maximum retries exceeded. Restarting websocket\"\n                );\n                HandleConnectFailure();\n            }\n            catch (IOException ex)\n            {\n                this.LogException(ex, \"IO Exception on connect\");\n                HandleConnectFailure();\n            }\n            catch (Exception ex)\n            {\n                this.LogException(\n                    ex,\n                    \"Error on Websocket Connect\"\n                );\n                HandleConnectFailure();\n            }\n        }\n\n        private void HandleConnectFailure()\n        {\n            _wsClient2 = null;\n\n            var wsHost = Host.Replace(\"http\", \"ws\");\n            var url = string.Format(\"{0}/system/join/{1}\", wsHost, SystemUuid);\n            _wsClient2 = new WebSocket(url)\n            {\n                Log =\n                {\n                    Output = (data, s) =>\n                        this.LogDebug(\n                            \"Message from websocket: {message}\",\n                            data\n                        )\n                }\n            };\n\n            _wsClient2.OnMessage -= HandleMessage;\n            _wsClient2.OnOpen -= HandleOpen;\n            _wsClient2.OnError -= HandleError;\n            _wsClient2.OnClose -= HandleClose;\n\n            _wsClient2.OnMessage += HandleMessage;\n            _wsClient2.OnOpen += HandleOpen;\n            _wsClient2.OnError += HandleError;\n            _wsClient2.OnClose += HandleClose;\n\n            StartServerReconnectTimer();\n        }\n\n        private void HandleOpen(object sender, EventArgs e)\n        {\n            StopServerReconnectTimer();\n            StartPingTimer();\n            this.LogInformation(\"Mobile Control API connected\");\n            SendMessageObject(new MobileControlMessage { Type = \"hello\" });\n        }\n\n        private void HandleMessage(object sender, MessageEventArgs e)\n        {\n            if (e.IsPing)\n            {\n                _lastAckMessage = DateTime.Now;\n                IsAuthorized = true;\n                ResetPingTimer();\n                return;\n            }\n\n            if (e.IsText && e.Data.Length > 0)\n            {\n                _receiveQueue.Enqueue(new ProcessStringMessage(e.Data, ParseStreamRx));\n            }\n        }\n\n        private void HandleError(object sender, ErrorEventArgs e)\n        {\n            this.LogError(\"Websocket error {0}\", e.Message);\n\n            IsAuthorized = false;\n            StartServerReconnectTimer();\n        }\n\n        private void HandleClose(object sender, CloseEventArgs e)\n        {\n            this.LogDebug(\n                \"Websocket close {code} {reason}, clean={wasClean}\",\n                e.Code,\n                e.Reason,\n                e.WasClean\n            );\n            IsAuthorized = false;\n            StopPingTimer();\n\n            // Start the reconnect timer only if disableReconnect is false and the code isn't 4200. 4200 indicates system is not authorized;\n            if (_disableReconnect || e.Code == 4200)\n            {\n                return;\n            }\n\n            StartServerReconnectTimer();\n        }\n\n        private void SendInitialMessage()\n        {\n            this.LogInformation(\"Sending initial join message\");\n\n            var touchPanels = DeviceManager\n                .AllDevices.OfType<IMobileControlTouchpanelController>()\n                .Where(tp => !tp.UseDirectServer)\n                .Select(\n                    (tp) =>\n                    {\n                        return new { touchPanelKey = tp.Key, roomKey = tp.DefaultRoomKey };\n                    }\n                );\n\n            var msg = new MobileControlMessage\n            {\n                Type = \"join\",\n                Content = JToken.FromObject(\n                    new { config = GetConfigWithPluginVersion(), touchPanels }\n                )\n            };\n\n            SendMessageObject(msg);\n        }\n\n        /// <summary>\n        /// Get the Essentials configuration with version data\n        /// </summary>\n        public MobileControlEssentialsConfig GetConfigWithPluginVersion()\n        {\n            // Populate the application name and version number\n            var confObject = new MobileControlEssentialsConfig(ConfigReader.ConfigObject);\n\n            confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;\n\n            var essentialsVersion = Global.AssemblyVersion;\n            confObject.Info.RuntimeInfo.AssemblyVersion = essentialsVersion;\n\n\n            //            // Set for local testing\n            //            confObject.RuntimeInfo.PluginVersion = \"4.0.0-localBuild\";\n\n            // Populate the plugin version\n            var pluginVersion = Assembly\n                .GetExecutingAssembly()\n                .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);\n\n\n            if (pluginVersion[0] is AssemblyInformationalVersionAttribute fullVersionAtt)\n            {\n                var pluginInformationalVersion = fullVersionAtt.InformationalVersion;\n\n                confObject.RuntimeInfo.PluginVersion = pluginInformationalVersion;\n                confObject.RuntimeInfo.EssentialsVersion = Global.AssemblyVersion;\n                confObject.RuntimeInfo.PepperDashCoreVersion = PluginLoader.PepperDashCoreAssembly.Version;\n                confObject.RuntimeInfo.EssentialsPlugins = PluginLoader.EssentialsPluginAssemblies;\n            }\n            return confObject;\n        }\n\n        /// <summary>\n        /// Set the Client URL for a given room\n        /// </summary>\n        /// <param name=\"path\">new App URL</param>\n        /// <param name=\"roomKey\">room key. Default is null</param>\n        /// <remarks>\n        /// If roomKey is null, the URL will be set for the entire system.\n        /// </remarks>\n        public void SetClientUrl(string path, string roomKey = null)\n        {\n            var message = new MobileControlMessage\n            {\n                Type = string.IsNullOrEmpty(roomKey) ? $\"/event/system/setUrl\" : $\"/event/room/{roomKey}/setUrl\",\n                Content = JToken.FromObject(new MobileControlSimpleContent<string> { Value = path })\n            };\n\n            SendMessageObject(message);\n        }\n\n        /// <summary>\n        /// Sends any object type to server\n        /// </summary>\n        /// <param name=\"o\"></param>\n        public void SendMessageObject(IMobileControlMessage o)\n        {\n\n            if (Config.EnableApiServer)\n            {\n\n                _transmitToServerQueue.Enqueue(new TransmitMessage(o, _wsClient2));\n\n            }\n\n            if (\n                Config.DirectServer != null\n                && Config.DirectServer.EnableDirectServer\n                && _directServer != null\n            )\n            {\n                _transmitToClientsQueue.Enqueue(new MessageToClients(o, _directServer));\n            }\n\n        }\n\n\n        /// <summary>\n        /// Send a message to a client using the Direct Server\n        /// </summary>\n        /// <param name=\"o\">object to send</param>\n        public void SendMessageObjectToDirectClient(object o)\n        {\n            if (\n                Config.DirectServer != null\n                && Config.DirectServer.EnableDirectServer\n                && _directServer != null\n            )\n            {\n                _transmitToClientsQueue.Enqueue(new MessageToClients(o, _directServer));\n            }\n        }\n\n        private void CleanUpWebsocketClient()\n        {\n            if (_wsClient2 == null)\n            {\n                return;\n            }\n\n            this.LogDebug(\"Disconnecting websocket\");\n\n            _wsClient2.Close();\n        }\n\n        private void ResetPingTimer()\n        {\n            // This tells us we're online with the API and getting pings\n            _pingTimer.Reset(PingInterval);\n        }\n\n        private void StartPingTimer()\n        {\n            StopPingTimer();\n            _pingTimer = new CTimer(PingTimerCallback, null, PingInterval);\n        }\n\n        private void StopPingTimer()\n        {\n            if (_pingTimer == null)\n            {\n                return;\n            }\n\n            _pingTimer.Stop();\n            _pingTimer.Dispose();\n            _pingTimer = null;\n        }\n\n        private void PingTimerCallback(object o)\n        {\n            this.LogDebug(\n\n                \"Ping timer expired. Closing websocket\"\n            );\n\n            try\n            {\n                _wsClient2.Close();\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex,\n                    \"Exception closing websocket\"\n                );\n\n                HandleConnectFailure();\n            }\n        }\n\n        private void StartServerReconnectTimer()\n        {\n            StopServerReconnectTimer();\n            _serverReconnectTimer = new CTimer(\n                ReconnectToServerTimerCallback,\n                ServerReconnectInterval\n            );\n            this.LogDebug(\"Reconnect Timer Started.\");\n        }\n\n        private void StopServerReconnectTimer()\n        {\n            if (_serverReconnectTimer == null)\n            {\n                return;\n            }\n            _serverReconnectTimer.Stop();\n            _serverReconnectTimer = null;\n        }\n\n        private void HandleHeartBeat(JToken content)\n        {\n            SendMessageObject(new MobileControlMessage { Type = \"/system/heartbeatAck\" });\n\n            var code = content[\"userCode\"];\n            if (code == null)\n            {\n                return;\n            }\n\n            foreach (var b in _roomBridges)\n            {\n                b.SetUserCode(code.Value<string>());\n            }\n        }\n\n        private void HandleClientJoined(JToken content)\n        {\n            var clientId = content[\"clientId\"].Value<string>();\n            var roomKey = content[\"roomKey\"].Value<string>();\n            var touchpanelKey = content.SelectToken(\"touchpanelKey\");\n\n            if (_roomCombiner == null)\n            {\n                var message = new MobileControlMessage\n                {\n                    Type = \"/system/roomKey\",\n                    ClientId = clientId,\n                    Content = roomKey\n                };\n\n                SendMessageObject(message);\n\n                SendDeviceInterfaces(clientId);\n\n                SendTouchpanelKey(clientId, touchpanelKey);\n                return;\n            }\n\n            if (_roomCombiner.CurrentScenario == null)\n            {\n                var message = new MobileControlMessage\n                {\n                    Type = \"/system/roomKey\",\n                    ClientId = clientId,\n                    Content = roomKey\n                };\n\n                SendMessageObject(message);\n\n                SendDeviceInterfaces(clientId);\n\n                SendTouchpanelKey(clientId, touchpanelKey);\n                return;\n            }\n\n            if (!_roomCombiner.CurrentScenario.UiMap.ContainsKey(roomKey))\n            {\n\n                this.LogWarning(\n                    \"Unable to find correct roomKey for {roomKey} in current scenario. Returning {roomKey} as roomKey\", roomKey);\n\n                var message = new MobileControlMessage\n                {\n                    Type = \"/system/roomKey\",\n                    ClientId = clientId,\n                    Content = roomKey\n                };\n\n                SendMessageObject(message);\n\n                SendDeviceInterfaces(clientId);\n\n                SendTouchpanelKey(clientId, touchpanelKey);\n                return;\n            }\n\n            var newRoomKey = _roomCombiner.CurrentScenario.UiMap[roomKey];\n\n            var newMessage = new MobileControlMessage\n            {\n                Type = \"/system/roomKey\",\n                ClientId = clientId,\n                Content = newRoomKey\n            };\n\n            SendMessageObject(newMessage);\n\n            SendDeviceInterfaces(clientId);\n\n            SendTouchpanelKey(clientId, touchpanelKey);\n        }\n\n        private void SendTouchpanelKey(string clientId, JToken touchpanelKeyToken)\n        {\n            if (touchpanelKeyToken == null)\n            {\n                this.LogWarning(\"Touchpanel key not found for client {clientId}\", clientId);\n                return;\n            }\n\n            SendMessageObject(new MobileControlMessage\n            {\n                Type = \"/system/touchpanelKey\",\n                ClientId = clientId,\n                Content = touchpanelKeyToken.Value<string>()\n            });\n        }\n\n        private void SendDeviceInterfaces(string clientId)\n        {\n            this.LogDebug(\"Sending Device interfaces\");\n            var devices = DeviceManager.GetDevices();\n            Dictionary<string, DeviceInterfaceInfo> deviceInterfaces = new Dictionary<string, DeviceInterfaceInfo>();\n\n            foreach (var device in devices)\n            {\n                var interfaces = device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List<string>();\n\n                deviceInterfaces.Add(device.Key, new DeviceInterfaceInfo\n                {\n                    Key = device.Key,\n                    Name = (device as IKeyName)?.Name ?? \"\",\n                    Interfaces = interfaces\n                });\n            }\n\n            var message = new MobileControlMessage\n            {\n                Type = \"/system/deviceInterfaces\",\n                ClientId = clientId,\n                Content = JToken.FromObject(new { deviceInterfaces })\n            };\n\n            SendMessageObject(message);\n        }\n\n        private void HandleUserCode(JToken content, Action<string, string> action = null)\n        {\n            var code = content[\"userCode\"];\n\n            JToken qrChecksum;\n\n            try\n            {\n                qrChecksum = content.SelectToken(\"qrChecksum\", false);\n            }\n            catch\n            {\n                qrChecksum = new JValue(string.Empty);\n            }\n\n            if (code == null)\n            {\n                return;\n            }\n\n            if (action == null)\n            {\n                foreach (var bridge in _roomBridges)\n                {\n                    bridge.SetUserCode(code.Value<string>(), qrChecksum.Value<string>());\n                }\n\n                return;\n            }\n\n            action(code.Value<string>(), qrChecksum.Value<string>());\n        }\n\n        /// <summary>\n        /// Enqueue an incoming message for processing\n        /// </summary>\n        public void HandleClientMessage(string message)\n        {\n            _receiveQueue.Enqueue(new ProcessStringMessage(message, ParseStreamRx));\n        }\n\n        private void ParseStreamRx(string messageText)\n        {\n            if (string.IsNullOrEmpty(messageText))\n            {\n                return;\n            }\n\n            if (!messageText.Contains(\"/system/heartbeat\"))\n            {\n                this.LogDebug(\n                    \"Message RX: {messageText}\",\n                    messageText\n                );\n            }\n\n            try\n            {\n                var message = JsonConvert.DeserializeObject<MobileControlMessage>(messageText);\n\n                switch (message.Type)\n                {\n                    case \"hello\":\n                        SendInitialMessage();\n                        break;\n                    case \"/system/heartbeat\":\n                        HandleHeartBeat(message.Content);\n                        break;\n                    case \"/system/userCode\":\n                        HandleUserCode(message.Content);\n                        break;\n                    case \"/system/clientJoined\":\n                        HandleClientJoined(message.Content);\n                        break;\n                    case \"/system/reboot\":\n                        SystemMonitorController.ProcessorReboot();\n                        break;\n                    case \"/system/programReset\":\n                        SystemMonitorController.ProgramReset(InitialParametersClass.ApplicationNumber);\n                        break;\n                    case \"raw\":\n                        var wrapper = message.Content.ToObject<DeviceActionWrapper>();\n                        DeviceJsonApi.DoDeviceAction(wrapper);\n                        break;\n                    case \"close\":\n                        this.LogDebug(\"Received close message from server\");\n                        break;\n                    default:\n                        // Incoming message example\n                        // /room/roomA/status\n                        // /room/roomAB/status\n\n                        // ActionDictionary Keys example\n                        // /room/roomA\n                        // /room/roomAB\n\n                        // Can't do direct comparison because it will match /room/roomA with /room/roomA/xxx instead of /room/roomAB/xxx\n                        var handlers = _actionDictionary.Where(kv => message.Type.StartsWith(kv.Key + \"/\")).SelectMany(kv => kv.Value).ToList(); // adds trailing slash to ensure above case is handled\n\n\n                        if (handlers.Count == 0)\n                        {\n                            this.LogInformation(\"-- Warning: Incoming message has no registered handler {type}\", message.Type);\n                            break;\n                        }\n\n                        foreach (var handler in handlers)\n                        {\n                            Task.Run(() =>\n                            {\n                                try\n                                {\n                                    handler.Action(message.Type, message.ClientId, message.Content);\n                                }\n                                catch (Exception ex)\n                                {\n                                    this.LogError(\n                                        \"Exception in handler for message type {type}, ClientId {clientId}\",\n                                        message.Type,\n                                        message.ClientId\n                                    );\n                                    this.LogDebug(ex, \"Stack Trace: \");\n                                }\n                            }).ContinueWith(task =>\n                            {\n                                if (task.IsFaulted && task.Exception != null)\n                                {\n                                    this.LogError(\n                                        \"Unhandled exception in Task for message type {type}, ClientId {clientId}\",\n                                        message.Type,\n                                        message.ClientId\n                                    );\n                                    this.LogDebug(task.Exception.GetBaseException(), \"Stack Trace: \");\n                                }\n                            }, TaskContinuationOptions.OnlyOnFaulted);\n                        }\n\n                        break;\n                }\n            }\n            catch (Exception err)\n            {\n                this.LogException(\n                    err,\n                    \"Unable to parse {message}\",\n                    messageText\n                );\n            }\n        }\n\n        private void TestHttpRequest(string s)\n        {\n            {\n                s = s.Trim();\n                if (string.IsNullOrEmpty(s))\n                {\n                    PrintTestHttpRequestUsage();\n                    return;\n                }\n                var tokens = s.Split(' ');\n                if (tokens.Length < 2)\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"Too few paramaters\\r\");\n                    PrintTestHttpRequestUsage();\n                    return;\n                }\n\n                try\n                {\n                    var url = tokens[1];\n                    switch (tokens[0].ToLower())\n                    {\n                        case \"get\":\n                            {\n                                var resp = new HttpClient().Get(url);\n                                CrestronConsole.ConsoleCommandResponse(\"RESPONSE:\\r{0}\\r\\r\", resp);\n                            }\n                            break;\n                        case \"post\":\n                            {\n                                var resp = new HttpClient().Post(url, new byte[] { });\n                                CrestronConsole.ConsoleCommandResponse(\"RESPONSE:\\r{0}\\r\\r\", resp);\n                            }\n                            break;\n                        default:\n                            CrestronConsole.ConsoleCommandResponse(\"Only get or post supported\\r\");\n                            PrintTestHttpRequestUsage();\n                            break;\n                    }\n                }\n                catch (HttpException e)\n                {\n                    CrestronConsole.ConsoleCommandResponse(\"Exception in request:\\r\");\n                    CrestronConsole.ConsoleCommandResponse(\n                        \"Response URL: {0}\\r\",\n                        e.Response.ResponseUrl\n                    );\n                    CrestronConsole.ConsoleCommandResponse(\n                        \"Response Error Code: {0}\\r\",\n                        e.Response.Code\n                    );\n                    CrestronConsole.ConsoleCommandResponse(\n                        \"Response body: {0}\\r\",\n                        e.Response.ContentString\n                    );\n                }\n            }\n        }\n\n        private void PrintTestHttpRequestUsage()\n        {\n            CrestronConsole.ConsoleCommandResponse(\"Usage: mobilehttprequest:N get/post url\\r\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/PepperDash.Essentials.MobileControl.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">  \n  <PropertyGroup>\n    <RootNamespace>PepperDash.Essentials</RootNamespace>\n    <TargetFramework>net472</TargetFramework>\n    <EnableDynamicLoading>true</EnableDynamicLoading>\n    <Deterministic>false</Deterministic>\n    <AssemblyTitle>epi-essentials-mobile-control</AssemblyTitle>\n    <AssemblyName>epi-essentials-mobile-control</AssemblyName>\n    <Company>PepperDash Technologies</Company>\n    <Product>epi-essentials-mobile-control</Product>\n    <Description>This software is a plugin designed to work as a part of PepperDash Essentials for Crestron control processors. This plugin allows for connection to a PepperDash Mobile Control server.</Description>\n    <Copyright>Copyright 2020</Copyright>\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>\n    <InformationalVersion>$(Version)</InformationalVersion>\n    <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n    <Authors>PepperDash Technologies</Authors>\n    <PackageId>PepperDash.Essentials.MobileControl</PackageId>\n    <PackageTags>crestron 4series</PackageTags>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DefineConstants>TRACE;DEBUG;SERIES4</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <DefineConstants>TRACE;SERIES4</DefineConstants>\n    <DocumentationFile>bin\\$(Configuration)\\epi-essentials-mobile-control.xml</DocumentationFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Remove=\"bin\\**\" />\n    <EmbeddedResource Remove=\"bin\\**\" />\n    <None Remove=\"bin\\**\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Remove=\"MobileControlSimplDeviceBridge.cs\" />\n    <Compile Remove=\"RoomBridges\\MobileControlSIMPLRoomBridge.cs\" />\n    <Compile Remove=\"RoomBridges\\SourceDeviceMapDictionary.cs\" />\n  </ItemGroup>  \n  <ItemGroup>    \n    <PackageReference Include=\"Crestron.SimplSharp.SDK.ProgramLibrary\" Version=\"2.21.90\" />\n    <PackageReference Include=\"WebSocketSharp-netstandard\" Version=\"1.0.1\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Net.Http\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Remove=\"C:\\Users\\awelker\\source\\Essentials\\Essentials\\src\\PepperDash.Essentials.MobileControl\\bin\\Debug\\net472\\PepperDash.Essentials.MobileControl.4.0.0-local.net472.cplz\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Remove=\"C:\\Users\\awelker\\source\\Essentials\\Essentials\\src\\PepperDash.Essentials.MobileControl\\bin\\Debug\\net472\\PepperDash.Essentials.MobileControl.4.0.0-local.net472.cplz\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\PepperDash.Core\\PepperDash.Core.csproj\" />\n    <ProjectReference Include=\"..\\PepperDash.Essentials.Core\\PepperDash.Essentials.Core.csproj\">\n      <Private>false</Private>\n      <ExcludeAssets>runtime</ExcludeAssets>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\PepperDash.Essentials.Devices.Common\\PepperDash.Essentials.Devices.Common.csproj\">\n      <Private>false</Private>\n      <ExcludeAssets>runtime</ExcludeAssets>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\PepperDash.Essentials.MobileControl.Messengers\\PepperDash.Essentials.MobileControl.Messengers.csproj\">\n      <Private>false</Private>\n      <ExcludeAssets>runtime</ExcludeAssets>\n    </ProjectReference>\n  </ItemGroup>  \n</Project>"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlBridgeBase.cs",
    "content": "﻿using System;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\n\nnamespace PepperDash.Essentials.RoomBridges\n{\n    /// <summary>\n    /// Base class for a Mobile Control Bridge that's used to control a room\n    /// </summary>\n    public abstract class MobileControlBridgeBase : MessengerBase, IMobileControlRoomMessenger\n    {\n        /// <summary>\n        /// Triggered when the user Code changes\n        /// </summary>\n        public event EventHandler<EventArgs> UserCodeChanged;\n\n        /// <summary>\n        /// Triggered when a user should be prompted for the new code\n        /// </summary>\n        public event EventHandler<EventArgs> UserPromptedForCode;\n\n        /// <summary>\n        /// Triggered when a client joins to control this room\n        /// </summary>\n        public event EventHandler<EventArgs> ClientJoined;\n\n        /// <summary>\n        /// Triggered when the App URL for this room changes\n        /// </summary>\n        public event EventHandler<EventArgs> AppUrlChanged;\n\n        /// <summary>\n        /// Gets or sets the Parent\n        /// </summary>\n        public IMobileControl Parent { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the AppUrl\n        /// </summary>\n        public string AppUrl { get; private set; }\n        /// <summary>\n        /// Gets or sets the UserCode\n        /// </summary>\n        public string UserCode { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the QrCodeUrl\n        /// </summary>\n        public string QrCodeUrl { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the QrCodeChecksum\n        /// </summary>\n        public string QrCodeChecksum { get; protected set; }\n\n        /// <summary>\n        /// Gets or sets the McServerUrl\n        /// </summary>\n        public string McServerUrl { get; private set; }\n\n        /// <summary>\n        /// Room Name\n        /// </summary>\n        public abstract string RoomName { get; }\n\n        /// <summary>\n        /// Room key\n        /// </summary>\n        public abstract string RoomKey { get; }\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileControlBridgeBase\"/> class\n        /// </summary>\n        /// <param name=\"key\">The unique key for this bridge</param>\n        /// <param name=\"messagePath\">The message path for this bridge</param>\n        protected MobileControlBridgeBase(string key, string messagePath)\n            : base(key, messagePath)\n        {\n        }\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileControlBridgeBase\"/> class\n        /// </summary>\n        /// <param name=\"key\">The unique key for this bridge</param>\n        /// <param name=\"messagePath\">The message path for this bridge</param>\n        /// <param name=\"device\">The device associated with this bridge</param>\n        protected MobileControlBridgeBase(string key, string messagePath, IKeyName device)\n            : base(key, messagePath, device)\n        {\n        }\n\n        /// <summary>\n        /// Set the parent.  Does nothing else.  Override to add functionality such\n        /// as adding actions to parent\n        /// </summary>\n        /// <param name=\"parent\"></param>\n        /// <summary>\n        /// AddParent method\n        /// </summary>\n        public virtual void AddParent(IMobileControl parent)\n        {\n            Parent = parent;\n\n            McServerUrl = Parent.ClientAppUrl;\n        }\n\n        /// <summary>\n        /// Sets the UserCode on the bridge object. Called from controller. A changed code will\n        /// fire method UserCodeChange.  Override that to handle changes\n        /// </summary>\n        /// <param name=\"code\"></param>\n        /// <summary>\n        /// SetUserCode method\n        /// </summary>\n        public void SetUserCode(string code)\n        {\n            var changed = UserCode != code;\n            UserCode = code;\n            if (changed)\n            {\n                UserCodeChange();\n            }\n        }\n\n\n        /// <summary>\n        /// Sets the UserCode on the bridge object. Called from controller. A changed code will\n        /// fire method UserCodeChange.  Override that to handle changes\n        /// </summary>\n        /// <param name=\"code\"></param>\n        /// <param name=\"qrChecksum\">Checksum of the QR code. Used for Cisco codec branding command</param>\n        public void SetUserCode(string code, string qrChecksum)\n        {\n            QrCodeChecksum = qrChecksum;\n\n            SetUserCode(code);\n        }\n\n        /// <summary>\n        /// Update the App Url with the provided URL\n        /// </summary>\n        /// <param name=\"url\">The new App URL</param>\n        public virtual void UpdateAppUrl(string url)\n        {\n            AppUrl = url;\n\n            var handler = AppUrlChanged;\n\n            if (handler == null) return;\n\n            handler(this, new EventArgs());\n        }\n\n        /// <summary>\n        /// Empty method in base class.  Override this to add functionality\n        /// when code changes\n        /// </summary>\n        protected virtual void UserCodeChange()\n        {\n            this.LogDebug(\"Server user code changed: {userCode}\", UserCode);\n\n            var qrUrl = string.Format($\"{Parent.Host}/api/rooms/{Parent.SystemUuid}/{RoomKey}/qr?x={new Random().Next()}\");\n            QrCodeUrl = qrUrl;\n\n            this.LogDebug(\"Server user code changed: {userCode} - {qrCodeUrl}\", UserCode, qrUrl);\n\n            OnUserCodeChanged();\n        }\n\n        /// <summary>\n        /// Trigger the UserCodeChanged event\n        /// </summary>\n        protected void OnUserCodeChanged()\n        {\n            UserCodeChanged?.Invoke(this, new EventArgs());\n        }\n\n        /// <summary>\n        /// Trigger the UserPromptedForCode event\n        /// </summary>\n        protected void OnUserPromptedForCode()\n        {\n            UserPromptedForCode?.Invoke(this, new EventArgs());\n        }\n\n        /// <summary>\n        /// Trigger the ClientJoined event\n        /// </summary>\n        protected void OnClientJoined()\n        {\n            ClientJoined?.Invoke(this, new EventArgs());\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.CrestronIO;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Core.Lighting;\nusing PepperDash.Essentials.Core.Shades;\nusing PepperDash.Essentials.Devices.Common.AudioCodec;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing PepperDash.Essentials.Devices.Common.Room;\nusing PepperDash.Essentials.Devices.Common.VideoCodec;\nusing PepperDash.Essentials.Room.Config;\nusing PepperDash.Essentials.WebSocketServer;\nusing IShades = PepperDash.Essentials.Core.Shades.IShades;\nusing ShadeBase = PepperDash.Essentials.Devices.Common.Shades.ShadeBase;\n\nnamespace PepperDash.Essentials.RoomBridges\n{\n    /// <summary>\n    /// Represents a MobileControlEssentialsRoomBridge\n    /// </summary>\n    public class MobileControlEssentialsRoomBridge : MobileControlBridgeBase\n    {\n        private List<JoinToken> _touchPanelTokens = new List<JoinToken>();\n        /// <summary>\n        /// Gets or sets the Room\n        /// </summary>\n        public IEssentialsRoom Room { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the DefaultRoomKey\n        /// </summary>\n        public string DefaultRoomKey { get; private set; }\n        /// <summary>\n        /// Gets the name of the room\n        /// </summary>\n        public override string RoomName\n        {\n            get { return Room.Name; }\n        }\n\n        /// <summary>\n        /// Gets the key of the room\n        /// </summary>\n        public override string RoomKey\n        {\n            get { return Room.Key; }\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"MobileControlEssentialsRoomBridge\"/> class with the specified room\n        /// </summary>\n        /// <param name=\"room\">The essentials room to bridge</param>\n        public MobileControlEssentialsRoomBridge(IEssentialsRoom room) :\n            this($\"mobileControlBridge-{room.Key}\", room.Key, room)\n        {\n            Room = room;\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"MobileControlEssentialsRoomBridge\"/> class with the specified parameters\n        /// </summary>\n        /// <param name=\"key\">The unique key for this bridge</param>\n        /// <param name=\"roomKey\">The key of the room to bridge</param>\n        /// <param name=\"room\">The essentials room to bridge</param>\n        public MobileControlEssentialsRoomBridge(string key, string roomKey, IEssentialsRoom room) : base(key, $\"/room/{room.Key}\", room as Device)\n        {\n            DefaultRoomKey = roomKey;\n\n            AddPreActivationAction(GetRoom);\n        }\n\n        /// <summary>\n        /// Registers all message handling actions with the AppServer for this room bridge\n        /// </summary>\n        protected override void RegisterActions()\n        {\n            // we add actions to the messaging system with a path, and a related action. Custom action\n            // content objects can be handled in the controller's LineReceived method - and perhaps other\n            // sub-controller parsing could be attached to these classes, so that the systemController\n            // doesn't need to know about everything.\n\n            this.LogInformation(\"Registering Actions with AppServer\");\n\n            AddAction(\"/promptForCode\", (id, content) => OnUserPromptedForCode());\n            AddAction(\"/clientJoined\", (id, content) => OnClientJoined());\n\n            AddAction(\"/touchPanels\", (id, content) => OnTouchPanelsUpdated(content));\n\n            AddAction($\"/userApp\", (id, content) => OnUserAppUpdated(content));\n\n            AddAction(\"/userCode\", (id, content) =>\n            {\n                var msg = content.ToObject<UserCodeChangedContent>();\n\n                SetUserCode(msg.UserCode, msg.QrChecksum ?? string.Empty);\n            });\n\n\n            // Source Changes and room off\n            AddAction(\"/status\", (id, content) =>\n            {\n                SendFullStatusForClientId(id, Room);\n            });\n\n            if (Room is IRunRouteAction routeRoom)\n                AddAction(\"/source\", (id, content) =>\n                {\n\n                    var msg = content.ToObject<SourceSelectMessageContent>();\n\n                    this.LogVerbose(\"Received request to route to source: {sourceListKey} on list: {sourceList}\", msg.SourceListItemKey, msg.SourceListKey);\n\n                    routeRoom.RunRouteAction(msg.SourceListItemKey, msg.SourceListKey);\n                });\n\n            if (Room is IRunDirectRouteAction directRouteRoom)\n            {\n                AddAction(\"/directRoute\", (id, content) =>\n                {\n                    var msg = content.ToObject<DirectRoute>();\n\n\n                    this.LogVerbose(\"Running direct route from {sourceKey} to {destinationKey} with signal type {signalType}\", msg.SourceKey, msg.DestinationKey, msg.SignalType);\n\n                    directRouteRoom.RunDirectRoute(msg.SourceKey, msg.DestinationKey, msg.SignalType);\n                });\n            }\n\n\n            if (Room is IRunDefaultPresentRoute defaultRoom)\n                AddAction(\"/defaultsource\", (id, content) => defaultRoom.RunDefaultPresentRoute());\n\n            if (Room is IHasCurrentSourceInfoChange sscRoom)\n                sscRoom.CurrentSourceChange += Room_CurrentSingleSourceChange;\n\n            if (Room is IEssentialsHuddleVtc1Room vtcRoom)\n            {\n                if (vtcRoom.ScheduleSource != null)\n                {\n                    var key = vtcRoom.Key + \"-\" + Key;\n\n                    if (!AppServerController.CheckForDeviceMessenger(key))\n                    {\n                        var scheduleMessenger = new IHasScheduleAwarenessMessenger(key, vtcRoom.ScheduleSource,\n                            $\"/room/{vtcRoom.Key}\");\n                        AppServerController.AddDeviceMessenger(scheduleMessenger);\n                    }\n                }\n\n                vtcRoom.InCallFeedback.OutputChange += InCallFeedback_OutputChange;\n            }\n\n            if (Room is IPrivacy privacyRoom)\n            {\n                AddAction(\"/volumes/master/privacyMuteToggle\", (id, content) => privacyRoom.PrivacyModeToggle());\n\n                privacyRoom.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange;\n            }\n\n\n            if (Room is IRunDefaultCallRoute defCallRm)\n            {\n                AddAction(\"/activityVideo\", (id, content) => defCallRm.RunDefaultCallRoute());\n            }\n\n            Room.OnFeedback.OutputChange += OnFeedback_OutputChange;\n            Room.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange;\n            Room.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange;\n\n            AddTechRoomActions();\n\n            if (Room is IHasCurrentVolumeControls volumeRoom)\n            {\n                volumeRoom.CurrentVolumeDeviceChange += Room_CurrentVolumeDeviceChange;\n\n                if (volumeRoom.CurrentVolumeControls == null) return;\n\n                AddAction(\"/volumes/master/level\", (id, content) =>\n                {\n                    var msg = content.ToObject<MobileControlSimpleContent<ushort>>();\n\n\n                    if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)\n                        basicVolumeWithFeedback.SetVolume(msg.Value);\n                });\n\n                AddAction(\"/volumes/master/muteToggle\", (id, content) => volumeRoom.CurrentVolumeControls.MuteToggle());\n\n                AddAction(\"/volumes/master/muteOn\", (id, content) =>\n                {\n                    if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)\n                        basicVolumeWithFeedback.MuteOn();\n                });\n\n                AddAction(\"/volumes/master/muteOff\", (id, content) =>\n                {\n                    if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)\n                        basicVolumeWithFeedback.MuteOff();\n                });\n\n                AddAction(\"/volumes/master/volumeUp\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) =>\n                {\n                    if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)\n                    {\n                        basicVolumeWithFeedback.VolumeUp(b);\n                    }\n                }\n                ));\n\n                AddAction(\"/volumes/master/volumeDown\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) =>\n                {\n                    if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)\n                    {\n                        basicVolumeWithFeedback.VolumeDown(b);\n                    }\n                }\n                ));\n\n                // Registers for initial volume events, if possible\n                if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback currentVolumeDevice)\n                {\n                    this.LogVerbose(\"Registering for volume feedback events\");\n\n                    currentVolumeDevice.MuteFeedback.OutputChange += MuteFeedback_OutputChange;\n                    currentVolumeDevice.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;\n                }\n            }\n        }\n\n        private void OnTouchPanelsUpdated(JToken content)\n        {\n            var message = content.ToObject<ApiTouchPanelToken>();\n\n            _touchPanelTokens = message.TouchPanels;\n\n            UpdateTouchPanelAppUrls(message.UserAppUrl);\n        }\n\n        private void UpdateTouchPanelAppUrls(string userAppUrl)\n        {\n            foreach (var tp in _touchPanelTokens)\n            {\n                var dev = DeviceManager.AllDevices.OfType<IMobileControlTouchpanelController>().FirstOrDefault((tpc) => tpc.Key.Equals(tp.TouchpanelKey, StringComparison.InvariantCultureIgnoreCase));\n\n                if (dev == null)\n                {\n                    continue;\n                }\n\n                //UpdateAppUrl($\"{userAppUrl}?token={tp.Token}\");\n\n                dev.SetAppUrl($\"{userAppUrl}?token={tp.Token}\");\n            }\n        }\n\n        private void OnUserAppUpdated(JToken content)\n        {\n            var message = content.ToObject<ApiTouchPanelToken>();\n\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Information, \"Updating User App URL to {userAppUrl}. Full Message: {@message}\", this, message.UserAppUrl, content);\n\n            UpdateTouchPanelAppUrls(message.UserAppUrl);\n        }\n\n        private void InCallFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            var state = new RoomStateMessage\n            {\n                IsInCall = e.BoolValue\n            };\n            PostStatusMessage(state);\n        }\n\n        private void GetRoom()\n        {\n            if (Room != null)\n            {\n                this.LogInformation(\"Room with key {key} already linked.\", DefaultRoomKey);\n                return;\n            }\n\n\n            if (!(DeviceManager.GetDeviceForKey(DefaultRoomKey) is IEssentialsRoom tempRoom))\n            {\n                this.LogInformation(\"Room with key {key} not found or is not an Essentials Room\", DefaultRoomKey);\n                return;\n            }\n\n            Room = tempRoom;\n        }\n\n        /// <summary>\n        /// Handles user code changes and generates QR code URL\n        /// </summary>\n        protected override void UserCodeChange()\n        {\n            Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, \"Server user code changed: {userCode}\", this, UserCode);\n\n            var qrUrl = string.Format(\"{0}/rooms/{1}/{3}/qr?x={2}\", Parent?.Host, Parent?.SystemUuid, new Random().Next(), DefaultRoomKey);\n\n            QrCodeUrl = qrUrl;\n\n            this.LogDebug(\"Server user code changed: {userCode} - {qrUrl}\", UserCode, qrUrl);\n\n            OnUserCodeChanged();\n        }\n\n        /*        /// <summary>\n                /// Override of base: calls base to add parent and then registers actions and events.\n                /// </summary>\n                /// <param name=\"parent\"></param>\n                /// <summary>\n                /// AddParent method\n                /// </summary>\n                /// <inheritdoc />\n                public override void AddParent(MobileControlSystemController parent)\n                {\n                    base.AddParent(parent);\n\n                }*/\n\n        private void AddTechRoomActions()\n        {\n            if (!(Room is IEssentialsTechRoom techRoom))\n            {\n                return;\n            }\n\n            AddAction(\"/roomPowerOn\", (id, content) => techRoom.RoomPowerOn());\n            AddAction(\"/roomPowerOff\", (id, content) => techRoom.RoomPowerOff());\n        }\n\n        private void PrivacyModeIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            var state = new RoomStateMessage();\n\n            var volumes = new Dictionary<string, Volume>\n            {\n                { \"master\",  new Volume(\"master\")\n                    {\n                        PrivacyMuted = e.BoolValue\n                    }\n                }\n            };\n\n            state.Volumes = volumes;\n\n            PostStatusMessage(state);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void IsSharingFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            // sharing source \n            string shareText;\n            bool isSharing;\n\n            if (Room is IHasCurrentSourceInfoChange srcInfoRoom && Room is IHasVideoCodec vcRoom && vcRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && srcInfoRoom.CurrentSourceInfo != null)\n            {\n                shareText = srcInfoRoom.CurrentSourceInfo.PreferredName;\n                isSharing = true;\n            }\n            else\n            {\n                shareText = \"None\";\n                isSharing = false;\n            }\n\n            var state = new RoomStateMessage\n            {\n                Share = new ShareState\n                {\n                    CurrentShareText = shareText,\n                    IsSharing = isSharing\n                }\n            };\n\n            PostStatusMessage(state);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void IsWarmingUpFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            var state = new\n            {\n                isWarmingUp = e.BoolValue\n            };\n\n            PostStatusMessage(JToken.FromObject(state));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            var state = new\n            {\n                isCoolingDown = e.BoolValue\n            };\n            PostStatusMessage(JToken.FromObject(state));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void OnFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            var state = new\n            {\n                isOn = e.BoolValue\n            };\n            PostStatusMessage(JToken.FromObject(state));\n        }\n\n        private void Room_CurrentVolumeDeviceChange(object sender, VolumeDeviceChangeEventArgs e)\n        {\n            if (e.OldDev is IBasicVolumeWithFeedback)\n            {\n                var oldDev = e.OldDev as IBasicVolumeWithFeedback;\n                oldDev.MuteFeedback.OutputChange -= MuteFeedback_OutputChange;\n                oldDev.VolumeLevelFeedback.OutputChange -= VolumeLevelFeedback_OutputChange;\n            }\n\n            if (e.NewDev is IBasicVolumeWithFeedback)\n            {\n                var newDev = e.NewDev as IBasicVolumeWithFeedback;\n                newDev.MuteFeedback.OutputChange += MuteFeedback_OutputChange;\n                newDev.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;\n            }\n        }\n\n        /// <summary>\n        /// Event handler for mute changes\n        /// </summary>\n        private void MuteFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            var state = new RoomStateMessage();\n\n            var volumes = new Dictionary<string, Volume>\n            {\n                { \"master\", new Volume(\"master\", e.BoolValue) }\n            };\n\n            state.Volumes = volumes;\n\n            PostStatusMessage(state);\n        }\n\n        /// <summary>\n        /// Handles Volume changes on room\n        /// </summary>\n        private void VolumeLevelFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n\n            var state = new\n            {\n                volumes = new Dictionary<string, Volume>\n                {\n                    { \"master\", new Volume(\"master\", e.IntValue) }\n                }\n            };\n            PostStatusMessage(JToken.FromObject(state));\n        }\n\n\n        private void Room_CurrentSingleSourceChange(SourceListItem info, ChangeType type)\n        {\n            /* Example message\n             * {\n                  \"type\":\"/room/status\",\n                  \"content\": {\n                    \"selectedSourceKey\": \"off\",\n                  }\n                }\n             */\n\n        }\n\n        /// <summary>\n        /// Sends the full status of the room to the server\n        /// </summary>\n        /// <param name=\"room\"></param>\n        /// <param name=\"id\"></param>\n        private void SendFullStatusForClientId(string id, IEssentialsRoom room)\n        {\n            //Parent.SendMessageObject(GetFullStatus(room));\n            var message = GetFullStatusForClientId(room);\n\n            if (message == null)\n            {\n                return;\n            }\n            PostStatusMessage(message, id);\n        }\n\n\n        /// <summary>\n        /// Gets full room status\n        /// </summary>\n        /// <param name=\"room\">The room to get status of</param>\n        /// <returns>The status response message</returns>\n        private RoomStateMessage GetFullStatusForClientId(IEssentialsRoom room)\n        {\n            try\n            {\n                this.LogVerbose(\"GetFullStatus\");\n\n                var sourceKey = room is IHasCurrentSourceInfoChange ? (room as IHasCurrentSourceInfoChange).CurrentSourceInfoKey : null;\n\n                var volumes = new Dictionary<string, Volume>();\n                if (room is IHasCurrentVolumeControls rmVc)\n                {\n                    if (rmVc.CurrentVolumeControls is IBasicVolumeWithFeedback vc)\n                    {\n                        var volume = new Volume(\"master\", vc.VolumeLevelFeedback.UShortValue, vc.MuteFeedback.BoolValue, \"Volume\", true, \"\");\n                        if (room is IPrivacy privacyRoom)\n                        {\n                            volume.HasPrivacyMute = true;\n                            volume.PrivacyMuted = privacyRoom.PrivacyModeIsOnFeedback.BoolValue;\n                        }\n\n                        volumes.Add(\"master\", volume);\n                    }\n                }\n\n                var state = new RoomStateMessage\n                {\n                    Configuration = GetRoomConfiguration(room),\n                    ActivityMode = 1,\n                    IsOn = room.OnFeedback.BoolValue,\n                    SelectedSourceKey = sourceKey,\n                    Volumes = volumes,\n                    IsWarmingUp = room.IsWarmingUpFeedback.BoolValue,\n                    IsCoolingDown = room.IsCoolingDownFeedback.BoolValue\n                };\n\n                if (room is IEssentialsHuddleVtc1Room vtcRoom)\n                {\n                    state.IsInCall = vtcRoom.InCallFeedback.BoolValue;\n                }\n\n                return state;\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Error getting full status\", this);\n                return null;\n            }\n        }\n\n        /// <summary>\n        /// Determines the configuration of the room and the details about the devices associated with the room\n        /// </summary>\n        /// <param name=\"room\"></param>\n        /// <returns></returns>\n        private RoomConfiguration GetRoomConfiguration(IEssentialsRoom room)\n        {\n            try\n            {\n                var configuration = new RoomConfiguration\n                {\n                    //ShutdownPromptSeconds = room.ShutdownPromptSeconds,\n                    TouchpanelKeys = DeviceManager.AllDevices.\n                    OfType<IMobileControlTouchpanelController>()\n                    .Where((tp) => tp.DefaultRoomKey.Equals(room.Key, StringComparison.InvariantCultureIgnoreCase))\n                    .Select(tp => tp.Key).ToList()\n                };\n\n                try\n                {\n                    var zrcTp = DeviceManager.AllDevices.OfType<IMobileControlTouchpanelController>().SingleOrDefault((tp) => tp.ZoomRoomController);\n\n                    configuration.ZoomRoomControllerKey = zrcTp?.Key;\n                }\n                catch\n                {\n                    configuration.ZoomRoomControllerKey = room.Key;\n                }\n\n                if (room is IHasCiscoNavigatorTouchpanel ciscoNavRoom)\n                {\n                    Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $\"Setting CiscoNavigatorKey to: {ciscoNavRoom.CiscoNavigatorTouchpanelKey}\", this);\n                    configuration.CiscoNavigatorKey = ciscoNavRoom.CiscoNavigatorTouchpanelKey;\n                }\n\n\n\n                // find the room combiner for this room by checking if the room is in the list of rooms for the room combiner\n                var roomCombiner = DeviceManager.AllDevices.OfType<IEssentialsRoomCombiner>().FirstOrDefault();\n\n                configuration.RoomCombinerKey = roomCombiner?.Key;\n\n\n                if (room is IEssentialsRoomPropertiesConfig propertiesConfig)\n                {\n                    configuration.HelpMessage = propertiesConfig.PropertiesConfig.HelpMessageForDisplay;\n                }\n\n                if (room is IEssentialsHuddleSpaceRoom huddleRoom && !string.IsNullOrEmpty(huddleRoom.PropertiesConfig.HelpMessageForDisplay))\n                {\n                    this.LogVerbose(\"Getting huddle room config\");\n                    configuration.HelpMessage = huddleRoom.PropertiesConfig.HelpMessageForDisplay;\n                    configuration.UiBehavior = huddleRoom.PropertiesConfig.UiBehavior;\n                    configuration.DefaultPresentationSourceKey = huddleRoom.PropertiesConfig.DefaultSourceItem;\n\n                }\n\n                if (room is IEssentialsHuddleVtc1Room vtc1Room && !string.IsNullOrEmpty(vtc1Room.PropertiesConfig.HelpMessageForDisplay))\n                {\n                    this.LogVerbose(\"Getting vtc room config\");\n                    configuration.HelpMessage = vtc1Room.PropertiesConfig.HelpMessageForDisplay;\n                    configuration.UiBehavior = vtc1Room.PropertiesConfig.UiBehavior;\n                    configuration.DefaultPresentationSourceKey = vtc1Room.PropertiesConfig.DefaultSourceItem;\n                }\n\n                if (room is IEssentialsTechRoom techRoom && !string.IsNullOrEmpty(techRoom.PropertiesConfig.HelpMessage))\n                {\n                    this.LogVerbose(\"Getting tech room config\");\n                    configuration.HelpMessage = techRoom.PropertiesConfig.HelpMessage;\n                }\n\n                if (room is IHasVideoCodec vcRoom)\n                {\n                    if (vcRoom.VideoCodec != null)\n                    {\n                        this.LogVerbose(\"Getting codec config\");\n                        var type = vcRoom.VideoCodec.GetType();\n\n                        configuration.HasVideoConferencing = true;\n                        configuration.VideoCodecKey = vcRoom.VideoCodec.Key;\n                        configuration.VideoCodecIsZoomRoom = type.Name.Equals(\"ZoomRoom\", StringComparison.InvariantCultureIgnoreCase);\n                    }\n                }\n                ;\n\n                if (room is IHasAudioCodec acRoom)\n                {\n                    if (acRoom.AudioCodec != null)\n                    {\n                        this.LogVerbose(\"Getting audio codec config\");\n                        configuration.HasAudioConferencing = true;\n                        configuration.AudioCodecKey = acRoom.AudioCodec.Key;\n                    }\n                }\n\n\n                if (room is IHasMatrixRouting matrixRoutingRoom)\n                {\n                    this.LogVerbose(\"Getting matrix routing config\");\n                    configuration.MatrixRoutingKey = matrixRoutingRoom.MatrixRoutingDeviceKey;\n                    configuration.EndpointKeys = matrixRoutingRoom.EndpointKeys;\n                }\n\n                if (room is IEnvironmentalControls envRoom)\n                {\n                    this.LogVerbose(\"Getting environmental controls config. RoomHasEnvironmentalControls: {hasEnvironmentalControls}\", envRoom.HasEnvironmentalControlDevices);\n                    configuration.HasEnvironmentalControls = envRoom.HasEnvironmentalControlDevices;\n\n                    if (envRoom.HasEnvironmentalControlDevices)\n                    {\n                        this.LogVerbose(\"Room Has {count} Environmental Control Devices.\", envRoom.EnvironmentalControlDevices.Count);\n\n                        foreach (var dev in envRoom.EnvironmentalControlDevices)\n                        {\n                            this.LogVerbose(\"Adding environmental device: {key}\", dev.Key);\n\n                            eEnvironmentalDeviceTypes type = eEnvironmentalDeviceTypes.None;\n\n                            if (dev is ILightingScenes)\n                            {\n                                type = eEnvironmentalDeviceTypes.Lighting;\n                            }\n                            else if (dev is ShadeBase || dev is IShadesOpenCloseStop || dev is IShadesOpenClosePreset)\n                            {\n                                type = eEnvironmentalDeviceTypes.Shade;\n                            }\n                            else if (dev is IShades)\n                            {\n                                type = eEnvironmentalDeviceTypes.ShadeController;\n                            }\n                            else if (dev is ISwitchedOutput)\n                            {\n                                type = eEnvironmentalDeviceTypes.Relay;\n                            }\n\n                            this.LogVerbose(\"Environmental Device Type: {type}\", type);\n\n                            var envDevice = new EnvironmentalDeviceConfiguration(dev.Key, type);\n\n                            configuration.EnvironmentalDevices.Add(envDevice);\n                        }\n                    }\n                    else\n                    {\n                        this.LogVerbose(\"Room Has No Environmental Control Devices\");\n                    }\n                }\n\n                if (room is IHasDefaultDisplay defDisplayRoom)\n                {\n                    this.LogVerbose(\"Getting default display config\");\n                    configuration.DefaultDisplayKey = defDisplayRoom.DefaultDisplay.Key;\n                    configuration.Destinations.Add(eSourceListItemDestinationTypes.defaultDisplay, defDisplayRoom.DefaultDisplay.Key);\n                }\n\n                if (room is IHasMultipleDisplays multiDisplayRoom)\n                {\n                    this.LogVerbose(\"Getting multiple display config\");\n\n                    if (multiDisplayRoom.Displays == null)\n                    {\n                        this.LogVerbose(\"Displays collection is null\");\n                    }\n                    else\n                    {\n                        this.LogVerbose(\"Displays collection exists\");\n\n                        configuration.Destinations = multiDisplayRoom.Displays.ToDictionary(kv => kv.Key, kv => kv.Value.Key);\n                    }\n                }\n\n                if (room is IHasAccessoryDevices accRoom)\n                {\n                    Debug.LogMessage(Serilog.Events.LogEventLevel.Information, \"Getting accessory devices config\", this);\n\n                    if (accRoom.AccessoryDeviceKeys == null)\n                    {\n                        Debug.LogMessage(Serilog.Events.LogEventLevel.Information, \"Accessory devices collection is null\", this);\n                    }\n                    else\n                    {\n                        Debug.LogMessage(Serilog.Events.LogEventLevel.Information, \"Accessory devices collection exists\", this);\n\n                        configuration.AccessoryDeviceKeys = accRoom.AccessoryDeviceKeys;\n                    }\n                }\n\n                var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(room.SourceListKey);\n                if (sourceList != null)\n                {\n                    this.LogVerbose(\"Getting source list config\");\n                    configuration.SourceList = sourceList;\n                    configuration.HasRoutingControls = true;\n\n                    foreach (var source in sourceList)\n                    {\n                        if (source.Value.SourceDevice is Devices.Common.IRSetTopBoxBase)\n                        {\n                            configuration.HasSetTopBoxControls = true;\n                            continue;\n                        }\n                        else if (source.Value.SourceDevice is CameraBase)\n                        {\n                            configuration.HasCameraControls = true;\n                            continue;\n                        }\n                    }\n                }\n\n                var destinationList = ConfigReader.ConfigObject.GetDestinationListForKey(room.DestinationListKey);\n\n                if (destinationList != null)\n                {\n                    configuration.DestinationList = destinationList;\n                }\n\n                var audioControlPointList = ConfigReader.ConfigObject.GetAudioControlPointListForKey(room.AudioControlPointListKey);\n\n                if (audioControlPointList != null)\n                {\n                    configuration.AudioControlPointList = audioControlPointList;\n                }\n\n                var cameraList = ConfigReader.ConfigObject.GetCameraListForKey(room.CameraListKey);\n\n                if (cameraList != null)\n                {\n                    configuration.CameraList = cameraList;\n                }\n\n                return configuration;\n\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Exception getting room configuration\");\n                return new RoomConfiguration();\n            }\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a RoomStateMessage\n    /// </summary>\n    public class RoomStateMessage : DeviceStateMessageBase\n    {\n\n        /// <summary>\n        /// Gets or sets the Configuration\n        /// </summary>\n        [JsonProperty(\"configuration\", NullValueHandling = NullValueHandling.Ignore)]\n        public RoomConfiguration Configuration { get; set; }\n\n        /// <summary>\n        /// Gets or sets the activity mode of the room\n        /// </summary>\n        [JsonProperty(\"activityMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public int? ActivityMode { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether advanced sharing is active\n        /// </summary>\n        [JsonProperty(\"advancedSharingActive\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? AdvancedSharingActive { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room is powered on\n        /// </summary>\n        [JsonProperty(\"isOn\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsOn { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room is warming up\n        /// </summary>\n        [JsonProperty(\"isWarmingUp\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsWarmingUp { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room is cooling down\n        /// </summary>\n        [JsonProperty(\"isCoolingDown\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsCoolingDown { get; set; }\n\n        /// <summary>\n        /// Gets or sets the SelectedSourceKey\n        /// </summary>\n        [JsonProperty(\"selectedSourceKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string SelectedSourceKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Share\n        /// </summary>\n        [JsonProperty(\"share\", NullValueHandling = NullValueHandling.Ignore)]\n        public ShareState Share { get; set; }\n\n        /// <summary>\n        /// Gets or sets the volume controls collection\n        /// </summary>\n        [JsonProperty(\"volumes\", NullValueHandling = NullValueHandling.Ignore)]\n        public Dictionary<string, Volume> Volumes { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room is in a call\n        /// </summary>\n        [JsonProperty(\"isInCall\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsInCall { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a ShareState\n    /// </summary>\n    public class ShareState\n    {\n\n        /// <summary>\n        /// Gets or sets the CurrentShareText\n        /// </summary>\n        [JsonProperty(\"currentShareText\", NullValueHandling = NullValueHandling.Ignore)]\n        public string CurrentShareText { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether sharing is enabled\n        /// </summary>\n        [JsonProperty(\"enabled\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? Enabled { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether content is currently being shared\n        /// </summary>\n        [JsonProperty(\"isSharing\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsSharing { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a RoomConfiguration\n    /// </summary>\n    public class RoomConfiguration\n    {\n        /// <summary>\n        /// Gets or sets whether the room has video conferencing capabilities\n        /// </summary>\n        [JsonProperty(\"hasVideoConferencing\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasVideoConferencing { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the video codec is a Zoom Room\n        /// </summary>\n        [JsonProperty(\"videoCodecIsZoomRoom\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? VideoCodecIsZoomRoom { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room has audio conferencing capabilities\n        /// </summary>\n        [JsonProperty(\"hasAudioConferencing\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasAudioConferencing { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room has environmental controls (lighting, shades, etc.)\n        /// </summary>\n        [JsonProperty(\"hasEnvironmentalControls\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasEnvironmentalControls { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room has camera controls\n        /// </summary>\n        [JsonProperty(\"hasCameraControls\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasCameraControls { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room has set-top box controls\n        /// </summary>\n        [JsonProperty(\"hasSetTopBoxControls\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasSetTopBoxControls { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room has routing controls\n        /// </summary>\n        [JsonProperty(\"hasRoutingControls\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasRoutingControls { get; set; }\n\n        /// <summary>\n        /// Gets or sets the TouchpanelKeys\n        /// </summary>\n        [JsonProperty(\"touchpanelKeys\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<string> TouchpanelKeys { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the ZoomRoomControllerKey\n        /// </summary>\n        [JsonProperty(\"zoomRoomControllerKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string ZoomRoomControllerKey { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the CiscoNavigatorKey\n        /// </summary>\n        [JsonProperty(\"ciscoNavigatorKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string CiscoNavigatorKey { get; set; }\n\n\n\n        /// <summary>\n        /// Gets or sets the VideoCodecKey\n        /// </summary>\n        [JsonProperty(\"videoCodecKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string VideoCodecKey { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the AudioCodecKey\n        /// </summary>\n        [JsonProperty(\"audioCodecKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string AudioCodecKey { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the MatrixRoutingKey\n        /// </summary>\n        [JsonProperty(\"matrixRoutingKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string MatrixRoutingKey { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the EndpointKeys\n        /// </summary>\n        [JsonProperty(\"endpointKeys\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<string> EndpointKeys { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the AccessoryDeviceKeys\n        /// </summary>\n        [JsonProperty(\"accessoryDeviceKeys\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<string> AccessoryDeviceKeys { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the DefaultDisplayKey\n        /// </summary>\n        [JsonProperty(\"defaultDisplayKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string DefaultDisplayKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the destinations dictionary keyed by destination type\n        /// </summary>\n        [JsonProperty(\"destinations\", NullValueHandling = NullValueHandling.Ignore)]\n        public Dictionary<eSourceListItemDestinationTypes, string> Destinations { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the EnvironmentalDevices\n        /// </summary>\n        [JsonProperty(\"environmentalDevices\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<EnvironmentalDeviceConfiguration> EnvironmentalDevices { get; set; }\n\n        /// <summary>\n        /// Gets or sets the source list for the room\n        /// </summary>\n        [JsonProperty(\"sourceList\", NullValueHandling = NullValueHandling.Ignore)]\n        public Dictionary<string, SourceListItem> SourceList { get; set; }\n\n        /// <summary>\n        /// Gets or sets the destination list for the room\n        /// </summary>\n        [JsonProperty(\"destinationList\", NullValueHandling = NullValueHandling.Ignore)]\n        public Dictionary<string, DestinationListItem> DestinationList { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the AudioControlPointList\n        /// </summary>\n        [JsonProperty(\"audioControlPointList\", NullValueHandling = NullValueHandling.Ignore)]\n        public AudioControlPointListItem AudioControlPointList { get; set; }\n\n        /// <summary>\n        /// Gets or sets the camera list for the room\n        /// </summary>\n        [JsonProperty(\"cameraList\", NullValueHandling = NullValueHandling.Ignore)]\n        public Dictionary<string, CameraListItem> CameraList { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the DefaultPresentationSourceKey\n        /// </summary>\n        [JsonProperty(\"defaultPresentationSourceKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string DefaultPresentationSourceKey { get; set; }\n\n\n\n        /// <summary>\n        /// Gets or sets the HelpMessage\n        /// </summary>\n        [JsonProperty(\"helpMessage\", NullValueHandling = NullValueHandling.Ignore)]\n        public string HelpMessage { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the TechPassword\n        /// </summary>\n        [JsonProperty(\"techPassword\", NullValueHandling = NullValueHandling.Ignore)]\n        public string TechPassword { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the UiBehavior\n        /// </summary>\n        [JsonProperty(\"uiBehavior\", NullValueHandling = NullValueHandling.Ignore)]\n        public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the room supports advanced sharing features\n        /// </summary>\n        [JsonProperty(\"supportsAdvancedSharing\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? SupportsAdvancedSharing { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the user can change the share mode\n        /// </summary>\n        [JsonProperty(\"userCanChangeShareMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? UserCanChangeShareMode { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the RoomCombinerKey\n        /// </summary>\n        [JsonProperty(\"roomCombinerKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string RoomCombinerKey { get; set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RoomConfiguration\"/> class\n        /// </summary>\n        public RoomConfiguration()\n        {\n            Destinations = new Dictionary<eSourceListItemDestinationTypes, string>();\n            EnvironmentalDevices = new List<EnvironmentalDeviceConfiguration>();\n            SourceList = new Dictionary<string, SourceListItem>();\n            TouchpanelKeys = new List<string>();\n        }\n    }\n\n    /// <summary>\n    /// Represents a EnvironmentalDeviceConfiguration\n    /// </summary>\n    public class EnvironmentalDeviceConfiguration\n    {\n\n        /// <summary>\n        /// Gets or sets the DeviceKey\n        /// </summary>\n        [JsonProperty(\"deviceKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string DeviceKey { get; private set; }\n\n\n        /// <summary>\n        /// Gets or sets the DeviceType\n        /// </summary>\n        [JsonConverter(typeof(StringEnumConverter))]\n        [JsonProperty(\"deviceType\", NullValueHandling = NullValueHandling.Ignore)]\n        public eEnvironmentalDeviceTypes DeviceType { get; private set; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"EnvironmentalDeviceConfiguration\"/> class\n        /// </summary>\n        /// <param name=\"key\">The device key</param>\n        /// <param name=\"type\">The environmental device type</param>\n        public EnvironmentalDeviceConfiguration(string key, eEnvironmentalDeviceTypes type)\n        {\n            DeviceKey = key;\n            DeviceType = type;\n        }\n    }\n\n    /// <summary>\n    /// Enumeration of environmental device types\n    /// </summary>\n    public enum eEnvironmentalDeviceTypes\n    {\n        /// <summary>\n        /// No environmental device type specified\n        /// </summary>\n        None,\n        /// <summary>\n        /// Lighting device type\n        /// </summary>\n        Lighting,\n        /// <summary>\n        /// Shade device type\n        /// </summary>\n        Shade,\n        /// <summary>\n        /// Shade controller device type\n        /// </summary>\n        ShadeController,\n        /// <summary>\n        /// Relay device type\n        /// </summary>\n        Relay,\n    }\n\n    /// <summary>\n    /// Represents a ApiTouchPanelToken\n    /// </summary>\n    public class ApiTouchPanelToken\n    {\n\n        /// <summary>\n        /// Gets or sets the TouchPanels\n        /// </summary>\n        [JsonProperty(\"touchPanels\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<JoinToken> TouchPanels { get; set; } = new List<JoinToken>();\n\n\n        /// <summary>\n        /// Gets or sets the UserAppUrl\n        /// </summary>\n        [JsonProperty(\"userAppUrl\", NullValueHandling = NullValueHandling.Ignore)]\n        public string UserAppUrl { get; set; } = \"\";\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlSIMPLRoomBridge.cs",
    "content": "﻿using Crestron.SimplSharp;\nusing Crestron.SimplSharp.Reflection;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.EthernetCommunication;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing PepperDash.Essentials.Devices.Common.Codec;\nusing PepperDash.Essentials.Room.Config;\nusing System;\nusing System.Collections.Generic;\n\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    // ReSharper disable once InconsistentNaming\n    /// <summary>\n    /// Represents a MobileControlSIMPLRoomBridge\n    /// </summary>\n    public class MobileControlSIMPLRoomBridge : MobileControlBridgeBase, IDelayedConfiguration\n    {\n        private const int SupportedDisplayCount = 10;\n\n        /// <summary>\n        /// Fires when config is ready to go\n        /// </summary>\n        public event EventHandler<EventArgs> ConfigurationIsReady;\n\n        /// <summary>\n        /// Gets or sets the Eisc\n        /// </summary>\n        public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the JoinMap\n        /// </summary>\n        public MobileControlSIMPLRoomJoinMap JoinMap { get; private set; }\n\n        public Dictionary<string, MessengerBase> DeviceMessengers { get; private set; }\n\n\n        /// <summary>\n        /// Gets or sets the ConfigIsLoaded\n        /// </summary>\n        public bool ConfigIsLoaded { get; private set; }\n\n        /// <inheritdoc />\n        public override string RoomName\n        {\n            get\n            {\n                var name = Eisc.StringOutput[JoinMap.ConfigRoomName.JoinNumber].StringValue;\n                return string.IsNullOrEmpty(name) ? \"Not Loaded\" : name;\n            }\n        }\n\n        /// <inheritdoc />\n        public override string RoomKey\n        {\n            get { return \"room1\"; }\n        }\n\n        private readonly MobileControlSimplDeviceBridge _sourceBridge;\n\n        private SIMPLAtcMessenger _atcMessenger;\n        private SIMPLVtcMessenger _vtcMessenger;\n        private SimplDirectRouteMessenger _directRouteMessenger;\n\n        private const string _syntheticDeviceKey = \"syntheticDevice\";\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"name\"></param>\n        /// <param name=\"ipId\"></param>\n        public MobileControlSIMPLRoomBridge(string key, string name, uint ipId)\n            : base(key, \"\")\n        {\n            Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipId, \"127.0.0.2\", Global.ControlSystem);\n            var reg = Eisc.Register();\n            if (reg != eDeviceRegistrationUnRegistrationResponse.Success)\n                Debug.Console(0, this, \"Cannot connect EISC at IPID {0}: \\r{1}\", ipId, reg);\n\n            JoinMap = new MobileControlSIMPLRoomJoinMap(1);\n\n            _sourceBridge = new MobileControlSimplDeviceBridge(key + \"-sourceBridge\", \"SIMPL source bridge\", Eisc);\n            DeviceManager.AddDevice(_sourceBridge);\n\n            CrestronConsole.AddNewConsoleCommand((s) => JoinMap.PrintJoinMapInfo(), \"printmobilejoinmap\", \"Prints the MobileControlSIMPLRoomBridge JoinMap\", ConsoleAccessLevelEnum.AccessOperator);\n\n            AddPostActivationAction(() =>\n                {\n                    // Inform the SIMPL program that config can be sent\n                    Eisc.BooleanInput[JoinMap.ReadyForConfig.JoinNumber].BoolValue = true;\n\n                    Eisc.SigChange += EISC_SigChange;\n                    Eisc.OnlineStatusChange += (o, a) =>\n                    {\n                        if (!a.DeviceOnLine)\n                        {\n                            return;\n                        }\n\n                        Debug.Console(1, this, \"SIMPL EISC online={0}. Config is ready={1}. Use Essentials Config={2}\",\n                            a.DeviceOnLine, Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue,\n                            Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue);\n\n                        if (Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue)\n                            LoadConfigValues();\n\n                        if (Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue)\n                            UseEssentialsConfig();\n                    };\n                    // load config if it's already there\n                    if (Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue)\n                    {\n                        LoadConfigValues();\n                    }\n\n                    if (Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue)\n                    {\n                        UseEssentialsConfig();\n                    }\n                });\n        }\n\n\n        /// <summary>\n        /// Finish wiring up everything after all devices are created. The base class will hunt down the related\n        /// parent controller and link them up.\n        /// </summary>\n        /// <returns></returns>\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        public override bool CustomActivate()\n        {\n            Debug.Console(0, this, \"Final activation. Setting up actions and feedbacks\");\n            //SetupFunctions();\n            //SetupFeedbacks();\n\n            var atcKey = string.Format(\"atc-{0}-{1}\", Key, Key);\n            _atcMessenger = new SIMPLAtcMessenger(atcKey, Eisc, \"/device/audioCodec\");\n            _atcMessenger.RegisterWithAppServer(Parent);\n\n            var vtcKey = string.Format(\"atc-{0}-{1}\", Key, Key);\n            _vtcMessenger = new SIMPLVtcMessenger(vtcKey, Eisc, \"/device/videoCodec\");\n            _vtcMessenger.RegisterWithAppServer(Parent);\n\n            var drKey = string.Format(\"directRoute-{0}-{1}\", Key, Key);\n            _directRouteMessenger = new SimplDirectRouteMessenger(drKey, Eisc, \"/routing\");\n            _directRouteMessenger.RegisterWithAppServer(Parent);\n\n            CrestronConsole.AddNewConsoleCommand(s =>\n            {\n                JoinMap.PrintJoinMapInfo();\n\n                _atcMessenger.JoinMap.PrintJoinMapInfo();\n\n                _vtcMessenger.JoinMap.PrintJoinMapInfo();\n\n                _directRouteMessenger.JoinMap.PrintJoinMapInfo();\n\n                // TODO: Update Source Bridge to use new JoinMap scheme\n                //_sourceBridge.JoinMap.PrintJoinMapInfo();\n            }, \"printmobilebridge\", \"Prints MC-SIMPL bridge EISC data\", ConsoleAccessLevelEnum.AccessOperator);\n\n            return base.CustomActivate();\n        }\n\n        private void UseEssentialsConfig()\n        {\n            ConfigIsLoaded = false;\n\n            SetupDeviceMessengers();\n\n            Debug.Console(0, this, \"******* ESSENTIALS CONFIG: \\r{0}\",\n                JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));\n\n            ConfigurationIsReady?.Invoke(this, new EventArgs());\n\n            ConfigIsLoaded = true;\n        }\n\n        protected override void RegisterActions()\n\n        {\n            SetupFunctions();\n            SetupFeedbacks();\n        }\n\n        /// <summary>\n        /// Setup the actions to take place on various incoming API calls\n        /// </summary>\n        private void SetupFunctions()\n        {\n            AddAction(@\"/promptForCode\",\n                (id, content) => Eisc.PulseBool(JoinMap.PromptForCode.JoinNumber));\n            AddAction(@\"/clientJoined\", (id, content) => Eisc.PulseBool(JoinMap.ClientJoined.JoinNumber));\n\n            AddAction(@\"/status\", (id, content) => SendFullStatus());\n\n            AddAction(@\"/source\", (id, content) =>\n            {\n                var msg = content.ToObject<SourceSelectMessageContent>();\n\n                Eisc.SetString(JoinMap.CurrentSourceKey.JoinNumber, msg.SourceListItemKey);\n                Eisc.PulseBool(JoinMap.SourceHasChanged.JoinNumber);\n            });\n\n            AddAction(@\"/defaultsource\", (id, content) =>\n                Eisc.PulseBool(JoinMap.ActivityShare.JoinNumber));\n            AddAction(@\"/activityPhone\", (id, content) =>\n                Eisc.PulseBool(JoinMap.ActivityPhoneCall.JoinNumber));\n            AddAction(@\"/activityVideo\", (id, content) =>\n                Eisc.PulseBool(JoinMap.ActivityVideoCall.JoinNumber));\n\n            AddAction(@\"/volumes/master/level\", (id, content) =>\n            {\n                var value = content[\"value\"].Value<ushort>();\n\n                Eisc.SetUshort(JoinMap.MasterVolume.JoinNumber, value);\n            });\n\n            AddAction(@\"/volumes/master/muteToggle\", (id, content) =>\n                Eisc.PulseBool(JoinMap.MasterVolume.JoinNumber));\n            AddAction(@\"/volumes/master/privacyMuteToggle\", (id, content) =>\n                Eisc.PulseBool(JoinMap.PrivacyMute.JoinNumber));\n\n\n            // /xyzxyz/volumes/master/muteToggle ---> BoolInput[1]\n\n            var volumeStart = JoinMap.VolumeJoinStart.JoinNumber;\n            var volumeEnd = JoinMap.VolumeJoinStart.JoinNumber + JoinMap.VolumeJoinStart.JoinSpan;\n\n            for (uint i = volumeStart; i <= volumeEnd; i++)\n            {\n                var index = i;\n                AddAction(string.Format(@\"/volumes/level-{0}/level\", index), (id, content) =>\n                {\n                    var value = content[\"value\"].Value<ushort>();\n                    Eisc.SetUshort(index, value);\n                });\n\n                AddAction(string.Format(@\"/volumes/level-{0}/muteToggle\", index), (id, content) =>\n                    Eisc.PulseBool(index));\n            }\n\n            AddAction(@\"/shutdownStart\", (id, content) =>\n                Eisc.PulseBool(JoinMap.ShutdownStart.JoinNumber));\n            AddAction(@\"/shutdownEnd\", (id, content) =>\n                Eisc.PulseBool(JoinMap.ShutdownEnd.JoinNumber));\n            AddAction(@\"/shutdownCancel\", (id, content) =>\n                Eisc.PulseBool(JoinMap.ShutdownCancel.JoinNumber));\n        }\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"devKey\"></param>\n        private void SetupSourceFunctions(string devKey)\n        {\n            var sourceJoinMap = new SourceDeviceMapDictionary();\n\n            var prefix = string.Format(\"/device/{0}/\", devKey);\n\n            foreach (var item in sourceJoinMap)\n            {\n                var join = item.Value;\n                AddAction(string.Format(\"{0}{1}\", prefix, item.Key), (id, content) =>\n                {\n                    HandlePressAndHoldEisc(content, b => Eisc.SetBool(join, b));\n                });\n            }\n        }\n\n        private void HandlePressAndHoldEisc(JToken content, Action<bool> action)\n        {\n            var state = content.ToObject<MobileControlSimpleContent<string>>();\n\n            var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);\n            if (timerHandler == null)\n            {\n                return;\n            }\n\n            timerHandler(state.Value, action);\n\n            action(state.Value.Equals(\"true\", StringComparison.InvariantCultureIgnoreCase));\n        }\n\n\n        /// <summary>\n        /// Links feedbacks to whatever is gonna happen!\n        /// </summary>\n        private void SetupFeedbacks()\n        {\n            // Power \n            Eisc.SetBoolSigAction(JoinMap.RoomIsOn.JoinNumber, b =>\n                PostStatus(new\n                {\n                    isOn = b\n                }));\n\n            // Source change things\n            Eisc.SetSigTrueAction(JoinMap.SourceHasChanged.JoinNumber, () =>\n                PostStatus(new\n                {\n                    selectedSourceKey = Eisc.StringOutput[JoinMap.CurrentSourceKey.JoinNumber].StringValue\n                }));\n\n            // Volume things\n            Eisc.SetUShortSigAction(JoinMap.MasterVolume.JoinNumber, u =>\n                PostStatus(new\n                {\n                    volumes = new\n                    {\n                        master = new\n                        {\n                            level = u\n                        }\n                    }\n                }));\n\n            // map MasterVolumeIsMuted join -> status/volumes/master/muted\n            // \n\n            Eisc.SetBoolSigAction(JoinMap.MasterVolume.JoinNumber, b =>\n                PostStatus(new\n                {\n                    volumes = new\n                    {\n                        master = new\n                        {\n                            muted = b\n                        }\n                    }\n                }));\n            Eisc.SetBoolSigAction(JoinMap.PrivacyMute.JoinNumber, b =>\n                PostStatus(new\n                {\n                    volumes = new\n                    {\n                        master = new\n                        {\n                            privacyMuted = b\n                        }\n                    }\n                }));\n\n            var volumeStart = JoinMap.VolumeJoinStart.JoinNumber;\n            var volumeEnd = JoinMap.VolumeJoinStart.JoinNumber + JoinMap.VolumeJoinStart.JoinSpan;\n\n            for (uint i = volumeStart; i <= volumeEnd; i++)\n            {\n                var index = i; // local scope for lambdas\n                Eisc.SetUShortSigAction(index, u => // start at join 2\n                {\n                    // need a dict in order to create the level-n property on auxFaders\n                    var dict = new Dictionary<string, object> { { \"level-\" + index, new { level = u } } };\n                    PostStatus(new\n                    {\n                        volumes = new\n                        {\n                            auxFaders = dict,\n                        }\n                    });\n                });\n                Eisc.SetBoolSigAction(index, b =>\n                {\n                    // need a dict in order to create the level-n property on auxFaders\n                    var dict = new Dictionary<string, object> { { \"level-\" + index, new { muted = b } } };\n                    PostStatus(new\n                    {\n                        volumes = new\n                        {\n                            auxFaders = dict,\n                        }\n                    });\n                });\n            }\n\n            Eisc.SetUShortSigAction(JoinMap.NumberOfAuxFaders.JoinNumber, u =>\n                PostStatus(new\n                {\n                    volumes = new\n                    {\n                        numberOfAuxFaders = u,\n                    }\n                }));\n\n            // shutdown things\n            Eisc.SetSigTrueAction(JoinMap.ShutdownCancel.JoinNumber, () =>\n                PostMessage(\"/shutdown/\", new\n                {\n                    state = \"wasCancelled\"\n                }));\n            Eisc.SetSigTrueAction(JoinMap.ShutdownEnd.JoinNumber, () =>\n                PostMessage(\"/shutdown/\", new\n                {\n                    state = \"hasFinished\"\n                }));\n            Eisc.SetSigTrueAction(JoinMap.ShutdownStart.JoinNumber, () =>\n                PostMessage(\"/shutdown/\", new\n                {\n                    state = \"hasStarted\",\n                    duration = Eisc.UShortOutput[JoinMap.ShutdownPromptDuration.JoinNumber].UShortValue\n                }));\n\n            // Config things\n            Eisc.SetSigTrueAction(JoinMap.ConfigIsReady.JoinNumber, LoadConfigValues);\n\n            // Activity modes\n            Eisc.SetSigTrueAction(JoinMap.ActivityShare.JoinNumber, () => UpdateActivity(1));\n            Eisc.SetSigTrueAction(JoinMap.ActivityPhoneCall.JoinNumber, () => UpdateActivity(2));\n            Eisc.SetSigTrueAction(JoinMap.ActivityVideoCall.JoinNumber, () => UpdateActivity(3));\n\n            AppServerController.ApiOnlineAndAuthorized.LinkInputSig(Eisc.BooleanInput[JoinMap.ApiOnlineAndAuthorized.JoinNumber]);\n        }\n\n\n        /// <summary>\n        /// Updates activity states\n        /// </summary>\n        private void UpdateActivity(int mode)\n        {\n            PostStatus(new\n            {\n                activityMode = mode,\n            });\n        }\n\n        /// <summary>\n        /// Synthesizes a source device config from the SIMPL config join data\n        /// </summary>\n        /// <param name=\"sli\"></param>\n        /// <param name=\"type\"></param>\n        /// <param name=\"i\"></param>\n        /// <returns></returns>\n        private DeviceConfig GetSyntheticSourceDevice(SourceListItem sli, string type, uint i)\n        {\n            var groupMap = GetSourceGroupDictionary();\n            var key = sli.SourceKey;\n            var name = sli.Name;\n\n            // If not, synthesize the device config\n            var group = \"genericsource\";\n            if (groupMap.ContainsKey(type))\n            {\n                group = groupMap[type];\n            }\n\n            // add dev to devices list\n            var devConf = new DeviceConfig\n            {\n                Group = group,\n                Key = key,\n                Name = name,\n                Type = type,\n                Properties = new JObject(new JProperty(_syntheticDeviceKey, true)),\n            };\n\n            if (group.ToLower().StartsWith(\"settopbox\")) // Add others here as needed\n            {\n                SetupSourceFunctions(key);\n            }\n\n            if (group.ToLower().Equals(\"simplmessenger\"))\n            {\n                if (type.ToLower().Equals(\"simplcameramessenger\"))\n                {\n                    var props = new SimplMessengerPropertiesConfig\n                    {\n                        DeviceKey = key,\n                        JoinMapKey = \"\"\n                    };\n                    var joinStart = 1000 + (i * 100) + 1; // 1001, 1101, 1201, 1301... etc.\n                    props.JoinStart = joinStart;\n                    devConf.Properties = JToken.FromObject(props);\n                }\n            }\n\n            return devConf;\n        }\n\n        /// <summary>\n        /// Reads in config values when the Simpl program is ready\n        /// </summary>\n        private void LoadConfigValues()\n        {\n            Debug.Console(1, this, \"Loading configuration from SIMPL EISC bridge\");\n            ConfigIsLoaded = false;\n\n            var co = ConfigReader.ConfigObject;\n\n            if (!string.IsNullOrEmpty(Eisc.StringOutput[JoinMap.PortalSystemUrl.JoinNumber].StringValue))\n            {\n                ConfigReader.ConfigObject.SystemUrl = Eisc.StringOutput[JoinMap.PortalSystemUrl.JoinNumber].StringValue;\n            }\n\n            co.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;\n            var version = Assembly.GetExecutingAssembly().GetName().Version;\n            co.Info.RuntimeInfo.AssemblyVersion = string.Format(\"{0}.{1}.{2}\", version.Major, version.Minor,\n                version.Build);\n\n            //Room\n            //if (co.Rooms == null)\n            // always start fresh in case simpl changed\n            co.Rooms = new List<DeviceConfig>();\n            var rm = new DeviceConfig();\n            if (co.Rooms.Count == 0)\n            {\n                Debug.Console(0, this, \"Adding room to config\");\n                co.Rooms.Add(rm);\n            }\n            else\n            {\n                Debug.Console(0, this, \"Replacing Room[0] in config\");\n                co.Rooms[0] = rm;\n            }\n            rm.Name = Eisc.StringOutput[JoinMap.ConfigRoomName.JoinNumber].StringValue;\n            rm.Key = \"room1\";\n            rm.Type = \"SIMPL01\";\n\n            var rmProps = rm.Properties == null\n                ? new SimplRoomPropertiesConfig()\n                : JsonConvert.DeserializeObject<SimplRoomPropertiesConfig>(rm.Properties.ToString());\n\n            rmProps.Help = new EssentialsHelpPropertiesConfig\n            {\n                CallButtonText = Eisc.StringOutput[JoinMap.ConfigHelpNumber.JoinNumber].StringValue,\n                Message = Eisc.StringOutput[JoinMap.ConfigHelpMessage.JoinNumber].StringValue\n            };\n\n            rmProps.Environment = new EssentialsEnvironmentPropertiesConfig(); // enabled defaults to false\n\n            rmProps.RoomPhoneNumber = Eisc.StringOutput[JoinMap.ConfigRoomPhoneNumber.JoinNumber].StringValue;\n            rmProps.RoomURI = Eisc.StringOutput[JoinMap.ConfigRoomUri.JoinNumber].StringValue;\n            rmProps.SpeedDials = new List<SimplSpeedDial>();\n\n            // This MAY need a check \n            if (Eisc.BooleanOutput[JoinMap.ActivityPhoneCallEnable.JoinNumber].BoolValue)\n            {\n                rmProps.AudioCodecKey = \"audioCodec\";\n            }\n\n            if (Eisc.BooleanOutput[JoinMap.ActivityVideoCallEnable.JoinNumber].BoolValue)\n            {\n                rmProps.VideoCodecKey = \"videoCodec\";\n            }\n\n            // volume control names\n\n            //// use Volumes object or?\n            //rmProps.VolumeSliderNames = new List<string>();\n            //for(uint i = 701; i <= 700 + volCount; i++)\n            //{\n            //    rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue);\n            //}\n\n            // There should be Mobile Control devices in here, I think...\n            if (co.Devices == null)\n                co.Devices = new List<DeviceConfig>();\n\n            // clear out previous SIMPL devices\n            co.Devices.RemoveAll(d =>\n                d.Key.StartsWith(\"source-\", StringComparison.OrdinalIgnoreCase)\n                || d.Key.Equals(\"audioCodec\", StringComparison.OrdinalIgnoreCase)\n                || d.Key.Equals(\"videoCodec\", StringComparison.OrdinalIgnoreCase)\n            || d.Key.StartsWith(\"destination-\", StringComparison.OrdinalIgnoreCase));\n\n            rmProps.SourceListKey = \"default\";\n            rm.Properties = JToken.FromObject(rmProps);\n\n            // Source list! This might be brutal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n            co.SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>();\n            var newSl = new Dictionary<string, SourceListItem>();\n            // add \"none\" source if VTC present\n\n            if (!string.IsNullOrEmpty(rmProps.VideoCodecKey))\n            {\n                var codecOsd = new SourceListItem\n                {\n                    Name = \"None\",\n                    IncludeInSourceList = true,\n                    Order = 1,\n                    Type = eSourceListItemType.Route,\n                    SourceKey = \"\"\n                };\n                newSl.Add(\"Source-None\", codecOsd);\n            }\n            // add sources...\n            var useSourceEnabled = Eisc.BooleanOutput[JoinMap.UseSourceEnabled.JoinNumber].BoolValue;\n            for (uint i = 0; i <= 19; i++)\n            {\n                var name = Eisc.StringOutput[JoinMap.SourceNameJoinStart.JoinNumber + i].StringValue;\n\n                if (!Eisc.BooleanOutput[JoinMap.UseSourceEnabled.JoinNumber].BoolValue && string.IsNullOrEmpty(name))\n                {\n                    this.LogDebug(\"Source at join {join} does not have a name\", JoinMap.SourceNameJoinStart.JoinNumber + i);\n                    break;\n                }\n\n\n                var icon = Eisc.StringOutput[JoinMap.SourceIconJoinStart.JoinNumber + i].StringValue;\n                var key = Eisc.StringOutput[JoinMap.SourceKeyJoinStart.JoinNumber + i].StringValue;\n                var type = Eisc.StringOutput[JoinMap.SourceTypeJoinStart.JoinNumber + i].StringValue;\n                var disableShare = Eisc.BooleanOutput[JoinMap.SourceShareDisableJoinStart.JoinNumber + i].BoolValue;\n                var sourceEnabled = Eisc.BooleanOutput[JoinMap.SourceIsEnabledJoinStart.JoinNumber + i].BoolValue;\n                var controllable = Eisc.BooleanOutput[JoinMap.SourceIsControllableJoinStart.JoinNumber + i].BoolValue;\n                var audioSource = Eisc.BooleanOutput[JoinMap.SourceIsAudioSourceJoinStart.JoinNumber + i].BoolValue;\n\n                Debug.Console(0, this, \"Adding source {0} '{1}'\", key, name);\n\n                var sourceKey = Eisc.StringOutput[JoinMap.SourceControlDeviceKeyJoinStart.JoinNumber + i].StringValue;\n\n                var newSli = new SourceListItem\n                {\n                    Icon = icon,\n                    Name = name,\n                    Order = (int)i + 10,\n                    SourceKey = string.IsNullOrEmpty(sourceKey) ? key : sourceKey, // Use the value from the join if defined\n                    Type = eSourceListItemType.Route,\n                    DisableCodecSharing = disableShare,\n                    IncludeInSourceList = !useSourceEnabled || sourceEnabled,\n                    IsControllable = controllable,\n                    IsAudioSource = audioSource\n                };\n                newSl.Add(key, newSli);\n\n                var existingSourceDevice = co.GetDeviceForKey(newSli.SourceKey);\n\n                var syntheticDevice = GetSyntheticSourceDevice(newSli, type, i);\n\n                // Look to see if this is a device that already exists in Essentials and get it\n                if (existingSourceDevice != null)\n                {\n                    Debug.Console(0, this, \"Found device with key: {0} in Essentials.\", key);\n\n                    if (existingSourceDevice.Properties.Value<bool>(_syntheticDeviceKey))\n                    {\n                        Debug.Console(0, this, \"Updating previous device config with new values\");\n                        existingSourceDevice = syntheticDevice;\n                    }\n                    else\n                    {\n                        Debug.Console(0, this, \"Using existing Essentials device (non synthetic)\");\n                    }\n                }\n                else\n                {\n                    co.Devices.Add(syntheticDevice);\n                }\n            }\n\n            co.SourceLists.Add(\"default\", newSl);\n\n            if (Eisc.BooleanOutput[JoinMap.SupportsAdvancedSharing.JoinNumber].BoolValue)\n            {\n                if (co.DestinationLists == null)\n                {\n                    co.DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>();\n                }\n\n                CreateDestinationList(co);\n            }\n\n            // Build \"audioCodec\" config if we need\n            if (!string.IsNullOrEmpty(rmProps.AudioCodecKey))\n            {\n                var acFavs = new List<CodecActiveCallItem>();\n                for (uint i = 0; i < 4; i++)\n                {\n                    if (!Eisc.GetBool(JoinMap.SpeedDialVisibleStartJoin.JoinNumber + i))\n                    {\n                        break;\n                    }\n                    acFavs.Add(new CodecActiveCallItem\n                    {\n                        Name = Eisc.GetString(JoinMap.SpeedDialNameStartJoin.JoinNumber + i),\n                        Number = Eisc.GetString(JoinMap.SpeedDialNumberStartJoin.JoinNumber + i),\n                        Type = eCodecCallType.Audio\n                    });\n                }\n\n                var acProps = new\n                {\n                    favorites = acFavs\n                };\n\n                const string acStr = \"audioCodec\";\n                var acConf = new DeviceConfig\n                {\n                    Group = acStr,\n                    Key = acStr,\n                    Name = acStr,\n                    Type = acStr,\n                    Properties = JToken.FromObject(acProps)\n                };\n                co.Devices.Add(acConf);\n            }\n\n            // Build Video codec config\n            if (!string.IsNullOrEmpty(rmProps.VideoCodecKey))\n            {\n                // No favorites, for now?\n                var favs = new List<CodecActiveCallItem>();\n\n                // cameras\n                var camsProps = new List<object>();\n                for (uint i = 0; i < 9; i++)\n                {\n                    var name = Eisc.GetString(i + JoinMap.CameraNearNameStart.JoinNumber);\n                    if (!string.IsNullOrEmpty(name))\n                    {\n                        camsProps.Add(new\n                        {\n                            name,\n                            selector = \"camera\" + (i + 1),\n                        });\n                    }\n                }\n                var farName = Eisc.GetString(JoinMap.CameraFarName.JoinNumber);\n                if (!string.IsNullOrEmpty(farName))\n                {\n                    camsProps.Add(new\n                    {\n                        name = farName,\n                        selector = \"cameraFar\",\n                    });\n                }\n\n                var props = new\n                {\n                    favorites = favs,\n                    cameras = camsProps,\n                };\n                const string str = \"videoCodec\";\n                var conf = new DeviceConfig\n                {\n                    Group = str,\n                    Key = str,\n                    Name = str,\n                    Type = str,\n                    Properties = JToken.FromObject(props)\n                };\n                co.Devices.Add(conf);\n            }\n\n            SetupDeviceMessengers();\n\n            Debug.Console(0, this, \"******* CONFIG FROM SIMPL: \\r{0}\",\n                JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));\n\n            ConfigurationIsReady?.Invoke(this, new EventArgs());\n\n            ConfigIsLoaded = true;\n        }\n\n        private DeviceConfig GetSyntheticDestinationDevice(string key, string name)\n        {\n            // If not, synthesize the device config\n            var devConf = new DeviceConfig\n            {\n                Group = \"genericdestination\",\n                Key = key,\n                Name = name,\n                Type = \"genericdestination\",\n                Properties = new JObject(new JProperty(_syntheticDeviceKey, true)),\n            };\n\n            return devConf;\n        }\n\n        private void CreateDestinationList(BasicConfig co)\n        {\n            var useDestEnable = Eisc.BooleanOutput[JoinMap.UseDestinationEnable.JoinNumber].BoolValue;\n\n            var newDl = new Dictionary<string, DestinationListItem>();\n\n            for (uint i = 0; i < SupportedDisplayCount; i++)\n            {\n                var name = Eisc.StringOutput[JoinMap.DestinationNameJoinStart.JoinNumber + i].StringValue;\n                var routeType = Eisc.StringOutput[JoinMap.DestinationTypeJoinStart.JoinNumber + i].StringValue;\n                var key = Eisc.StringOutput[JoinMap.DestinationDeviceKeyJoinStart.JoinNumber + i].StringValue;\n                //var order = Eisc.UShortOutput[JoinMap.DestinationOrderJoinStart.JoinNumber + i].UShortValue;\n                var enabled = Eisc.BooleanOutput[JoinMap.DestinationIsEnabledJoinStart.JoinNumber + i].BoolValue;\n\n                if (useDestEnable && !enabled)\n                {\n                    continue;\n                }\n\n                if (string.IsNullOrEmpty(key))\n                {\n                    continue;\n                }\n\n                Debug.Console(0, this, \"Adding destination {0} - {1}\", key, name);\n\n                eRoutingSignalType parsedType;\n                try\n                {\n                    parsedType = (eRoutingSignalType)Enum.Parse(typeof(eRoutingSignalType), routeType, true);\n                }\n                catch\n                {\n                    Debug.Console(0, this, \"Error parsing destination type: {0}\", routeType);\n                    parsedType = eRoutingSignalType.AudioVideo;\n                }\n\n                var newDli = new DestinationListItem\n                {\n                    Name = name,\n                    Order = (int)i,\n                    SinkKey = key,\n                    SinkType = parsedType,\n                };\n\n                if (!newDl.ContainsKey(key))\n                {\n                    newDl.Add(key, newDli);\n                }\n                else\n                {\n                    newDl[key] = newDli;\n                }\n\n                if (!_directRouteMessenger.DestinationList.ContainsKey(newDli.SinkKey))\n                {\n                    //add same DestinationListItem to dictionary for messenger in order to allow for correlation by index\n                    _directRouteMessenger.DestinationList.Add(key, newDli);\n                }\n                else\n                {\n                    _directRouteMessenger.DestinationList[key] = newDli;\n                }\n\n                var existingDev = co.GetDeviceForKey(key);\n\n                var syntheticDisplay = GetSyntheticDestinationDevice(key, name);\n\n                if (existingDev != null)\n                {\n                    Debug.Console(0, this, \"Found device with key: {0} in Essentials.\", key);\n\n                    if (existingDev.Properties.Value<bool>(_syntheticDeviceKey))\n                    {\n                        Debug.Console(0, this, \"Updating previous device config with new values\");\n                    }\n                    else\n                    {\n                        Debug.Console(0, this, \"Using existing Essentials device (non synthetic)\");\n                    }\n                }\n                else\n                {\n                    co.Devices.Add(syntheticDisplay);\n                }\n            }\n\n            if (!co.DestinationLists.ContainsKey(\"default\"))\n            {\n                co.DestinationLists.Add(\"default\", newDl);\n            }\n            else\n            {\n                co.DestinationLists[\"default\"] = newDl;\n            }\n\n            _directRouteMessenger.RegisterForDestinationPaths();\n        }\n\n        /// <summary>\n        /// Iterates device config and adds messengers as neede for each device type\n        /// </summary>\n        private void SetupDeviceMessengers()\n        {\n            DeviceMessengers = new Dictionary<string, MessengerBase>();\n\n            try\n            {\n                foreach (var device in ConfigReader.ConfigObject.Devices)\n                {\n                    if (device.Group.Equals(\"simplmessenger\"))\n                    {\n                        var props =\n                            JsonConvert.DeserializeObject<SimplMessengerPropertiesConfig>(device.Properties.ToString());\n\n                        var messengerKey = string.Format(\"device-{0}-{1}\", Key, Key);\n\n                        if (DeviceManager.GetDeviceForKey(messengerKey) != null)\n                        {\n                            Debug.Console(2, this, \"Messenger with key: {0} already exists. Skipping...\", messengerKey);\n                            continue;\n                        }\n\n                        var dev = ConfigReader.ConfigObject.GetDeviceForKey(props.DeviceKey);\n\n                        if (dev == null)\n                        {\n                            Debug.Console(1, this, \"Unable to find device config for key: '{0}'\", props.DeviceKey);\n                            continue;\n                        }\n\n                        var type = device.Type.ToLower();\n                        MessengerBase messenger = null;\n\n                        if (type.Equals(\"simplcameramessenger\"))\n                        {\n                            Debug.Console(2, this, \"Adding SIMPLCameraMessenger for: '{0}'\", props.DeviceKey);\n                            messenger = new SIMPLCameraMessenger(messengerKey, Eisc, \"/device/\" + props.DeviceKey,\n                                props.JoinStart);\n                        }\n                        else if (type.Equals(\"simplroutemessenger\"))\n                        {\n                            Debug.Console(2, this, \"Adding SIMPLRouteMessenger for: '{0}'\", props.DeviceKey);\n                            messenger = new SIMPLRouteMessenger(messengerKey, Eisc, \"/device/\" + props.DeviceKey,\n                                props.JoinStart);\n                        }\n\n                        if (messenger != null)\n                        {\n                            DeviceManager.AddDevice(messenger);\n                            DeviceMessengers.Add(device.Key, messenger);\n                            messenger.RegisterWithAppServer(Parent);\n                        }\n                        else\n                        {\n                            Debug.Console(2, this, \"Unable to add messenger for device: '{0}' of type: '{1}'\",\n                                props.DeviceKey, type);\n                        }\n                    }\n                    else\n                    {\n                        var dev = DeviceManager.GetDeviceForKey(device.Key);\n\n                        if (dev != null)\n                        {\n                            if (dev is CameraBase)\n                            {\n                                var camDevice = dev as CameraBase;\n                                Debug.Console(1, this, \"Adding CameraBaseMessenger for device: {0}\", dev.Key);\n                                var cameraMessenger = new CameraBaseMessenger(device.Key + \"-\" + Key, camDevice,\n                                    \"/device/\" + device.Key);\n                                DeviceMessengers.Add(device.Key, cameraMessenger);\n                                DeviceManager.AddDevice(cameraMessenger);\n                                cameraMessenger.RegisterWithAppServer(Parent);\n                                continue;\n                            }\n                        }\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                Debug.Console(2, this, \"Error Setting up Device Managers: {0}\", e);\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void SendFullStatus()\n        {\n            if (ConfigIsLoaded)\n            {\n                var count = Eisc.UShortOutput[JoinMap.NumberOfAuxFaders.JoinNumber].UShortValue;\n\n                Debug.Console(1, this, \"The Fader Count is : {0}\", count);\n\n                // build volumes object, serialize and put in content of method below\n\n                // Create auxFaders\n                var auxFaderDict = new Dictionary<string, Volume>();\n\n                var volumeStart = JoinMap.VolumeJoinStart.JoinNumber;\n\n                for (var i = volumeStart; i <= count; i++)\n                {\n                    auxFaderDict.Add(\"level-\" + i,\n                        new Volume(\"level-\" + i,\n                            Eisc.UShortOutput[i].UShortValue,\n                            Eisc.BooleanOutput[i].BoolValue,\n                            Eisc.StringOutput[i].StringValue,\n                            true,\n                            \"someting.png\"));\n                }\n\n                var volumes = new Volumes\n                {\n                    Master = new Volume(\"master\",\n                        Eisc.UShortOutput[JoinMap.MasterVolume.JoinNumber].UShortValue,\n                        Eisc.BooleanOutput[JoinMap.MasterVolume.JoinNumber].BoolValue,\n                        Eisc.StringOutput[JoinMap.MasterVolume.JoinNumber].StringValue,\n                        true,\n                        \"something.png\")\n                    {\n                        HasPrivacyMute = true,\n                        PrivacyMuted = Eisc.BooleanOutput[JoinMap.PrivacyMute.JoinNumber].BoolValue\n                    },\n                    AuxFaders = auxFaderDict,\n                    NumberOfAuxFaders = Eisc.UShortInput[JoinMap.NumberOfAuxFaders.JoinNumber].UShortValue\n                };\n\n                // TODO: Add property to status message to indicate if advanced sharing is supported and if users can change share mode\n\n                PostStatus(new\n                {\n                    activityMode = GetActivityMode(),\n                    isOn = Eisc.BooleanOutput[JoinMap.RoomIsOn.JoinNumber].BoolValue,\n                    selectedSourceKey = Eisc.StringOutput[JoinMap.CurrentSourceKey.JoinNumber].StringValue,\n                    volumes,\n                    supportsAdvancedSharing = Eisc.BooleanOutput[JoinMap.SupportsAdvancedSharing.JoinNumber].BoolValue,\n                    userCanChangeShareMode = Eisc.BooleanOutput[JoinMap.UserCanChangeShareMode.JoinNumber].BoolValue,\n                });\n            }\n            else\n            {\n                PostStatus(new\n                {\n                    error = \"systemNotReady\"\n                });\n            }\n        }\n\n        /// <summary>\n        /// Returns the activity mode int\n        /// </summary>\n        /// <returns></returns>\n        private int GetActivityMode()\n        {\n            if (Eisc.BooleanOutput[JoinMap.ActivityPhoneCall.JoinNumber].BoolValue) return 2;\n            if (Eisc.BooleanOutput[JoinMap.ActivityShare.JoinNumber].BoolValue) return 1;\n\n            return Eisc.BooleanOutput[JoinMap.ActivityVideoCall.JoinNumber].BoolValue ? 3 : 0;\n        }\n\n        /// <summary>\n        /// Helper for posting status message\n        /// </summary>\n        /// <param name=\"contentObject\">The contents of the content object</param>\n        private void PostStatus(object contentObject)\n        {\n            AppServerController.SendMessageObject(new MobileControlMessage\n            {\n                Type = \"/status/\",\n                Content = JToken.FromObject(contentObject)\n            });\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"messageType\"></param>\n        /// <param name=\"contentObject\"></param>\n        private void PostMessage(string messageType, object contentObject)\n        {\n            AppServerController.SendMessageObject(new MobileControlMessage\n            {\n                Type = messageType,\n                Content = JToken.FromObject(contentObject)\n            });\n        }\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"currentDevice\"></param>\n        /// <param name=\"args\"></param>\n        private void EISC_SigChange(object currentDevice, SigEventArgs args)\n        {\n            if (Debug.Level >= 1)\n                Debug.Console(1, this, \"SIMPL EISC change: {0} {1}={2}\", args.Sig.Type, args.Sig.Number,\n                    args.Sig.StringValue);\n            var uo = args.Sig.UserObject;\n            if (uo != null)\n            {\n                if (uo is Action<bool>)\n                    (uo as Action<bool>)(args.Sig.BoolValue);\n                else if (uo is Action<ushort>)\n                    (uo as Action<ushort>)(args.Sig.UShortValue);\n                else if (uo is Action<string>)\n                    (uo as Action<string>)(args.Sig.StringValue);\n            }\n        }\n\n        /// <summary>\n        /// Returns the mapping of types to groups, for setting up devices.\n        /// </summary>\n        /// <returns></returns>\n        private Dictionary<string, string> GetSourceGroupDictionary()\n        {\n            //type, group\n            var d = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)\n            {\n                {\"laptop\", \"pc\"},\n                {\"pc\", \"pc\"},\n                {\"wireless\", \"genericsource\"},\n                {\"iptv\", \"settopbox\"},\n                {\"simplcameramessenger\", \"simplmessenger\"},\n                {\"camera\", \"camera\"},\n\n            };\n            return d;\n        }\n\n        /// <summary>\n        /// updates the usercode from server\n        /// </summary>\n        protected override void UserCodeChange()\n        {\n\n            Debug.Console(1, this, \"Server user code changed: {0}\", UserCode);\n\n            var qrUrl = string.Format(\"{0}/api/rooms/{1}/{3}/qr?x={2}\", AppServerController.Host, AppServerController.SystemUuid, new Random().Next(), \"room1\");\n            QrCodeUrl = qrUrl;\n\n            Debug.Console(1, this, \"Server user code changed: {0} - {1}\", UserCode, qrUrl);\n\n            OnUserCodeChanged();\n\n            Eisc.StringInput[JoinMap.UserCodeToSystem.JoinNumber].StringValue = UserCode;\n            Eisc.StringInput[JoinMap.ServerUrl.JoinNumber].StringValue = McServerUrl;\n            Eisc.StringInput[JoinMap.QrCodeUrl.JoinNumber].StringValue = QrCodeUrl;\n\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/RoomBridges/SourceDeviceMapDictionary.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a SourceDeviceMapDictionary\n    /// </summary>\n    public class SourceDeviceMapDictionary : Dictionary<string, uint>\n    {\n        public SourceDeviceMapDictionary()\n        {\n            var dictionary = new Dictionary<string, uint>\n            {\n                {\"preset01\", 101},\n                {\"preset02\", 102},\n                {\"preset03\", 103},\n                {\"preset04\", 104},\n                {\"preset05\", 105},\n                {\"preset06\", 106},\n                {\"preset07\", 107},\n                {\"preset08\", 108},\n                {\"preset09\", 109},\n                {\"preset10\", 110},\n                {\"preset11\", 111},\n                {\"preset12\", 112},\n                {\"preset13\", 113},\n                {\"preset14\", 114},\n                {\"preset15\", 115},\n                {\"preset16\", 116},\n                {\"preset17\", 117},\n                {\"preset18\", 118},\n                {\"preset19\", 119},\n                {\"preset20\", 120},\n                {\"preset21\", 121},\n                {\"preset22\", 122},\n                {\"preset23\", 123},\n                {\"preset24\", 124},\n                {\"num0\", 130},\n                {\"num1\", 131},\n                {\"num2\", 132},\n                {\"num3\", 133},\n                {\"num4\", 134},\n                {\"num5\", 135},\n                {\"num6\", 136},\n                {\"num7\", 137},\n                {\"num8\", 138},\n                {\"num9\", 139},\n                {\"numDash\", 140},\n                {\"numEnter\", 141},\n                {\"chanUp\", 142},\n                {\"chanDown\", 143},\n                {\"lastChan\", 144},\n                {\"exit\", 145},\n                {\"powerToggle\", 146},\n                {\"red\", 147},\n                {\"green\", 148},\n                {\"yellow\", 149},\n                {\"blue\", 150},\n                {\"video\", 151},\n                {\"previous\", 152},\n                {\"next\", 153},\n                {\"rewind\", 154},\n                {\"ffwd\", 155},\n                {\"closedCaption\", 156},\n                {\"stop\", 157},\n                {\"pause\", 158},\n                {\"up\", 159},\n                {\"down\", 160},\n                {\"left\", 161},\n                {\"right\", 162},\n                {\"settings\", 163},\n                {\"info\", 164},\n                {\"return\", 165},\n                {\"guide\", 166},\n                {\"reboot\", 167},\n                {\"dvrList\", 168},\n                {\"replay\", 169},\n                {\"play\", 170},\n                {\"select\", 171},\n                {\"record\", 172},\n                {\"menu\", 173},\n                {\"topMenu\", 174},\n                {\"prevTrack\", 175},\n                {\"nextTrack\", 176},\n                {\"powerOn\", 177},\n                {\"powerOff\", 178},\n                {\"dot\", 179}\n            };\n\n            foreach (var item in dictionary)\n            {\n                Add(item.Key, item.Value);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Services/MobileControlApiService.cs",
    "content": "﻿using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.Services\n{\n\n    /// <summary>\n    /// Service for interacting with a Mobile Control Edge server instance\n    /// </summary>\n    public class MobileControlApiService\n    {\n        private readonly HttpClient _client;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileControlApiService\"/> class.\n        /// </summary>\n        /// <param name=\"apiUrl\">Mobile Control Edge API URL</param>\n        public MobileControlApiService(string apiUrl)\n        {\n            var handler = new HttpClientHandler\n            {\n                AllowAutoRedirect = false,\n                ServerCertificateCustomValidationCallback = (req, cert, certChain, errors) => true\n            };\n\n            _client = new HttpClient(handler);\n        }\n\n        /// <summary>\n        /// Send authorization request to Mobile Control Edge Server\n        /// </summary>\n        /// <param name=\"apiUrl\">Mobile Control Edge API URL</param>\n        /// <param name=\"grantCode\">Grant code for authorization</param>\n        /// <param name=\"systemUuid\">System UUID for authorization</param>\n        /// <returns>Authorization response</returns>\n        public async Task<AuthorizationResponse> SendAuthorizationRequest(string apiUrl, string grantCode, string systemUuid)\n        {\n            try\n            {\n                var request = new HttpRequestMessage(HttpMethod.Get, $\"{apiUrl}/system/{systemUuid}/authorize?grantCode={grantCode}\");\n\n                Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, \"Sending authorization request to {host}\", null, request.RequestUri);\n\n                var response = await _client.SendAsync(request);\n\n                var authResponse = new AuthorizationResponse\n                {\n                    Authorized = response.StatusCode == System.Net.HttpStatusCode.OK\n                };\n\n                if (authResponse.Authorized)\n                {\n                    return authResponse;\n                }\n\n                if (response.StatusCode == System.Net.HttpStatusCode.Moved)\n                {\n                    var location = response.Headers.Location;\n\n                    authResponse.Reason = $\"ERROR: Mobile Control API has moved. Please adjust configuration to \\\"{location}\\\"\";\n\n                    return authResponse;\n                }\n\n                var responseString = await response.Content.ReadAsStringAsync();\n\n                switch (responseString)\n                {\n                    case \"codeNotFound\":\n                        authResponse.Reason = $\"Authorization failed. Code not found for system UUID {systemUuid}\";\n                        break;\n                    case \"uuidNotFound\":\n                        authResponse.Reason = $\"Authorization failed. System UUID {systemUuid} not found. Check Essentials configuration.\";\n                        break;\n                    default:\n                        authResponse.Reason = $\"Authorization failed. Response {response.StatusCode}: {responseString}\";\n                        break;\n                }\n\n                return authResponse;\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Error authorizing with Mobile Control\");\n                return new AuthorizationResponse { Authorized = false, Reason = ex.Message };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Touchpanel/ITheme.cs",
    "content": "﻿using PepperDash.Core;\n\nnamespace PepperDash.Essentials.Touchpanel\n{\n    /// <summary>\n    /// Defines the contract for ITheme\n    /// </summary>\n    public interface ITheme : IKeyed\n    {\n        /// <summary>\n        /// Current theme\n        /// </summary>\n        string Theme { get; }\n\n        /// <summary>\n        /// Set the theme with the given value\n        /// </summary>\n        /// <param name=\"theme\">The theme to set</param>\n        void UpdateTheme(string theme);\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControl.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Touchpanel\n{\n    /// <summary>\n    /// Defines the contract for ITswAppControl\n    /// </summary>\n    public interface ITswAppControl : IKeyed\n    {\n        /// <summary>\n        /// Updates when the Zoom Room Control Application opens or closes\n        /// </summary>\n        BoolFeedback AppOpenFeedback { get; }\n\n        /// <summary>\n        /// Hide the Zoom App and show the User Control Application\n        /// </summary>\n        void HideOpenApp();\n\n        /// <summary>\n        /// Close the Zoom App and show the User Control Application\n        /// </summary>\n        void CloseOpenApp();\n\n        /// <summary>\n        /// Open the Zoom App\n        /// </summary>\n        void OpenApp();\n    }\n\n    /// <summary>\n    /// Defines the contract for ITswZoomControl\n    /// </summary>\n    public interface ITswZoomControl : IKeyed\n    {\n        /// <summary>\n        /// Updates when Zoom has an incoming call\n        /// </summary>\n        BoolFeedback ZoomIncomingCallFeedback { get; }\n\n        /// <summary>\n        /// Updates when Zoom is in a call\n        /// </summary>\n        BoolFeedback ZoomInCallFeedback { get; }\n\n        /// <summary>\n        /// End a Zoom Call\n        /// </summary>\n        void EndZoomCall();\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControlMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer.Messengers;\n\nnamespace PepperDash.Essentials.Touchpanel\n{\n    /// <summary>\n    /// Messenger for controlling the Zoom App on a TSW Panel that supports the Zoom Room Control Application\n    /// </summary>\n    public class ITswAppControlMessenger : MessengerBase\n    {\n        private readonly ITswAppControl _appControl;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"ITswAppControlMessenger\"/> class.\n        /// </summary>\n        /// <param name=\"key\">The key for this messenger</param>\n        /// <param name=\"messagePath\">The message path for this messenger</param>\n        /// <param name=\"device\">The device for this messenger</param>\n        public ITswAppControlMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)\n        {\n            _appControl = device as ITswAppControl;\n        }\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n        {\n            if (_appControl == null)\n            {\n                this.LogInformation(\"{deviceKey} does not implement ITswAppControl\", _device.Key);\n                return;\n            }\n\n            AddAction($\"/fullStatus\", (id, context) => SendFullStatus(id));\n\n            AddAction($\"/openApp\", (id, context) => _appControl.OpenApp());\n\n            AddAction($\"/closeApp\", (id, context) => _appControl.CloseOpenApp());\n\n            AddAction($\"/hideApp\", (id, context) => _appControl.HideOpenApp());\n\n            _appControl.AppOpenFeedback.OutputChange += (s, a) =>\n            {\n                PostStatusMessage(JToken.FromObject(new\n                {\n                    appOpen = a.BoolValue\n                }));\n            };\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var message = new TswAppStateMessage\n            {\n                AppOpen = _appControl.AppOpenFeedback.BoolValue,\n            };\n\n            PostStatusMessage(message, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a TswAppStateMessage\n    /// </summary>\n    public class TswAppStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// True if the Zoom app is open on a TSW panel\n        /// </summary>\n        [JsonProperty(\"appOpen\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? AppOpen { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Touchpanel/ITswZoomControlMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer.Messengers;\n\n\nnamespace PepperDash.Essentials.Touchpanel\n{\n    /// <summary>\n    /// Messenger to handle Zoom status and control for a TSW panel that supports the Zoom Application\n    /// </summary>\n    public class ITswZoomControlMessenger : MessengerBase\n    {\n        private readonly ITswZoomControl _zoomControl;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"ITswZoomControlMessenger\"/> class for the given device\n        /// </summary>\n        /// <param name=\"key\">The key for this messenger</param>\n        /// <param name=\"messagePath\">The message path for this messenger</param>\n        /// <param name=\"device\">The device for this messenger</param>\n        public ITswZoomControlMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)\n        {\n            _zoomControl = device as ITswZoomControl;\n        }\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n        {\n            if (_zoomControl == null)\n            {\n                this.LogInformation(\"{deviceKey} does not implement ITswZoomControl\", _device.Key);\n                return;\n            }\n\n            AddAction($\"/fullStatus\", (id, context) => SendFullStatus(id));\n\n            AddAction($\"/zoomStatus\", (id, content) => SendFullStatus(id));\n\n\n            AddAction($\"/endCall\", (id, context) => _zoomControl.EndZoomCall());\n\n            _zoomControl.ZoomIncomingCallFeedback.OutputChange += (s, a) =>\n            {\n                PostStatusMessage(JToken.FromObject(new\n                {\n                    incomingCall = a.BoolValue,\n                    inCall = _zoomControl.ZoomInCallFeedback.BoolValue\n                }));\n            };\n\n\n            _zoomControl.ZoomInCallFeedback.OutputChange += (s, a) =>\n            {\n                PostStatusMessage(JToken.FromObject(\n                new\n                {\n                    inCall = a.BoolValue,\n                    incomingCall = _zoomControl.ZoomIncomingCallFeedback.BoolValue\n                }));\n            };\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var message = new TswZoomStateMessage\n            {\n                InCall = _zoomControl?.ZoomInCallFeedback.BoolValue,\n                IncomingCall = _zoomControl?.ZoomIncomingCallFeedback.BoolValue\n            };\n\n            PostStatusMessage(message, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a TswZoomStateMessage\n    /// </summary>\n    public class TswZoomStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// True if the panel is in a Zoom call\n        /// </summary>\n        [JsonProperty(\"inCall\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? InCall { get; set; }\n\n        /// <summary>\n        /// True if there is an incoming Zoom call\n        /// </summary>\n\n        [JsonProperty(\"incomingCall\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IncomingCall { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing System.Text.RegularExpressions;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharpPro;\nusing Crestron.SimplSharpPro.DeviceSupport;\nusing Crestron.SimplSharpPro.UI;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.Core.DeviceInfo;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Core.UI;\nusing Feedback = PepperDash.Essentials.Core.Feedback;\nusing IPAddress = System.Net.IPAddress;\n\nnamespace PepperDash.Essentials.Touchpanel\n{\n    /// <summary>\n    /// Mobile Control touchpanel controller that provides app control, Zoom integration, \n    /// and mobile control functionality for Crestron touchpanels.\n    /// </summary>\n    public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlCrestronTouchpanelController, ITheme\n    {\n        private readonly MobileControlTouchpanelProperties localConfig;\n        private IMobileControlRoomMessenger _bridge;\n\n        private string _appUrl;\n\n        /// <summary>\n        /// Gets or sets the AppUrlFeedback\n        /// </summary>\n        public StringFeedback AppUrlFeedback { get; private set; }\n\n        private readonly StringFeedback QrCodeUrlFeedback;\n        private readonly StringFeedback McServerUrlFeedback;\n        private readonly StringFeedback UserCodeFeedback;\n\n        private readonly BoolFeedback _appOpenFeedback;\n\n        /// <summary>\n        /// Gets feedback indicating whether an application is currently open on the touchpanel.\n        /// </summary>\n        public BoolFeedback AppOpenFeedback => _appOpenFeedback;\n\n        private readonly BoolFeedback _zoomIncomingCallFeedback;\n\n        /// <summary>\n        /// Gets feedback indicating whether there is an incoming Zoom call.\n        /// </summary>\n        public BoolFeedback ZoomIncomingCallFeedback => _zoomIncomingCallFeedback;\n\n        private readonly BoolFeedback _zoomInCallFeedback;\n\n        /// <summary>\n        /// Event that is raised when device information changes.\n        /// </summary>\n        public event DeviceInfoChangeHandler DeviceInfoChanged;\n\n        /// <summary>\n        /// Gets feedback indicating whether a Zoom call is currently active.\n        /// </summary>\n        public BoolFeedback ZoomInCallFeedback => _zoomInCallFeedback;\n\n        /// <summary>\n        /// Gets or sets the Feedbacks\n        /// </summary>\n        public FeedbackCollection<Feedback> Feedbacks { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the ZoomFeedbacks\n        /// </summary>\n        public FeedbackCollection<Feedback> ZoomFeedbacks { get; private set; }\n\n        /// <summary>\n        /// Gets the default room key for this touchpanel controller.\n        /// </summary>\n        public string DefaultRoomKey => _config.DefaultRoomKey;\n\n        /// <summary>\n        /// Gets a value indicating whether to use direct server communication.\n        /// </summary>\n        public bool UseDirectServer => localConfig.UseDirectServer;\n\n        /// <summary>\n        /// Gets a value indicating whether this touchpanel acts as a Zoom Room controller.\n        /// </summary>\n        public bool ZoomRoomController => localConfig.ZoomRoomController;\n\n        /// <summary>\n        /// Gets the current theme for the touchpanel interface.\n        /// </summary>\n        public string Theme => localConfig.Theme;\n\n        /// <summary>\n        /// Gets or sets the ThemeFeedback\n        /// </summary>\n        public StringFeedback ThemeFeedback { get; private set; }\n\n        /// <summary>\n        /// Gets device information including MAC address and IP address.\n        /// </summary>\n        public DeviceInfo DeviceInfo => new DeviceInfo();\n\n        /// <summary>\n        /// Gets the list of connected IPs for this IpId\n        /// </summary>\n        public ReadOnlyCollection<ConnectedIpInformation> ConnectedIps => Panel.ConnectedIpList;\n\n        private readonly IPAddress csIpAddress;\n\n        private readonly IPAddress csSubnetMask;\n\n\n        /// <summary>\n        /// Initializes a new instance of the MobileControlTouchpanelController class.\n        /// </summary>\n        /// <param name=\"key\">The unique key identifier for this touchpanel controller.</param>\n        /// <param name=\"name\">The friendly name for this touchpanel controller.</param>\n        /// <param name=\"panel\">The touchpanel hardware device.</param>\n        /// <param name=\"config\">The configuration properties for this controller.</param>\n        public MobileControlTouchpanelController(string key, string name, BasicTriListWithSmartObject panel, MobileControlTouchpanelProperties config) : base(key, name, panel, config)\n        {\n            localConfig = config;\n\n            AddPostActivationAction(SubscribeForMobileControlUpdates);\n\n            ThemeFeedback = new StringFeedback($\"{Key}-theme\", () => Theme);\n            AppUrlFeedback = new StringFeedback($\"{Key}-appUrl\", () => _appUrl);\n            QrCodeUrlFeedback = new StringFeedback($\"{Key}-qrCodeUrl\", () => _bridge?.QrCodeUrl);\n            McServerUrlFeedback = new StringFeedback($\"{Key}-mcServerUrl\", () => _bridge?.McServerUrl);\n            UserCodeFeedback = new StringFeedback($\"{Key}-userCode\", () => _bridge?.UserCode);\n\n            _appOpenFeedback = new BoolFeedback($\"{Key}-appOpen\", () =>\n            {\n                if (Panel is TswX60BaseClass tsX60)\n                {\n                    Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $\"x60 sending {tsX60.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.BoolValue}\");\n                    return !tsX60.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.BoolValue;\n                }\n\n                if (Panel is TswX70Base tsX70)\n                {\n                    Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $\"x70 sending {tsX70.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue}\");\n                    return !tsX70.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue;\n                }\n\n                return false;\n            });\n\n            _zoomIncomingCallFeedback = new BoolFeedback($\"{Key}-zoomIncomingCall\", () =>\n            {\n                if (Panel is TswX60WithZoomRoomAppReservedSigs tsX60)\n                {\n                    return tsX60.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.BoolValue;\n                }\n\n                if (Panel is TswX70Base tsX70)\n                {\n                    return tsX70.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.BoolValue;\n                }\n\n                return false;\n            });\n\n            _zoomInCallFeedback = new BoolFeedback($\"{Key}-zoomInCall\", () =>\n            {\n                if (Panel is TswX60WithZoomRoomAppReservedSigs tsX60)\n                {\n                    return tsX60.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.BoolValue;\n                }\n\n                if (Panel is TswX70Base tsX70)\n                {\n                    return tsX70.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.BoolValue;\n                }\n\n                return false;\n            });\n\n            Feedbacks = new FeedbackCollection<Feedback>\n            {\n                AppUrlFeedback, QrCodeUrlFeedback, McServerUrlFeedback, UserCodeFeedback\n            };\n\n            ZoomFeedbacks = new FeedbackCollection<Feedback> {\n                AppOpenFeedback, _zoomInCallFeedback, _zoomIncomingCallFeedback\n            };\n\n            RegisterForExtenders();\n\n            try\n            {\n                var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);\n                var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId);\n                var csIpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);\n\n                this.csSubnetMask = IPAddress.Parse(csSubnetMask);\n                this.csIpAddress = IPAddress.Parse(csIpAddress);\n            }\n            catch (ArgumentException)\n            {\n                Debug.LogInformation(\"This processor does not have a CS LAN\", this);\n            }\n            catch (InvalidOperationException)\n            {\n                Debug.LogInformation(\"This processor does not have a CS LAN\", this);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError($\"Unexpected exception when checking CS LAN: {ex}\", this);\n            }\n        }\n\n        /// <summary>\n        /// Updates the theme setting for this touchpanel controller and persists the change to configuration.\n        /// </summary>\n        /// <param name=\"theme\">The new theme identifier to apply.</param>\n        /// <summary>\n        /// UpdateTheme method\n        /// </summary>\n        public void UpdateTheme(string theme)\n        {\n            localConfig.Theme = theme;\n\n            var props = JToken.FromObject(localConfig);\n\n            var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault((d) => d.Key == Key);\n\n            if (deviceConfig == null) { return; }\n\n            deviceConfig.Properties = props;\n\n            ConfigWriter.UpdateDeviceConfig(deviceConfig);\n        }\n\n        private void RegisterForExtenders()\n        {\n            if (Panel is TswXX70Base x70Panel)\n            {\n                x70Panel.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) =>\n                {\n                    this.LogVerbose(\"X70 App Control Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}\", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);\n\n                    UpdateZoomFeedbacks();\n\n                    if (!x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue)\n                    {\n                        x70Panel.ExtenderButtonToolbarReservedSigs.ShowButtonToolbar();\n\n                        x70Panel.ExtenderButtonToolbarReservedSigs.Button2On();\n                    }\n                    else\n                    {\n                        x70Panel.ExtenderButtonToolbarReservedSigs.HideButtonToolbar();\n                        x70Panel.ExtenderButtonToolbarReservedSigs.Button2Off();\n                    }\n                };\n\n\n                x70Panel.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) =>\n                {\n                    this.LogVerbose(\"X70 Zoom Room App Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}\", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);\n\n                    if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)\n                    {\n                        ZoomIncomingCallFeedback.FireUpdate();\n                    }\n                    else if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.Number)\n                    {\n                        ZoomInCallFeedback.FireUpdate();\n                    }\n                };\n\n\n                x70Panel.ExtenderEthernetReservedSigs.DeviceExtenderSigChange += (e, a) =>\n                {\n                    DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;\n                    DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;\n\n                    this.LogDebug(\"MAC: {macAddress} IP: {ipAddress}\", DeviceInfo.MacAddress, DeviceInfo.IpAddress);\n\n                    var handler = DeviceInfoChanged;\n\n                    if (handler == null)\n                    {\n                        return;\n                    }\n\n                    handler(this, new DeviceInfoEventArgs(DeviceInfo));\n                };\n\n                x70Panel.ExtenderButtonToolbarReservedSigs.DeviceExtenderSigChange += (o, a) =>\n                {\n                    this.LogVerbose(\"X70 Button Toolbar Device Extender args: {event}:{sig}:{name}:{type}:{boolValue}:{ushortValue}:{stringValue}\", a.Event, a.Sig, a.Sig.Name, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);\n                };\n\n                x70Panel.ExtenderApplicationControlReservedSigs.Use();\n                x70Panel.ExtenderZoomRoomAppReservedSigs.Use();\n                x70Panel.ExtenderEthernetReservedSigs.Use();\n                x70Panel.ExtenderButtonToolbarReservedSigs.Use();\n\n                return;\n            }\n\n            if (Panel is TswX60WithZoomRoomAppReservedSigs x60withZoomApp)\n            {\n                x60withZoomApp.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) =>\n                {\n                    this.LogVerbose(\"X60 App Control Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}\", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);\n\n                    if (a.Sig.Number == x60withZoomApp.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.Number)\n                    {\n                        AppOpenFeedback.FireUpdate();\n                    }\n                };\n                x60withZoomApp.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) =>\n                {\n                    this.LogVerbose(\"X60 Zoom Room App Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}\", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);\n\n                    if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)\n                    {\n                        ZoomIncomingCallFeedback.FireUpdate();\n                    }\n                    else if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.Number)\n                    {\n                        ZoomInCallFeedback.FireUpdate();\n                    }\n                };\n\n                x60withZoomApp.ExtenderEthernetReservedSigs.DeviceExtenderSigChange += (e, a) =>\n                {\n                    DeviceInfo.MacAddress = x60withZoomApp.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;\n                    DeviceInfo.IpAddress = x60withZoomApp.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;\n\n                    this.LogDebug(\"MAC: {macAddress} IP: {ipAddress}\", DeviceInfo.MacAddress, DeviceInfo.IpAddress);\n\n                    var handler = DeviceInfoChanged;\n\n                    if (handler == null)\n                    {\n                        return;\n                    }\n\n                    handler(this, new DeviceInfoEventArgs(DeviceInfo));\n                };\n\n                x60withZoomApp.ExtenderZoomRoomAppReservedSigs.Use();\n                x60withZoomApp.ExtenderApplicationControlReservedSigs.Use();\n                x60withZoomApp.ExtenderEthernetReservedSigs.Use();\n            }\n        }\n\n        /// <summary>\n        /// Performs custom activation setup for the touchpanel controller, including \n        /// registering messengers and linking to mobile control.\n        /// </summary>\n        /// <returns>True if activation was successful; otherwise, false.</returns>\n        /// <summary>\n        /// CustomActivate method\n        /// </summary>\n        public override bool CustomActivate()\n        {\n            var appMessenger = new ITswAppControlMessenger($\"appControlMessenger-{Key}\", $\"/device/{Key}\", this);\n\n            var zoomMessenger = new ITswZoomControlMessenger($\"zoomControlMessenger-{Key}\", $\"/device/{Key}\", this);\n\n            var themeMessenger = new ThemeMessenger($\"themeMessenger-{Key}\", $\"/device/{Key}\", this);\n\n            var mc = DeviceManager.AllDevices.OfType<IMobileControl>().FirstOrDefault();\n\n            if (mc == null)\n            {\n                return base.CustomActivate();\n            }\n\n            if (!(Panel is TswXX70Base) && !(Panel is TswX60WithZoomRoomAppReservedSigs))\n            {\n                mc.AddDeviceMessenger(themeMessenger);\n\n                return base.CustomActivate();\n            }\n\n            mc.AddDeviceMessenger(appMessenger);\n            mc.AddDeviceMessenger(zoomMessenger);\n            mc.AddDeviceMessenger(themeMessenger);\n\n            return base.CustomActivate();\n        }\n\n        /// <summary>\n        /// Handles device extender signal changes for system reserved signals.\n        /// </summary>\n        /// <param name=\"currentDeviceExtender\">The device extender that generated the signal change.</param>\n        /// <param name=\"args\">The signal event arguments containing the changed signal information.</param>\n        protected override void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)\n        {\n            this.LogVerbose(\"System Device Extender args: {event}:{sig}\", args.Event, args.Sig);\n        }\n\n        /// <summary>\n        /// Sets up the panel drivers and signal mappings for the specified room.\n        /// </summary>\n        /// <param name=\"roomKey\">The room key to configure the panel drivers for.</param>\n        protected override void SetupPanelDrivers(string roomKey)\n        {\n            AppUrlFeedback.LinkInputSig(Panel.StringInput[1]);\n            QrCodeUrlFeedback.LinkInputSig(Panel.StringInput[2]);\n            McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]);\n            UserCodeFeedback.LinkInputSig(Panel.StringInput[4]);\n\n            Panel.IpInformationChange -= Panel_IpInformationChange;\n            Panel.IpInformationChange += Panel_IpInformationChange;\n\n            Panel.OnlineStatusChange -= Panel_OnlineChange;\n            Panel.OnlineStatusChange += Panel_OnlineChange;\n        }\n\n        private void Panel_OnlineChange(GenericBase sender, OnlineOfflineEventArgs args)\n        {\n            try\n            {\n                if (!args.DeviceOnLine)\n                {\n                    this.LogInformation(\"panel is offline\");\n                    return;\n                }\n\n                this.LogDebug(\"panel is online\");\n\n                UpdateFeedbacks();\n                Panel.StringInput[1].StringValue = _appUrl;\n                Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue;\n                Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue;\n                Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue;\n\n                if (Panel is TswXX70Base x70Panel)\n                {\n                    this.LogDebug(\"setting buttons off\");\n\n                    x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();\n                    x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();\n                    x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();\n                    x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();\n                    x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();\n                }\n\n                SendUrlToPanel();\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Exception in panel online: {message}\", ex.Message);\n                this.LogDebug(ex, \"Stack Trace: \");\n            }\n        }\n\n        private void SendUrlToPanel()\n        {\n            var appUrl = GetUrlWithCorrectIp(_appUrl);\n\n            this.LogInformation(\"Sending {appUrl} on join 1\", AppUrlFeedback.StringValue);\n\n            if (Panel.StringInput[1].StringValue == appUrl)\n            {\n                this.LogInformation(\"App URL already set to {appUrl}, no update needed\", AppUrlFeedback.StringValue);\n                return;\n            }\n\n            Panel.StringInput[1].StringValue = appUrl;\n\n            SetAppUrl(appUrl);\n        }\n\n        private void Panel_IpInformationChange(GenericBase sender, ConnectedIpEventArgs args)\n        {\n            if (args.Connected)\n            {\n                this.LogVerbose(\"Connection from IP: {ip}\", args.DeviceIpAddress);\n                SendUrlToPanel();\n            }\n            else\n            {\n                this.LogVerbose(\"Disconnection from IP: {ip}\", args.DeviceIpAddress);\n            }\n        }\n\n        /// <summary>\n        /// Gets the URL with the correct IP address based on the connected devices and the Crestron processor's IP address.\n        /// </summary>\n        /// <param name=\"url\"></param>\n        /// <returns></returns>\n        private string GetUrlWithCorrectIp(string url)\n        {\n            var lanAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);\n\n            var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);\n\n            if (csIpAddress == null || csSubnetMask == null || url == null)\n            {\n                this.LogWarning(\"CS IP Address Subnet Mask or url is null, cannot determine correct IP for URL\");\n                return url;\n            }\n\n            this.LogVerbose(\"Processor IP: {processorIp}, CS IP: {csIpAddress}, CS Subnet Mask: {csSubnetMask}\", processorIp, csIpAddress, csSubnetMask);\n            this.LogVerbose(\"Connected IP Count: {connectedIps}\", ConnectedIps.Count);\n\n            var ip = ConnectedIps.Any(ipInfo =>\n            {\n                if (IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp))\n                {\n                    return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask);\n                }\n                this.LogWarning(\"Invalid IP address: {deviceIpAddress}\", ipInfo.DeviceIpAddress);\n                return false;\n            }) ? csIpAddress.ToString() : processorIp;\n\n            var match = Regex.Match(url, @\"^http://([^:/]+):\\d+/mc/app\\?token=.+$\");\n            if (match.Success)\n            {\n                string ipa = match.Groups[1].Value;\n                // ip will be \"192.168.1.100\"\n            }\n\n            // replace ipa with ip but leave the rest of the string intact\n            var updatedUrl = Regex.Replace(url, @\"^http://[^:/]+\", $\"http://{ip}\");\n\n            this.LogVerbose(\"Updated URL: {updatedUrl}\", updatedUrl);\n\n            return updatedUrl;\n        }\n\n        private void SubscribeForMobileControlUpdates()\n        {\n            foreach (var dev in DeviceManager.AllDevices)\n            {\n                Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $\"{dev.Key}:{dev.GetType().Name}\");\n            }\n\n            var mcList = DeviceManager.AllDevices.OfType<MobileControlSystemController>().ToList();\n\n            if (mcList.Count == 0)\n            {\n                this.LogError(\"No Mobile Control controller found\");\n\n                return;\n            }\n\n            // use first in list, since there should only be one.\n            var mc = mcList[0];\n\n            var bridge = mc.GetRoomBridge(_config.DefaultRoomKey);\n\n            if (bridge == null)\n            {\n                this.LogInformation(\"No Mobile Control bridge for {roomKey} found\", _config.DefaultRoomKey);\n                return;\n            }\n\n            _bridge = bridge;\n\n            _bridge.UserCodeChanged += UpdateFeedbacks;\n            _bridge.AppUrlChanged += (s, a) =>\n            {\n                this.LogInformation(\"AppURL changed: {appURL}\", _bridge.AppUrl);\n                SetAppUrl(_bridge.AppUrl);\n                UpdateFeedbacks(s, a);\n            };\n\n            SetAppUrl(_bridge.AppUrl);\n        }\n\n        /// <summary>\n        /// Sets the application URL and updates the corresponding feedback.\n        /// </summary>\n        /// <param name=\"url\">The new application URL to set.</param>\n        /// <summary>\n        /// SetAppUrl method\n        /// </summary>\n        public void SetAppUrl(string url)\n        {\n            _appUrl = GetUrlWithCorrectIp(url);\n\n            AppUrlFeedback.FireUpdate();\n        }\n\n        private void UpdateFeedbacks(object sender, EventArgs args)\n        {\n            UpdateFeedbacks();\n        }\n\n        private void UpdateFeedbacks()\n        {\n            foreach (var feedback in Feedbacks) { this.LogDebug(\"Updating {feedbackKey}\", feedback.Key); feedback.FireUpdate(); }\n        }\n\n        private void UpdateZoomFeedbacks()\n        {\n            foreach (var feedback in ZoomFeedbacks)\n            {\n                this.LogDebug(\"Updating {feedbackKey}\", feedback.Key);\n                feedback.FireUpdate();\n            }\n        }\n\n        /// <summary>\n        /// HideOpenApp method\n        /// </summary>\n        public void HideOpenApp()\n        {\n            if (Panel is TswX70Base x70Panel)\n            {\n                x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplication();\n                return;\n            }\n\n            if (Panel is TswX60BaseClass x60Panel)\n            {\n                x60Panel.ExtenderApplicationControlReservedSigs.HideOpenApplication();\n                return;\n            }\n        }\n\n        /// <summary>\n        /// OpenApp method\n        /// </summary>\n        public void OpenApp()\n        {\n            if (Panel is TswX70Base x70Panel)\n            {\n                x70Panel.ExtenderApplicationControlReservedSigs.OpenApplication();\n                return;\n            }\n\n            if (Panel is TswX60WithZoomRoomAppReservedSigs)\n            {\n                this.LogInformation(\"X60 panel does not support zoom app\");\n                return;\n            }\n        }\n\n        /// <summary>\n        /// CloseOpenApp method\n        /// </summary>\n        public void CloseOpenApp()\n        {\n            if (Panel is TswX70Base x70Panel)\n            {\n                x70Panel.ExtenderApplicationControlReservedSigs.CloseOpenedApplication();\n                return;\n            }\n\n            if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)\n            {\n                x60Panel.ExtenderApplicationControlReservedSigs.CloseOpenedApplication();\n                return;\n            }\n        }\n\n        /// <summary>\n        /// EndZoomCall method\n        /// </summary>\n        public void EndZoomCall()\n        {\n            if (Panel is TswX70Base x70Panel)\n            {\n                x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomEndCall();\n                return;\n            }\n\n            if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)\n            {\n                x60Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomEndCall();\n                return;\n            }\n        }\n\n        /// <summary>\n        /// UpdateDeviceInfo method\n        /// </summary>\n        public void UpdateDeviceInfo()\n        {\n            if (Panel is TswXX70Base x70Panel)\n            {\n                DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;\n                DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;\n\n                var handler = DeviceInfoChanged;\n\n                if (handler == null)\n                {\n                    return;\n                }\n\n                handler(this, new DeviceInfoEventArgs(DeviceInfo));\n            }\n\n            if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)\n            {\n                DeviceInfo.MacAddress = x60Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;\n                DeviceInfo.IpAddress = x60Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;\n\n                var handler = DeviceInfoChanged;\n\n                if (handler == null)\n                {\n                    return;\n                }\n\n                handler(this, new DeviceInfoEventArgs(DeviceInfo));\n            }\n\n            this.LogDebug(\"MAC: {macAddress} IP: {ipAddress}\", DeviceInfo.MacAddress, DeviceInfo.IpAddress);\n        }\n\n        /// <summary>\n        /// Force a reload of the iframe on the panel connected to this IP ID\n        /// </summary>\n        public void ReloadIframe()\n        {\n            this.LogInformation(\"Pulsing join 1\");\n            Panel.PulseBool(1, 100);\n        }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlTouchpanelControllerFactory\n    /// </summary>\n    public class MobileControlTouchpanelControllerFactory : EssentialsPluginDeviceFactory<MobileControlTouchpanelController>\n    {\n        private Dictionary<string, Func<uint, CrestronControlSystem, string, BasicTriListWithSmartObject>> factories;\n\n        /// <summary>\n        /// Initializes a new instance of the MobileControlTouchpanelControllerFactory class.\n        /// Sets up supported device type names and minimum framework version requirements.\n        /// </summary>\n        public MobileControlTouchpanelControllerFactory()\n        {\n            TypeNames = new List<string>() { \"mccrestronapp\", \"mctsw550\", \"mctsw750\", \"mctsw1050\", \"mctsw560\", \"mctsw760\", \"mctsw1060\", \"mctsw570\", \"mctsw770\", \"mcts770\", \"mctsw1070\", \"mcts1070\", \"mcxpanel\", \"mcdge1000\" };\n            MinimumEssentialsFrameworkVersion = \"2.0.0\";\n\n            factories = new Dictionary<string, Func<uint, CrestronControlSystem, string, BasicTriListWithSmartObject>>\n            {\n                {\"crestronapp\", (id, controlSystem, projectName) => {\n                    var app = new CrestronApp(id, Global.ControlSystem);\n                    app.ParameterProjectName.Value = projectName;\n                    return app;\n                }},\n                {\"xpanel\", (id, controlSystem, projectName) => new XpanelForHtml5(id, controlSystem)},\n                {\"tsw550\", (id, controlSystem, projectName) => new Tsw550(id, controlSystem)},\n                {\"tsw552\", (id, controlSystem, projectName) => new Tsw552(id, controlSystem)},\n                {\"tsw560\", (id, controlSystem, projectName) => new Tsw560(id, controlSystem)},\n                {\"tsw750\", (id, controlSystem, projectName) => new Tsw750(id, controlSystem)},\n                {\"tsw752\", (id, controlSystem, projectName) => new Tsw752(id, controlSystem)},\n                {\"tsw760\", (id, controlSystem, projectName) => new Tsw760(id, controlSystem)},\n                {\"tsw1050\", (id, controlSystem, projectName) => new Tsw1050(id, controlSystem)},\n                {\"tsw1052\", (id, controlSystem, projectName) => new Tsw1052(id, controlSystem)},\n                {\"tsw1060\", (id, controlSystem, projectName) => new Tsw1060(id, controlSystem)},\n                {\"tsw570\", (id, controlSystem, projectName) => new Tsw570(id, controlSystem)},\n                {\"tsw770\", (id, controlSystem, projectName) => new Tsw770(id, controlSystem)},\n                {\"ts770\", (id, controlSystem, projectName) => new Ts770(id, controlSystem)},\n                {\"tsw1070\", (id, controlSystem, projectName) => new Tsw1070(id, controlSystem)},\n                {\"ts1070\", (id, controlSystem, projectName) => new Ts1070(id, controlSystem)},\n                {\"dge1000\", (id, controlSystem, projectName) => new Dge1000(id, controlSystem)}\n            };\n        }\n\n        /// <summary>\n        /// Builds a MobileControlTouchpanelController device from the provided device configuration.\n        /// </summary>\n        /// <param name=\"dc\">The device configuration containing the device properties and settings.</param>\n        /// <returns>A configured MobileControlTouchpanelController instance.</returns>\n        /// <summary>\n        /// BuildDevice method\n        /// </summary>\n        public override EssentialsDevice BuildDevice(DeviceConfig dc)\n        {\n            var comm = CommFactory.GetControlPropertiesConfig(dc);\n            var props = JsonConvert.DeserializeObject<MobileControlTouchpanelProperties>(dc.Properties.ToString());\n\n            var panel = GetPanelForType(dc.Type, comm.IpIdInt, props.ProjectName);\n\n            if (panel == null)\n            {\n                Debug.LogError(\"Unable to create Touchpanel for type {type}. Touchpanel Controller WILL NOT function correctly\", dc.Type);\n            }\n\n            Debug.LogDebug(\"Factory Attempting to create new MobileControlTouchpanelController\");\n\n            var panelController = new MobileControlTouchpanelController(dc.Key, dc.Name, panel, props);\n\n            return panelController;\n        }\n\n        private BasicTriListWithSmartObject GetPanelForType(string type, uint id, string projectName)\n        {\n            type = type.ToLower().Replace(\"mc\", \"\");\n\n            try\n            {\n                if (!factories.TryGetValue(type, out var buildCrestronHardwareDevice))\n                {\n                    Debug.LogError(\"Cannot create TSW controller with type {type}\", type);\n                    return null;\n                }\n\n                return buildCrestronHardwareDevice(id, Global.ControlSystem, projectName);\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(\"Cannot create TSW base class. Panel will not function: {message}\", e.Message);\n                Debug.LogDebug(e, \"Stack Trace: \");\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelProperties.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Touchpanel\n{\n    /// <summary>\n    /// Represents a MobileControlTouchpanelProperties\n    /// </summary>\n    public class MobileControlTouchpanelProperties : CrestronTouchpanelPropertiesConfig\n    {\n\n        /// <summary>\n        /// Gets or sets the UseDirectServer\n        /// </summary>\n        [JsonProperty(\"useDirectServer\")]\n        public bool UseDirectServer { get; set; } = false;\n\n\n        /// <summary>\n        /// Gets or sets the ZoomRoomController\n        /// </summary>\n        [JsonProperty(\"zoomRoomController\")]\n        public bool ZoomRoomController { get; set; } = false;\n\n\n        /// <summary>\n        /// Gets or sets the ButtonToolbarTimoutInS\n        /// </summary>\n        [JsonProperty(\"buttonToolbarTimeoutInS\")]\n        public ushort ButtonToolbarTimoutInS { get; set; } = 0;\n\n\n        /// <summary>\n        /// Gets or sets the Theme\n        /// </summary>\n        [JsonProperty(\"theme\")]\n        public string Theme { get; set; } = \"light\";\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer;\nusing PepperDash.Essentials.AppServer.Messengers;\n\nnamespace PepperDash.Essentials.Touchpanel\n{\n    /// <summary>\n    /// Messenger to save the current theme (light/dark) and send to a device\n    /// </summary>\n    public class ThemeMessenger : MessengerBase\n    {\n        private readonly ITheme _tpDevice;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"ThemeMessenger\"/> class\n        /// </summary>\n        /// <param name=\"key\">The key for this messenger</param>\n        /// <param name=\"path\">The path for this messenger</param>\n        /// <param name=\"device\">The device for this messenger</param>\n        public ThemeMessenger(string key, string path, ITheme device) : base(key, path, device as Device)\n        {\n            _tpDevice = device;\n        }\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n        {\n            AddAction(\"/fullStatus\", (id, content) =>\n            {\n                PostStatusMessage(new ThemeUpdateMessage { Theme = _tpDevice.Theme }, id);\n            });\n\n            AddAction(\"/saveTheme\", (id, content) =>\n            {\n                var theme = content.ToObject<MobileControlSimpleContent<string>>();\n\n                this.LogInformation(\"Setting theme to {theme}\", theme.Value);\n                _tpDevice.UpdateTheme(theme.Value);\n\n                PostStatusMessage(JToken.FromObject(new { theme = theme.Value }), clientId: id);\n            });\n        }\n    }\n\n    /// <summary>\n    /// Represents a ThemeUpdateMessage\n    /// </summary>\n    public class ThemeUpdateMessage : DeviceStateMessageBase\n    {\n\n        /// <summary>\n        /// Gets or sets the Theme\n        /// </summary>\n        [JsonProperty(\"theme\")]\n        public string Theme { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/TransmitMessage.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core.Queues;\nusing WebSocketSharp;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Represents a TransmitMessage\n    /// </summary>\n    public class TransmitMessage : IQueueMessage\n    {\n        private readonly WebSocket _ws;\n        private readonly object msgToSend;\n\n        /// <summary>\n        /// Initialize a message to send\n        /// </summary>\n        /// <param name=\"msg\">message object to send</param>\n        /// <param name=\"ws\">WebSocket instance</param>\n        public TransmitMessage(object msg, WebSocket ws)\n        {\n            _ws = ws;\n            msgToSend = msg;\n        }\n\n        /// <summary>\n        /// Initialize a message to send\n        /// </summary>\n        /// <param name=\"msg\">message object to send</param>\n        /// <param name=\"ws\">WebSocket instance</param>\n        public TransmitMessage(DeviceStateMessageBase msg, WebSocket ws)\n        {\n            _ws = ws;\n            msgToSend = msg;\n        }\n\n        #region Implementation of IQueueMessage\n\n        /// <summary>\n        /// Dispatch method\n        /// </summary>\n        public void Dispatch()\n        {\n            try\n            {\n                if (_ws == null)\n                {\n                    Debug.LogWarning(\"Cannot send message.  Websocket client is null\");\n                    return;\n                }\n\n                if (!_ws.IsAlive)\n                {\n                    Debug.LogWarning(\"Cannot send message.  Websocket client is not connected\");\n                    return;\n                }\n\n\n                var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,\n                    new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });\n\n                Debug.LogVerbose(\"Message TX: {0}\", message);\n\n                _ws.Send(message);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogError(\"Caught an exception in the Transmit Processor: {message}\", ex.Message);\n                Debug.LogDebug(ex, \"Stack Trace: \");\n            }\n        }\n        #endregion\n    }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials\n{\n  /// <summary>\n  /// Defines the action to take when the User code changes\n  /// </summary>\n  public class UserCodeChanged\n  {\n    /// <summary>\n    /// Action to take when the User Code changes\n    /// </summary>\n    public Action<string, string> UpdateUserCode { get; private set; }\n\n    /// <summary>\n    /// create an instance of the <see cref=\"UserCodeChanged\"/> class\n    /// </summary>\n    /// <param name=\"updateMethod\">action to take when the User Code changes</param>\n    public UserCodeChanged(Action<string, string> updateMethod)\n    {\n      UpdateUserCode = updateMethod;\n    }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/UserCodeChangedContent.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Represents a UserCodeChangedContent\n    /// </summary>\n    public class UserCodeChangedContent\n    {\n\n        /// <summary>\n        /// Gets or sets the UserCode\n        /// </summary>\n        [JsonProperty(\"userCode\")]\n        public string UserCode { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the QrChecksum\n        /// </summary>\n        [JsonProperty(\"qrChecksum\", NullValueHandling = NullValueHandling.Include)]\n        public string QrChecksum { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Utilities.cs",
    "content": "using System.Threading;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing WebSocketSharp;\n\nnamespace PepperDash.Essentials\n{\n  /// <summary>\n  /// Utility functions for logging and other common tasks.\n  /// </summary>\n  public static class Utilities\n  {\n    private static int nextClientId = 0;\n\n    /// <summary>\n    /// Get the next unique client ID (thread-safe)\n    /// </summary>\n    /// <returns>Client ID</returns>\n    public static int GetNextClientId()\n    {\n      return Interlocked.Increment(ref nextClientId);\n    }\n    /// <summary>\n    /// Converts a WebSocketServer LogData object to Essentials logging calls.\n    /// </summary>\n    /// <param name=\"data\">The LogData object to convert.</param>\n    /// <param name=\"message\">The log message.</param>\n    /// <param name=\"device\">The device associated with the log message.</param>\n    public static void ConvertWebsocketLog(LogData data, string message, IKeyed device = null)\n    {\n\n      switch (data.Level)\n      {\n        case LogLevel.Trace:\n          if (device == null)\n          {\n            Debug.LogVerbose(message);\n          }\n          else\n          {\n            device.LogVerbose(message);\n          }\n          break;\n        case LogLevel.Debug:\n          if (device == null)\n          {\n            Debug.LogDebug(message);\n          }\n          else\n          {\n            device.LogDebug(message);\n          }\n          break;\n        case LogLevel.Info:\n          if (device == null)\n          {\n            Debug.LogInformation(message);\n          }\n          else\n          {\n            device.LogInformation(message);\n          }\n          break;\n        case LogLevel.Warn:\n          if (device == null)\n          {\n            Debug.LogWarning(message);\n          }\n          else\n          {\n            device.LogWarning(message);\n          }\n          break;\n        case LogLevel.Error:\n          if (device == null)\n          {\n            Debug.LogError(message);\n          }\n          else\n          {\n            device.LogError(message);\n          }\n          break;\n        case LogLevel.Fatal:\n          if (device == null)\n          {\n            Debug.LogFatal(message);\n          }\n          else\n          {\n            device.LogFatal(message);\n          }\n          break;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/Volumes.cs",
    "content": "﻿using System.Collections.Generic;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials\n{\n    /// <summary>\n    /// Represents a Volumes\n    /// </summary>\n    public class Volumes\n    {\n\n        /// <summary>\n        /// Gets or sets the Master\n        /// </summary>\n        [JsonProperty(\"master\", NullValueHandling = NullValueHandling.Ignore)]\n        public Volume Master { get; set; }\n\n        /// <summary>\n        /// Aux Faders as configured in the room\n        /// </summary>\n        [JsonProperty(\"auxFaders\", NullValueHandling = NullValueHandling.Ignore)]\n        public Dictionary<string, Volume> AuxFaders { get; set; }\n\n        /// <summary>\n        /// Count of aux faders for this system\n        /// </summary>\n        [JsonProperty(\"numberOfAuxFaders\", NullValueHandling = NullValueHandling.Ignore)]\n        public int? NumberOfAuxFaders { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a Volume\n    /// </summary>\n    public class Volume\n    {\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        [JsonProperty(\"key\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Key { get; set; }\n\n        /// <summary>\n        /// Level for this volume object\n        /// </summary>\n        [JsonProperty(\"level\", NullValueHandling = NullValueHandling.Ignore)]\n        public int? Level { get; set; }\n\n        /// <summary>\n        /// True if this volume control is muted\n        /// </summary>\n        [JsonProperty(\"muted\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? Muted { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Label\n        /// </summary>\n        [JsonProperty(\"label\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Label { get; set; }\n\n        /// <summary>\n        /// True if this volume object has mute control\n        /// </summary>\n        [JsonProperty(\"hasMute\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasMute { get; set; }\n\n        /// <summary>\n        /// True if this volume object has Privacy mute control\n        /// </summary>\n        [JsonProperty(\"hasPrivacyMute\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasPrivacyMute { get; set; }\n\n        /// <summary>\n        /// True if the privacy mute is muted\n        /// </summary>\n        [JsonProperty(\"privacyMuted\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? PrivacyMuted { get; set; }\n\n\n\n        /// <summary>\n        /// Gets or sets the MuteIcon\n        /// </summary>\n        [JsonProperty(\"muteIcon\", NullValueHandling = NullValueHandling.Ignore)]\n        public string MuteIcon { get; set; }\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"Volume\" /> class\n        /// </summary>\n        /// <param name=\"key\">The key for this volume object</param>\n        /// <param name=\"level\">The level for this volume object</param>\n        /// <param name=\"muted\">True if this volume control is muted</param>\n        /// <param name=\"label\">The label for this volume object</param>\n        /// <param name=\"hasMute\">True if this volume object has mute control</param>\n        /// <param name=\"muteIcon\">The mute icon for this volume object</param>\n        public Volume(string key, int level, bool muted, string label, bool hasMute, string muteIcon)\n            : this(key)\n        {\n            Level = level;\n            Muted = muted;\n            Label = label;\n            HasMute = hasMute;\n            MuteIcon = muteIcon;\n        }\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"Volume\" /> class\n        /// </summary>\n        /// <param name=\"key\">The key for this volume object</param>\n        /// <param name=\"level\">The level for this volume object</param>\n        public Volume(string key, int level)\n            : this(key)\n        {\n            Level = level;\n        }\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"Volume\" /> class\n        /// </summary>\n        /// <param name=\"key\">The key for this volume object</param>\n        /// <param name=\"muted\">True if this volume control is muted</param>\n        public Volume(string key, bool muted)\n            : this(key)\n        {\n            Muted = muted;\n        }\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"Volume\" /> class\n        /// </summary>\n        /// <param name=\"key\">The key for this volume object</param>\n        public Volume(string key)\n        {\n            Key = key;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\n\nnamespace PepperDash.Essentials.WebApiHandlers\n{\n    /// <summary>\n    /// Represents a ActionPathsHandler\n    /// </summary>\n    public class ActionPathsHandler : WebApiBaseRequestHandler\n    {\n        private readonly MobileControlSystemController mcController;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"ActionPathsHandler\"/> class.\n        /// </summary>\n        /// <param name=\"controller\"></param>\n        public ActionPathsHandler(MobileControlSystemController controller) : base(true)\n        {\n            mcController = controller;\n        }\n\n        /// <summary>\n        /// Handle a request to get the action paths\n        /// </summary>\n        /// <param name=\"context\">Request Context</param>\n        protected override void HandleGet(HttpCwsContext context)\n        {\n            var response = JsonConvert.SerializeObject(new ActionPathsResponse(mcController));\n\n            context.Response.StatusCode = 200;\n            context.Response.ContentType = \"application/json\";\n            context.Response.Headers.Add(\"Content-Type\", \"application/json\");\n            context.Response.Write(response, false);\n            context.Response.End();\n        }\n    }\n\n    /// <summary>\n    /// Represents a ActionPathsResponse\n    /// </summary>\n    public class ActionPathsResponse\n    {\n        [JsonIgnore]\n        private readonly MobileControlSystemController mcController;\n\n        /// <summary>\n        /// Registered action paths for this system\n        /// </summary>\n        [JsonProperty(\"actionPaths\")]\n        public List<ActionPath> ActionPaths => mcController.GetActionDictionaryPaths().Select((path) => new ActionPath { MessengerKey = path.Item1, Path = path.Item2 }).ToList();\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"ActionPathsResponse\"/> class.\n        /// </summary>\n        /// <param name=\"mcController\"></param>\n        public ActionPathsResponse(MobileControlSystemController mcController)\n        {\n            this.mcController = mcController;\n        }\n    }\n\n    /// <summary>\n    /// Represents a ActionPath\n    /// </summary>\n    public class ActionPath\n    {\n\n        /// <summary>\n        /// Gets or sets the MessengerKey\n        /// </summary>\n        [JsonProperty(\"messengerKey\")]\n        public string MessengerKey { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Path\n        /// </summary>\n        [JsonProperty(\"path\")]\n        public string Path { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebApiHandlers/DeleteAllUiClientsHandler.cs",
    "content": "using Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core.Web.RequestHandlers;\nusing PepperDash.Essentials.WebSocketServer;\n\n\nnamespace PepperDash.Essentials.WebApiHandlers\n{\n    /// <summary>\n    /// Represents a DeleteAllUiClientsHandler\n    /// </summary>\n    public class DeleteAllUiClientsHandler : WebApiBaseRequestHandler\n    {\n        private readonly MobileControlWebsocketServer server;\n\n        /// <summary>\n        /// Essentials CWS API handler for the MC Direct Server\n        /// </summary>\n        /// <param name=\"directServer\">Direct Server instance</param>\n        public DeleteAllUiClientsHandler(MobileControlWebsocketServer directServer) : base(true)\n        {\n            server = directServer;\n        }\n\n        /// <summary>\n        /// Deletes all clients from the Direct Server\n        /// </summary>\n        /// <param name=\"context\">HTTP Context for this request</param>\n        protected override void HandleDelete(HttpCwsContext context)\n        {\n            server.RemoveAllTokens(\"confirm\");\n\n            var res = context.Response;\n            res.StatusCode = 200;\n            res.ContentType = \"application/json\";\n            res.Headers.Add(\"Content-Type\", \"application/json\");\n            res.Write(JsonConvert.SerializeObject(new { success = true }), false);\n            res.End();\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileAuthRequestHandler.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\nusing PepperDash.Essentials.Core.Web;\n\nnamespace PepperDash.Essentials.WebApiHandlers\n{\n    /// <summary>\n    /// Represents a MobileAuthRequestHandler\n    /// </summary>\n    public class MobileAuthRequestHandler : WebApiBaseRequestAsyncHandler\n    {\n        private readonly MobileControlSystemController mcController;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileAuthRequestHandler\"/> class.\n        /// </summary>\n        /// <param name=\"controller\"></param>\n        public MobileAuthRequestHandler(MobileControlSystemController controller) : base(true)\n        {\n            mcController = controller;\n        }\n\n        /// <summary>\n        /// Handle authorization request for this processor\n        /// </summary>\n        /// <param name=\"context\">request context</param>\n        /// <returns>Task</returns>\n        protected override async Task HandlePost(HttpCwsContext context)\n        {\n            try\n            {\n                var requestBody = EssentialsWebApiHelpers.GetRequestBody(context.Request);\n\n                var grantCode = JsonConvert.DeserializeObject<AuthorizationRequest>(requestBody);\n\n                if (string.IsNullOrEmpty(grantCode?.GrantCode))\n                {\n                    Debug.LogMessage(Serilog.Events.LogEventLevel.Error, \"Missing grant code\");\n                    context.Response.StatusCode = 400;\n                    context.Response.StatusDescription = \"Missing grant code\";\n                    context.Response.End();\n                    return;\n                }\n\n                var response = await mcController.ApiService.SendAuthorizationRequest(mcController.Host, grantCode.GrantCode, mcController.SystemUuid);\n\n                Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, $\"response received\");\n                if (response.Authorized)\n                {\n                    mcController.RegisterSystemToServer();\n                }\n\n\n                context.Response.StatusCode = 200;\n                var responseBody = JsonConvert.SerializeObject(response, Formatting.None);\n                context.Response.ContentType = \"application/json\";\n                context.Response.Headers.Add(\"Content-Type\", \"application/json\");\n                context.Response.Write(responseBody, false);\n                context.Response.End();\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Exception recieved authorizing system\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\nusing PepperDash.Essentials.Core.Config;\nusing PepperDash.Essentials.WebSocketServer;\n\nnamespace PepperDash.Essentials.WebApiHandlers\n{\n    /// <summary>\n    /// Represents a MobileInfoHandler. Used with the Essentials CWS API\n    /// </summary>\n    public class MobileInfoHandler : WebApiBaseRequestHandler\n    {\n        private readonly MobileControlSystemController mcController;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileInfoHandler\"/> class.\n        /// </summary>\n        /// <param name=\"controller\"></param>\n        public MobileInfoHandler(MobileControlSystemController controller) : base(true)\n        {\n            mcController = controller;\n        }\n\n        /// <summary>\n        /// Get Mobile Control Information\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected override void HandleGet(HttpCwsContext context)\n        {\n            try\n            {\n                var response = new InformationResponse(mcController);\n\n                context.Response.StatusCode = 200;\n                context.Response.ContentType = \"application/json\";\n                context.Response.Write(JsonConvert.SerializeObject(response), false);\n                context.Response.End();\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"exception showing mobile info\");\n\n                context.Response.StatusCode = 500;\n                context.Response.End();\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a InformationResponse\n    /// </summary>\n    public class InformationResponse\n    {\n        [JsonIgnore]\n        private readonly MobileControlSystemController mcController;\n\n        /// <summary>\n        /// Edge Server. Null if edge server is disabled\n        /// </summary>\n        [JsonProperty(\"edgeServer\", NullValueHandling = NullValueHandling.Ignore)]\n        public MobileControlEdgeServer EdgeServer => mcController.Config.EnableApiServer ? new MobileControlEdgeServer(mcController) : null;\n\n        /// <summary>\n        /// Direct server. Null if the direct server is disabled\n        /// </summary>\n        [JsonProperty(\"directServer\", NullValueHandling = NullValueHandling.Ignore)]\n        public MobileControlDirectServer DirectServer => mcController.Config.DirectServer.EnableDirectServer ? new MobileControlDirectServer(mcController.DirectServer) : null;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"InformationResponse\"/> class.\n        /// </summary>\n        /// <param name=\"controller\"></param>\n        public InformationResponse(MobileControlSystemController controller)\n        {\n            mcController = controller;\n        }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlEdgeServer\n    /// </summary>\n    public class MobileControlEdgeServer\n    {\n        [JsonIgnore]\n        private readonly MobileControlSystemController mcController;\n\n        /// <summary>\n        /// Mobile Control Edge Server address for this system\n        /// </summary>\n        [JsonProperty(\"serverAddress\")]\n        public string ServerAddress => mcController.Config == null ? \"No Config\" : mcController.Host;\n\n        /// <summary>\n        /// System Name for this system\n        /// </summary>\n        [JsonProperty(\"systemName\")]\n        public string SystemName => mcController.RoomBridges.Count > 0 ? mcController.RoomBridges[0].RoomName : \"No Config\";\n\n        /// <summary>\n        /// System URL for this system\n        /// </summary>\n        [JsonProperty(\"systemUrl\")]\n        public string SystemUrl => ConfigReader.ConfigObject.SystemUrl;\n\n        /// <summary>\n        /// User code to use in MC UI for this system\n        /// </summary>\n        [JsonProperty(\"userCode\")]\n        public string UserCode => mcController.RoomBridges.Count > 0 ? mcController.RoomBridges[0].UserCode : \"Not available\";\n\n        /// <summary>\n        /// True if connected to edge server\n        /// </summary>\n        [JsonProperty(\"connected\")]\n        public bool Connected => mcController.Connected;\n\n        /// <summary>\n        /// Seconds since last comms with edge server\n        /// </summary>\n        [JsonProperty(\"secondsSinceLastAck\")]\n        public int SecondsSinceLastAck => (DateTime.Now - mcController.LastAckMessage).Seconds;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileControlEdgeServer\"/> class.\n        /// </summary>\n        /// <param name=\"controller\">controller to use for this</param>\n        public MobileControlEdgeServer(MobileControlSystemController controller)\n        {\n            mcController = controller;\n        }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlDirectServer\n    /// </summary>\n    public class MobileControlDirectServer\n    {\n        [JsonIgnore]\n        private readonly MobileControlWebsocketServer directServer;\n\n        /// <summary>\n        /// URL to use to interact with this server\n        /// </summary>\n        [JsonProperty(\"userAppUrl\")]\n        public string UserAppUrl => $\"{directServer.UserAppUrlPrefix}/[insert_client_token]\";\n\n        /// <summary>\n        /// TCP/IP Port this server is configured to use\n        /// </summary>\n        [JsonProperty(\"serverPort\")]\n        public int ServerPort => directServer.Port;\n\n        /// <summary>\n        /// Count of defined tokens for this server\n        /// </summary>\n        [JsonProperty(\"tokensDefined\")]\n        public int TokensDefined => directServer.UiClientContexts.Count;\n\n        /// <summary>\n        /// Count of connected clients\n        /// </summary>\n        [JsonProperty(\"clientsConnected\")]\n        public int ClientsConnected => directServer.ConnectedUiClientsCount;\n\n        /// <summary>\n        /// List of tokens and connected clients for this server\n        /// </summary>\n        [JsonProperty(\"clients\")]\n        public List<MobileControlDirectClient> Clients => directServer.UiClientContexts\n            .Select(context => (context, clients: directServer.UiClients.Where(client => client.Value.Token == context.Value.Token.Token).Select(c => c.Value).ToList()))\n            .Select((clientTuple, i) => new MobileControlDirectClient(clientTuple.clients, clientTuple.context, i, directServer.UserAppUrlPrefix))\n            .ToList();\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileControlDirectServer\"/> class.\n        /// </summary>\n        /// <param name=\"server\"></param>\n        public MobileControlDirectServer(MobileControlWebsocketServer server)\n        {\n            directServer = server;\n        }\n    }\n\n    /// <summary>\n    /// Represents a MobileControlDirectClient\n    /// </summary>\n    public class MobileControlDirectClient\n    {\n        [JsonIgnore]\n        private readonly UiClientContext context;\n\n        [JsonIgnore]\n        private readonly string Key;\n\n        [JsonIgnore]\n        private readonly int clientNumber;\n\n        [JsonIgnore]\n        private readonly string urlPrefix;\n\n        /// <summary>\n        /// Client number for this client\n        /// </summary>\n        [JsonProperty(\"clientNumber\")]\n        public string ClientNumber => $\"{clientNumber}\";\n\n        /// <summary>\n        /// Room Key for this client\n        /// </summary>\n        [JsonProperty(\"roomKey\")]\n        public string RoomKey => context.Token.RoomKey;\n\n        /// <summary>\n        /// Touchpanel Key, if defined, for this client\n        /// </summary>\n        [JsonProperty(\"touchpanelKey\")]\n        public string TouchpanelKey => context.Token.TouchpanelKey;\n\n        /// <summary>\n        /// URL for this client\n        /// </summary>\n        [JsonProperty(\"url\")]\n        public string Url => $\"{urlPrefix}{Key}\";\n\n        /// <summary>\n        /// Token for this client\n        /// </summary>\n        [JsonProperty(\"token\")]\n        public string Token => Key;\n\n        private readonly List<UiClient> clients;\n\n        /// <summary>\n        /// List of status for all connected UI Clients\n        /// </summary>\n        [JsonProperty(\"clientStatus\")]\n        public List<ClientStatus> ClientStatus => clients.Select(c => new ClientStatus(c)).ToList();\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"MobileControlDirectClient\"/> class.\n        /// </summary>\n        /// <param name=\"clients\">List of Websocket Clients</param>\n        /// <param name=\"context\">Context for the client</param>\n        /// <param name=\"index\">Index of the client</param>\n        /// <param name=\"urlPrefix\">URL prefix for the client</param>\n        public MobileControlDirectClient(List<UiClient> clients, KeyValuePair<string, UiClientContext> context, int index, string urlPrefix)\n        {\n            this.context = context.Value;\n            Key = context.Key;\n            clientNumber = index;\n            this.urlPrefix = urlPrefix;\n            this.clients = clients;\n        }\n    }\n\n    /// <summary>\n    /// Report the status of a UiClient\n    /// </summary>\n    public class ClientStatus\n    {\n        private readonly UiClient client;\n\n        /// <summary>\n        /// True if client is connected\n        /// </summary>\n        public bool Connected => client != null && client.Context.WebSocket.IsAlive;\n\n        /// <summary>\n        /// Get the time this client has been connected\n        /// </summary>\n        public double Duration => client == null ? 0 : client.ConnectedDuration.TotalSeconds;\n\n        /// <summary>\n        /// Create an instance of the <see cref=\"ClientStatus\"/> class for the specified client\n        /// </summary>\n        /// <param name=\"client\">client to report on</param>\n        public ClientStatus(UiClient client)\n        {\n            this.client = client;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs",
    "content": "﻿using Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Web.RequestHandlers;\nusing PepperDash.Essentials.Core.Web;\nusing PepperDash.Essentials.WebSocketServer;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.WebApiHandlers\n{\n    /// <summary>\n    /// Represents a UiClientHandler\n    /// </summary>\n    public class UiClientHandler : WebApiBaseRequestHandler\n    {\n        private readonly MobileControlWebsocketServer server;\n\n        /// <summary>\n        /// Essentials CWS API handler for the MC Direct Server\n        /// </summary>\n        /// <param name=\"directServer\">Direct Server instance</param>\n        public UiClientHandler(MobileControlWebsocketServer directServer) : base(true)\n        {\n            server = directServer;\n        }\n\n        /// <summary>\n        /// Create a client for the Direct Server\n        /// </summary>\n        /// <param name=\"context\">HTTP Context for this request</param>\n        protected override void HandlePost(HttpCwsContext context)\n        {\n            var req = context.Request;\n            var res = context.Response;\n            var body = EssentialsWebApiHelpers.GetRequestBody(req);\n\n            var request = JsonConvert.DeserializeObject<ClientRequest>(body);\n\n            var response = new ClientResponse();\n\n            if (string.IsNullOrEmpty(request?.RoomKey))\n            {\n                response.Error = \"roomKey is required\";\n\n                res.StatusCode = 400;\n                res.ContentType = \"application/json\";\n                res.Headers.Add(\"Content-Type\", \"application/json\");\n                res.Write(JsonConvert.SerializeObject(response), false);\n                res.End();\n                return;\n            }\n\n            if (string.IsNullOrEmpty(request.GrantCode))\n            {\n                response.Error = \"grantCode is required\";\n\n                res.StatusCode = 400;\n                res.ContentType = \"application/json\";\n                res.Headers.Add(\"Content-Type\", \"application/json\");\n                res.Write(JsonConvert.SerializeObject(response), false);\n                res.End();\n                return;\n            }\n\n            var (token, path) = server.ValidateGrantCode(request.GrantCode, request.RoomKey);\n\n            response.Token = token;\n            response.Path = path;\n\n            res.StatusCode = 200;\n            res.ContentType = \"application/json\";\n            res.Headers.Add(\"Content-Type\", \"application/json\");\n            res.Write(JsonConvert.SerializeObject(response), false);\n            res.End();\n        }\n\n        /// <summary>\n        /// Handle DELETE request for a Client\n        /// </summary>\n        /// <param name=\"context\"></param>\n        protected override void HandleDelete(HttpCwsContext context)\n        {\n            var req = context.Request;\n            var res = context.Response;\n            var body = EssentialsWebApiHelpers.GetRequestBody(req);\n\n            var request = JsonConvert.DeserializeObject<ClientRequest>(body);\n\n\n\n            if (string.IsNullOrEmpty(request?.Token))\n            {\n                var response = new ClientResponse\n                {\n                    Error = \"token is required\"\n                };\n\n                res.StatusCode = 400;\n                res.ContentType = \"application/json\";\n                res.Headers.Add(\"Content-Type\", \"application/json\");\n                res.Write(JsonConvert.SerializeObject(response), false);\n                res.End();\n\n                return;\n            }\n\n\n\n            if (!server.UiClientContexts.TryGetValue(request.Token, out UiClientContext clientContext))\n            {\n                var response = new ClientResponse\n                {\n                    Error = $\"Unable to find client with token: {request.Token}\"\n                };\n\n                res.StatusCode = 200;\n                res.ContentType = \"application/json\";\n                res.Headers.Add(\"Content-Type\", \"application/json\");\n                res.Write(JsonConvert.SerializeObject(response), false);\n                res.End();\n\n                return;\n            }\n\n            if (clientContext.Client != null && clientContext.Client.Context.WebSocket.IsAlive)\n            {\n                clientContext.Client.Context.WebSocket.Close(WebSocketSharp.CloseStatusCode.Normal, \"Token removed from server\");\n            }\n\n            var path = server.WsPath + request.Token;\n\n            if (!server.Server.RemoveWebSocketService(path))\n            {\n                Debug.LogMessage(LogEventLevel.Warning, \"Unable to remove client with token {token}\", request.Token);\n\n                var response = new ClientResponse\n                {\n                    Error = $\"Unable to remove client with token {request.Token}\"\n                };\n\n                res.StatusCode = 500;\n                res.ContentType = \"application/json\";\n                res.Headers.Add(\"Content-Type\", \"application/json\");\n                res.Write(JsonConvert.SerializeObject(response), false);\n                res.End();\n\n                return;\n            }\n\n            server.UiClientContexts.Remove(request.Token);\n\n            server.UpdateSecret();\n\n            res.StatusCode = 200;\n            res.End();\n        }\n    }\n\n    /// <summary>\n    /// Represents a ClientRequest\n    /// </summary>\n    public class ClientRequest\n    {\n\n        /// <summary>\n        /// Gets or sets the RoomKey\n        /// </summary>\n        [JsonProperty(\"roomKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string RoomKey { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the GrantCode\n        /// </summary>\n        [JsonProperty(\"grantCode\", NullValueHandling = NullValueHandling.Ignore)]\n        public string GrantCode { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Token\n        /// </summary>\n        [JsonProperty(\"token\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Token { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a ClientResponse\n    /// </summary>\n    public class ClientResponse\n    {\n\n        /// <summary>\n        /// Gets or sets the Error\n        /// </summary>\n        [JsonProperty(\"error\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Error { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Token\n        /// </summary>\n        [JsonProperty(\"token\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Token { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Path\n        /// </summary>\n        [JsonProperty(\"path\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Path { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/ConnectionClosedEventArgs.cs",
    "content": "using System;\n\nnamespace PepperDash.Essentials.WebSocketServer\n{\n  /// <summary>\n  /// Event Args for <see cref=\"UiClient\"/> ConnectionClosed event \n  /// </summary>\n  public class ConnectionClosedEventArgs : EventArgs\n  {\n    /// <summary>\n    /// Client ID that is being closed\n    /// </summary>\n    public string ClientId { get; private set; }\n\n    /// <summary>\n    /// Initialize an instance of the <see cref=\"ConnectionClosedEventArgs\"/> class.\n    /// </summary>\n    /// <param name=\"clientId\">client that's closing</param>\n    public ConnectionClosedEventArgs(string clientId)\n    {\n      ClientId = clientId;\n    }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/DeviceInterfaceInfo.cs",
    "content": "using System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\n\n\n/// <summary>\n/// Represents info about a device including supproted interfaces\n/// </summary>\npublic class DeviceInterfaceInfo : IKeyName\n{\n  /// <summary>\n  /// Gets or sets the Key\n  /// </summary>\n  [JsonProperty(\"key\")]\n  public string Key { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Name\n  /// </summary>\n  [JsonProperty(\"name\")]\n  public string Name { get; set; }\n\n  /// <summary>\n  /// Gets or sets the Interfaces\n  /// </summary>\n  [JsonProperty(\"interfaces\")]\n  public List<string> Interfaces { get; set; }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\n\n\nnamespace PepperDash.Essentials.WebSocketServer\n{\n  /// <summary>\n  /// Represents a JoinResponse\n  /// </summary>\n  public class JoinResponse\n  {\n\n    /// <summary>\n    /// Gets or sets the ClientId\n    /// </summary>\n    [JsonProperty(\"clientId\")]\n    public string ClientId { get; set; }\n\n    /// <summary>\n    /// Room Key for this client\n    /// </summary>\n    [JsonProperty(\"roomKey\")]\n    public string RoomKey { get; set; }\n\n    /// <summary>\n    /// System UUID for this system\n    /// </summary>\n    [JsonProperty(\"systemUUid\")]\n    public string SystemUuid { get; set; }\n\n\n    /// <summary>\n    /// Gets or sets the RoomUuid\n    /// </summary>\n    [JsonProperty(\"roomUUid\")]\n    public string RoomUuid { get; set; }\n\n\n    /// <summary>\n    /// Gets or sets the Config\n    /// </summary>\n    [JsonProperty(\"config\")]\n    public object Config { get; set; }\n\n\n    /// <summary>\n    /// Gets or sets the CodeExpires\n    /// </summary>\n    [JsonProperty(\"codeExpires\")]\n    public DateTime CodeExpires { get; set; }\n\n\n    /// <summary>\n    /// Gets or sets the UserCode\n    /// </summary>\n    [JsonProperty(\"userCode\")]\n    public string UserCode { get; set; }\n\n\n    /// <summary>\n    /// Gets or sets the UserAppUrl\n    /// </summary>\n    [JsonProperty(\"userAppUrl\")]\n    public string UserAppUrl { get; set; }\n\n    /// <summary>\n    /// Gets or sets the WebSocketUrl with clientId query parameter\n    /// </summary>\n    [JsonProperty(\"webSocketUrl\")]\n    public string WebSocketUrl { get; set; }\n\n\n    /// <summary>\n    /// Gets or sets the EnableDebug\n    /// </summary>\n    [JsonProperty(\"enableDebug\")]\n    public bool EnableDebug { get; set; }\n\n    /// <summary>\n    /// Gets or sets the DeviceInterfaceSupport\n    /// </summary>\n    [JsonProperty(\"deviceInterfaceSupport\")]\n    public Dictionary<string, DeviceInterfaceInfo> DeviceInterfaceSupport { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs",
    "content": "namespace PepperDash.Essentials.WebSocketServer\n{\n  /// <summary>\n  /// Represents a JoinToken\n  /// </summary>\n  public class JoinToken\n  {\n    /// <summary>\n    /// Unique client ID for a client that is joining\n    /// </summary>\n    public string Id { get; set; }\n    /// <summary>\n    /// Gets or sets the Code\n    /// </summary>\n    public string Code { get; set; }\n\n    /// <summary>\n    /// Room Key this token is associated with\n    /// </summary>\n    public string RoomKey { get; set; }\n\n    /// <summary>\n    /// Unique ID for this token\n    /// </summary>\n    public string Uuid { get; set; }\n\n    /// <summary>\n    /// Touchpanel Key this token is associated with, if this is a touch panel token\n    /// </summary>\n    public string TouchpanelKey { get; set; } = \"\";\n\n    /// <summary>\n    /// Gets or sets the Token\n    /// </summary>\n    public string Token { get; set; } = null;\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Security.Authentication;\nusing System.Security.Cryptography.X509Certificates;\nusing System.Text;\nusing Crestron.SimplSharp;\nusing Crestron.SimplSharp.WebScripting;\nusing Newtonsoft.Json;\nusing Org.BouncyCastle.Crypto.Prng;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Core.Web;\nusing PepperDash.Essentials.RoomBridges;\nusing PepperDash.Essentials.WebApiHandlers;\nusing Serilog.Events;\nusing WebSocketSharp;\nusing WebSocketSharp.Net;\nusing WebSocketSharp.Server;\n\n\nnamespace PepperDash.Essentials.WebSocketServer\n{\n    /// <summary>\n    /// Represents a MobileControlWebsocketServer\n    /// </summary>\n    public class MobileControlWebsocketServer : EssentialsDevice\n    {\n        private readonly string userAppPath = Global.FilePathPrefix + \"mcUserApp\" + Global.DirectorySeparator;\n\n        private readonly string localConfigFolderName = \"_local-config\";\n\n        private readonly string appConfigFileName = \"_config.local.json\";\n        private readonly string appConfigCsFileName = \"_config.cs.json\";\n\n        private const string certificateName = \"selfCres\";\n\n        private const string certificatePassword = \"cres12345\";\n\n        /// <summary>\n        /// Where the key is the join token and the value is the room key\n        /// </summary>\n        //private Dictionary<string, JoinToken> _joinTokens;\n\n        private HttpServer _server;\n\n        /// <summary>\n        /// Gets the HttpServer instance\n        /// </summary>\n        public HttpServer Server => _server;\n\n        /// <summary>\n        /// Gets the collection of UI client contexts\n        /// </summary>\n        public Dictionary<string, UiClientContext> UiClientContexts { get; private set; }\n\n        private readonly ConcurrentDictionary<string, UiClient> uiClients = new ConcurrentDictionary<string, UiClient>();\n\n        /// <summary>\n        /// Stores pending client registrations using composite key: token-clientId\n        /// This ensures the correct client ID is matched even when connections establish out of order\n        /// </summary>\n        private readonly ConcurrentDictionary<string, string> pendingClientRegistrations = new ConcurrentDictionary<string, string>();\n\n        /// <summary>\n        /// Stores pending client registrations with timestamp for legacy clients\n        /// Key is token, Value is list of (clientId, timestamp) tuples\n        /// Most recent registration is used to handle duplicate join requests\n        /// </summary>\n        private readonly ConcurrentDictionary<string, ConcurrentBag<(string clientId, DateTime timestamp)>> legacyClientRegistrations = new ConcurrentDictionary<string, ConcurrentBag<(string, DateTime)>>();\n\n        /// <summary>\n        /// Gets the collection of UI clients\n        /// </summary>\n        public IReadOnlyDictionary<string, UiClient> UiClients => uiClients;\n\n        private readonly MobileControlSystemController _parent;\n\n        private WebSocketServerSecretProvider _secretProvider;\n\n        private ServerTokenSecrets _secret;\n\n        private static readonly HttpClient LogClient = new HttpClient();\n\n        private string SecretProviderKey\n        {\n            get\n            {\n                return string.Format(\"{0}:{1}-tokens\", Global.ControlSystem.ProgramNumber, Key);\n            }\n        }\n\n        private string LanIpAddress => CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter));\n\n        private readonly System.Net.IPAddress csIpAddress;\n\n        private readonly System.Net.IPAddress csSubnetMask;\n\n        /// <summary>\n        /// The path for the WebSocket messaging\n        /// </summary>\n        private readonly string _wsPath = \"/mc/api/ui/join/\";\n\n        /// <summary>\n        /// Gets the WebSocket path\n        /// </summary>\n        public string WsPath => _wsPath;\n\n        /// <summary>\n        /// The path to the location of the files for the user app (single page Angular app)\n        /// </summary>\n        private readonly string _appPath = string.Format(\"{0}mcUserApp\", Global.FilePathPrefix);\n\n        /// <summary>\n        /// The base HREF that the user app uses\n        /// </summary>\n        private string _userAppBaseHref = \"/mc/app\";\n\n        /// <summary>\n        /// Gets or sets the Port\n        /// </summary>\n        public int Port { get; private set; }\n\n        /// <summary>\n        /// Gets the user app URL prefix\n        /// </summary>\n        public string UserAppUrlPrefix\n        {\n            get\n            {\n                return string.Format(\"http://{0}:{1}{2}?token=\",\n                    CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),\n                    Port,\n                    _userAppBaseHref);\n\n            }\n        }\n\n        /// <summary>\n        /// Gets the count of connected UI clients\n        /// </summary>\n        public int ConnectedUiClientsCount\n        {\n            get\n            {\n                return uiClients.Values.Where(c => c.Context.WebSocket.IsAlive).Count();\n            }\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the MobileControlWebsocketServer class.\n        /// </summary>\n        public MobileControlWebsocketServer(string key, int customPort, MobileControlSystemController parent)\n            : base(key)\n        {\n            _parent = parent;\n\n            // Set the default port to be 50000 plus the slot number of the program\n            Port = 50000 + (int)Global.ControlSystem.ProgramNumber;\n\n            if (customPort != 0)\n            {\n                Port = customPort;\n            }\n\n            if (parent.Config.DirectServer.AutomaticallyForwardPortToCSLAN == true)\n            {\n                try\n                {\n                    this.LogInformation(\"Automatically forwarding port {port} to CS LAN\", Port);\n\n                    var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);\n                    var csIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);\n\n                    var result = CrestronEthernetHelper.AddPortForwarding((ushort)Port, (ushort)Port, csIp, CrestronEthernetHelper.ePortMapTransport.TCP);\n\n                    if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr)\n                    {\n                        this.LogError(\"Error adding port forwarding: {error}\", result);\n                    }\n                }\n                catch (ArgumentException)\n                {\n                    this.LogInformation(\"This processor does not have a CS LAN\", this);\n                }\n                catch (Exception ex)\n                {\n                    this.LogError(\"Error automatically forwarding port to CS LAN: {message}\", ex.Message);\n                    this.LogDebug(ex, \"Stack Trace\");\n                }\n            }\n\n            try\n            {\n                var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);\n                var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId);\n                var csIpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);\n\n                this.csSubnetMask = System.Net.IPAddress.Parse(csSubnetMask);\n                this.csIpAddress = System.Net.IPAddress.Parse(csIpAddress);\n            }\n            catch (ArgumentException)\n            {\n                if (parent.Config.DirectServer.AutomaticallyForwardPortToCSLAN == false)\n                {\n                    this.LogInformation(\"This processor does not have a CS LAN\");\n                }\n            }\n\n\n            UiClientContexts = new Dictionary<string, UiClientContext>();\n\n            //_joinTokens = new Dictionary<string, JoinToken>();\n\n            if (Global.Platform == eDevicePlatform.Appliance)\n            {\n                AddConsoleCommands();\n            }\n\n            AddPreActivationAction(() => AddWebApiPaths());\n        }\n\n        private void AddWebApiPaths()\n        {\n            var apiServer = DeviceManager.AllDevices.OfType<EssentialsWebApi>().FirstOrDefault();\n\n            if (apiServer == null)\n            {\n                this.LogInformation(\"No API Server available\");\n                return;\n            }\n\n            var routes = new List<HttpCwsRoute>\n            {\n                new HttpCwsRoute($\"device/{Key}/client\")\n                {\n                    Name = \"ClientHandler\",\n                    RouteHandler = new UiClientHandler(this)\n                },\n\n                new HttpCwsRoute($\"device/{Key}/deleteAllUiClients\")\n                {\n                    Name = \"DeleteAllClientsHandler\",\n                    RouteHandler = new DeleteAllUiClientsHandler(this)\n                },\n            };\n\n            apiServer.AddRoute(routes);\n        }\n\n        private void AddConsoleCommands()\n        {\n            CrestronConsole.AddNewConsoleCommand(GenerateClientTokenFromConsole, \"MobileAddUiClient\", \"Adds a client and generates a token. ? for more help\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(RemoveToken, \"MobileRemoveUiClient\", \"Removes a client. ? for more help\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand((s) => PrintClientInfo(), \"MobileGetClientInfo\", \"Displays the current client info\", ConsoleAccessLevelEnum.AccessOperator);\n            CrestronConsole.AddNewConsoleCommand(RemoveAllTokens, \"MobileRemoveAllClients\", \"Removes all clients\", ConsoleAccessLevelEnum.AccessOperator);\n        }\n\n\n        /// <summary>\n        /// Initialize method\n        /// </summary>\n        /// <inheritdoc />\n        public override void Initialize()\n        {\n            try\n            {\n                base.Initialize();\n\n                _server = new HttpServer(Port, false);\n\n                _server.OnGet += Server_OnGet;\n\n                _server.OnOptions += Server_OnOptions;\n\n                if (_parent.Config.DirectServer.Logging.EnableRemoteLogging)\n                {\n                    _server.OnPost += Server_OnPost;\n                }\n\n                if (_parent.Config.DirectServer.Secure)\n                {\n                    this.LogInformation(\"Adding SSL Configuration to server\");\n                    _server.SslConfiguration = new ServerSslConfiguration(new X509Certificate2($\"\\\\user\\\\{certificateName}.pfx\", certificatePassword))\n                    {\n                        ClientCertificateRequired = false,\n                        CheckCertificateRevocation = false,\n                        EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11\n                    };\n                }\n\n                _server.Log.Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this);\n\n                // setting to trace to allow logging level to be controlled by appdebug\n                _server.Log.Level = LogLevel.Trace;\n\n                CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;\n\n                _server.Start();\n\n                if (_server.IsListening)\n                {\n                    this.LogInformation(\"Mobile Control WebSocket Server listening on port {port}\", _server.Port);\n                }\n\n                CrestronEnvironment.ProgramStatusEventHandler += OnProgramStop;\n\n                RetrieveSecret();\n\n                CreateFolderStructure();\n\n                AddClientsForTouchpanels();\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Exception initializing direct server: {message}\", ex.Message);\n                this.LogDebug(ex, \"Stack Trace\");\n            }\n        }\n\n        /// <summary>\n        /// Set the internal logging level for the Websocket Server\n        /// </summary>\n        public void SetWebsocketLogLevel(LogLevel level)\n        {\n            CrestronConsole.ConsoleCommandResponse($\"Setting direct server debug level to {level}\", level.ToString());\n            _server.Log.Level = level;\n        }\n\n        private void AddClientsForTouchpanels()\n        {\n            var touchpanels = DeviceManager.AllDevices\n                .OfType<IMobileControlTouchpanelController>().Where(tp => tp.UseDirectServer);\n\n\n            var touchpanelsToAdd = new List<IMobileControlTouchpanelController>();\n\n            if (_secret != null)\n            {\n                var newTouchpanels = touchpanels.Where(tp => !_secret.Tokens.Any(t => t.Value.TouchpanelKey != null && t.Value.TouchpanelKey.Equals(tp.Key, StringComparison.InvariantCultureIgnoreCase)));\n\n                touchpanelsToAdd.AddRange(newTouchpanels);\n            }\n            else\n            {\n                touchpanelsToAdd.AddRange(touchpanels);\n            }\n\n            foreach (var client in touchpanelsToAdd)\n            {\n                var bridge = _parent.GetRoomBridge(client.DefaultRoomKey);\n\n                if (bridge == null)\n                {\n                    this.LogWarning(\"Unable to find room with key: {defaultRoomKey}\", client.DefaultRoomKey);\n                    return;\n                }\n\n                var (key, path) = GenerateClientToken(bridge, client.Key);\n\n                if (key == null)\n                {\n                    this.LogWarning(\"Unable to generate a client for {clientKey}\", client.Key);\n                    continue;\n                }\n            }\n\n            var lanAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);\n\n            var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);\n\n            foreach (var touchpanel in touchpanels.Select(tp =>\n            {\n                var token = _secret.Tokens.FirstOrDefault((t) => t.Value.TouchpanelKey.Equals(tp.Key, StringComparison.InvariantCultureIgnoreCase));\n\n                var messenger = _parent.GetRoomBridge(tp.DefaultRoomKey);\n\n                return new { token.Key, Touchpanel = tp, Messenger = messenger };\n            }))\n            {\n                if (touchpanel.Key == null)\n                {\n                    this.LogWarning(\"Token for touchpanel {touchpanelKey} not found\", touchpanel.Touchpanel.Key);\n                    continue;\n                }\n\n                if (touchpanel.Messenger == null)\n                {\n                    this.LogWarning(\"Unable to find room messenger for {defaultRoomKey}\", touchpanel.Touchpanel.DefaultRoomKey);\n                    continue;\n                }\n\n                string ip = processorIp;\n\n                if (_parent.Config.DirectServer.CSLanUiDeviceKeys != null && _parent.Config.DirectServer.CSLanUiDeviceKeys.Any(k => k.Equals(touchpanel.Touchpanel.Key, StringComparison.InvariantCultureIgnoreCase)) && csIpAddress != null)\n                {\n                    ip = csIpAddress.ToString();\n                }\n\n                var appUrl = $\"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}\";\n\n                this.LogVerbose(\"Sending URL {appUrl} to touchpanel {touchpanelKey}\", appUrl, touchpanel.Touchpanel.Key);\n\n                touchpanel.Touchpanel.SetAppUrl($\"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}\");\n            }\n        }\n\n        private void OnProgramStop(eProgramStatusEventType programEventType)\n        {\n            switch (programEventType)\n            {\n                case eProgramStatusEventType.Stopping:\n                    _server.Stop();\n                    break;\n            }\n        }\n\n        private void CreateFolderStructure()\n        {\n            if (!Directory.Exists(userAppPath))\n            {\n                Directory.CreateDirectory(userAppPath);\n            }\n\n            if (!Directory.Exists($\"{userAppPath}{localConfigFolderName}\"))\n            {\n                Directory.CreateDirectory($\"{userAppPath}{localConfigFolderName}\");\n            }\n\n            using (var sw = new StreamWriter(File.Open($\"{userAppPath}{localConfigFolderName}{Global.DirectorySeparator}{appConfigFileName}\", FileMode.Create, FileAccess.ReadWrite)))\n            {\n                // Write the LAN application configuration file. Used when a request comes in for the application config from the LAN \n                var lanAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);\n\n                this.LogDebug(\"LAN Adapter ID: {lanAdapterId}\", lanAdapterId);\n\n                var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);\n\n                var config = GetApplicationConfig(processorIp);\n\n                var contents = JsonConvert.SerializeObject(config, Formatting.Indented);\n\n                sw.Write(contents);\n            }\n\n            short csAdapterId;\n            try\n            {\n                csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);\n            }\n            catch (ArgumentException)\n            {\n                this.LogDebug(\"This processor does not have a CS LAN\");\n                return;\n            }\n\n            if (csAdapterId == -1)\n            {\n                this.LogDebug(\"CS LAN Adapter not found\");\n                return;\n            }\n\n            this.LogDebug(\"CS LAN Adapter ID: {csAdapterId}. Adding CS Config\", csAdapterId);\n\n            using (var sw = new StreamWriter(File.Open($\"{userAppPath}{localConfigFolderName}{Global.DirectorySeparator}{appConfigCsFileName}\", FileMode.Create, FileAccess.ReadWrite)))\n            {\n                // Write the CS application configuration file. Used when a request comes in for the application config from the CS\n                var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);\n\n                var config = GetApplicationConfig(processorIp);\n\n                var contents = JsonConvert.SerializeObject(config, Formatting.Indented);\n\n                sw.Write(contents);\n            }\n        }\n\n        private MobileControlApplicationConfig GetApplicationConfig(string processorIp)\n        {\n            try\n            {\n                var config = new MobileControlApplicationConfig\n                {\n                    ApiPath = string.Format(\"http://{0}:{1}/mc/api\", processorIp, _parent.Config.DirectServer.Port),\n                    GatewayAppPath = \"\",\n                    LogoPath = _parent.Config.ApplicationConfig?.LogoPath ?? \"logo/logo.png\",\n                    EnableDev = _parent.Config.ApplicationConfig?.EnableDev ?? false,\n                    IconSet = _parent.Config.ApplicationConfig?.IconSet ?? MCIconSet.GOOGLE,\n                    LoginMode = _parent.Config.ApplicationConfig?.LoginMode ?? \"room-list\",\n                    Modes = _parent.Config.ApplicationConfig?.Modes ?? new Dictionary<string, McMode>\n                    {\n                        {\n                            \"room-list\",\n                            new McMode {\n                                ListPageText = \"Please select your room\",\n                                LoginHelpText = \"Please select your room from the list, then enter the code shown on the display.\",\n                                PasscodePageText = \"Please enter the code shown on this room's display\"\n                            }\n                        }\n                    },\n                    Logging = _parent.Config.ApplicationConfig?.Logging ?? false,\n                    PartnerMetadata = _parent.Config.ApplicationConfig?.PartnerMetadata ?? new List<MobileControlPartnerMetadata>()\n                };\n\n                return config;\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Error getting application configuration: {message}\", ex.Message);\n                this.LogDebug(ex, \"Stack Trace\");\n\n                return null;\n            }\n        }\n\n        /// <summary>\n        /// Attempts to retrieve secrets previously stored in memory\n        /// </summary>\n        private void RetrieveSecret()\n        {\n            try\n            {\n                // Add secret provider\n                _secretProvider = new WebSocketServerSecretProvider(SecretProviderKey);\n\n                // Check for existing secrets\n                var secret = _secretProvider.GetSecret(SecretProviderKey);\n\n                if (secret != null)\n                {\n                    Debug.LogMessage(LogEventLevel.Information, \"Secret successfully retrieved\", this);\n\n                    Debug.LogMessage(LogEventLevel.Debug, \"Secret: {0}\", this, secret.Value.ToString());\n\n\n                    // populate the local secrets object\n                    _secret = JsonConvert.DeserializeObject<ServerTokenSecrets>(secret.Value.ToString());\n\n                    if (_secret != null && _secret.Tokens != null)\n                    {\n                        // populate the _uiClient collection\n                        foreach (var token in _secret.Tokens)\n                        {\n                            if (token.Value == null)\n                            {\n                                this.LogWarning(\"Token value is null\");\n                                continue;\n                            }\n\n                            this.LogInformation(\"Adding token: {key} for room: {roomKey}\", token.Key, token.Value.RoomKey);\n\n                            if (UiClientContexts == null)\n                            {\n                                UiClientContexts = new Dictionary<string, UiClientContext>();\n                            }\n\n                            UiClientContexts.Add(token.Key, new UiClientContext(token.Value));\n                        }\n                    }\n\n                    if (UiClientContexts.Count > 0)\n                    {\n                        this.LogInformation(\"Restored {uiClientCount} UiClients from secrets data\", UiClientContexts.Count);\n\n                        foreach (var client in UiClientContexts)\n                        {\n                            var key = client.Key;\n                            var path = _wsPath + key;\n                            var roomKey = client.Value.Token.RoomKey;\n\n                            _server.AddWebSocketService(path, () =>\n                            {\n                                this.LogInformation(\"Building a UiClient with ID {id}\", client.Value.Token.Id);\n                                return BuildUiClient(roomKey, client.Value.Token, key);\n                            });\n                        }\n                    }\n                }\n                else\n                {\n                    this.LogWarning(\"No secret found\");\n                }\n\n                this.LogDebug(\"{uiClientCount} UiClients restored from secrets data\", UiClientContexts.Count);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Exception retrieving secret: {message}\", ex.Message);\n                this.LogDebug(ex, \"Stack Trace\");\n            }\n        }\n\n        /// <summary>\n        /// UpdateSecret method\n        /// </summary>\n        public void UpdateSecret()\n        {\n            try\n            {\n                if (_secret == null)\n                {\n                    this.LogError(\"Secret is null\");\n\n                    _secret = new ServerTokenSecrets(string.Empty);\n                }\n\n                _secret.Tokens.Clear();\n\n                foreach (var uiClientContext in UiClientContexts)\n                {\n                    _secret.Tokens.Add(uiClientContext.Key, uiClientContext.Value.Token);\n                }\n\n                var serializedSecret = JsonConvert.SerializeObject(_secret);\n\n                _secretProvider.SetSecret(SecretProviderKey, serializedSecret);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Exception updating secret: {message}\", ex.Message);\n                this.LogDebug(ex, \"Stack Trace\");\n            }\n        }\n\n        /// <summary>\n        /// Generates a new token based on validating a room key and grant code passed in.  If valid, returns a token and adds a service to the server for that token's path\n        /// </summary>\n        /// <param name=\"s\"></param>\n        private void GenerateClientTokenFromConsole(string s)\n        {\n            if (s == \"?\" || string.IsNullOrEmpty(s))\n            {\n                CrestronConsole.ConsoleCommandResponse(@\"[RoomKey] [GrantCode] Validates the room key against the grant code and returns a token for use in a UI client\");\n                return;\n            }\n\n            var values = s.Split(' ');\n\n            if (values.Length < 2)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Invalid number of arguments.  Please provide a room key and a grant code\");\n                return;\n            }\n\n\n            var roomKey = values[0];\n            var grantCode = values[1];\n\n            var bridge = _parent.GetRoomBridge(roomKey);\n\n            if (bridge == null)\n            {\n                CrestronConsole.ConsoleCommandResponse(string.Format(\"Unable to find room with key: {0}\", roomKey));\n                return;\n            }\n\n            var (token, path) = ValidateGrantCode(grantCode, bridge);\n\n            if (token == null)\n            {\n                CrestronConsole.ConsoleCommandResponse(\"Grant Code is not valid\");\n                return;\n            }\n\n            CrestronConsole.ConsoleCommandResponse($\"Added new WebSocket UiClient service at path: {path}\");\n            CrestronConsole.ConsoleCommandResponse($\"Token: {token}\");\n        }\n\n        /// <summary>\n        /// Validates the grant code against the room key\n        /// </summary>\n        public (string, string) ValidateGrantCode(string grantCode, string roomKey)\n        {\n            var bridge = _parent.GetRoomBridge(roomKey);\n\n            if (bridge == null)\n            {\n                this.LogWarning(\"Unable to find room with key: {roomKey}\", roomKey);\n                return (null, null);\n            }\n\n            return ValidateGrantCode(grantCode, bridge);\n        }\n\n        /// <summary>\n        /// Validates the grant code against the room key\n        /// </summary>\n        public (string, string) ValidateGrantCode(string grantCode, MobileControlBridgeBase bridge)\n        {\n            // TODO: Authenticate grant code passed in\n            // For now, we just generate a random guid as the token and use it as the ClientId as well\n            var grantCodeIsValid = true;\n\n            if (grantCodeIsValid)\n            {\n                if (_secret == null)\n                {\n                    _secret = new ServerTokenSecrets(grantCode);\n                }\n\n                return GenerateClientToken(bridge, \"\");\n            }\n            else\n            {\n                return (null, null);\n            }\n        }\n\n        /// <summary>\n        /// Generates a new client token for the specified bridge\n        /// </summary>\n        public (string, string) GenerateClientToken(MobileControlBridgeBase bridge, string touchPanelKey = \"\")\n        {\n            var key = Guid.NewGuid().ToString();\n\n            var token = new JoinToken { Code = bridge.UserCode, RoomKey = bridge.RoomKey, Uuid = _parent.SystemUuid, TouchpanelKey = touchPanelKey };\n\n            UiClientContexts.Add(key, new UiClientContext(token));\n\n            var path = _wsPath + key;\n\n            _server.AddWebSocketService(path, () =>\n            {\n                this.LogInformation(\"Building a UiClient with ID {id}\", token.Id);\n                return BuildUiClient(bridge.RoomKey, token, key);\n            });\n\n            this.LogInformation(\"Added new WebSocket UiClient for path: {path}\", path);\n            this.LogInformation(\"Token: {@token}\", token);\n\n            this.LogVerbose(\"{serviceCount} websocket services present\", _server.WebSocketServices.Count);\n\n            UpdateSecret();\n\n            return (key, path);\n        }\n\n        private UiClient BuildUiClient(string roomKey, JoinToken token, string key)\n        {\n            // Get the most recent unused clientId for this token (legacy support)\n            // New clients will override this ID in OnOpen with the validated query parameter value\n            var clientId = \"pending\";\n            if (legacyClientRegistrations.TryGetValue(key, out var registrations))\n            {\n                // Get most recent registration\n                var sorted = registrations.OrderByDescending(r => r.timestamp).ToList();\n                if (sorted.Any())\n                {\n                    clientId = sorted.First().clientId;\n                    // Remove it from the bag\n                    var newBag = new ConcurrentBag<(string, DateTime)>(sorted.Skip(1));\n                    legacyClientRegistrations.TryUpdate(key, newBag, registrations);\n                    this.LogVerbose(\"Assigned most recent legacy clientId {clientId} for token {token}\", clientId, key);\n                }\n            }\n\n            var c = new UiClient($\"uiclient-{key}-{roomKey}-{clientId}\", clientId, token.Token, token.TouchpanelKey);\n            this.LogInformation(\"Constructing UiClient with key {key} and temporary ID (will be set from query param)\", key);\n            c.Controller = _parent;\n            c.RoomKey = roomKey;\n            c.TokenKey = key; // Store the URL token key for filtering\n            c.Server = this; // Give UiClient access to server for ID registration\n\n            // Don't add to uiClients yet - will be added in OnOpen after ID is set from query param\n\n            c.ConnectionClosed += (o, a) =>\n            {\n                uiClients.TryRemove(a.ClientId, out _);\n                // Clean up any pending registrations for this token\n                var keysToRemove = pendingClientRegistrations.Keys\n                    .Where(k => k.StartsWith($\"{key}-\"))\n                    .ToList();\n                foreach (var k in keysToRemove)\n                {\n                    pendingClientRegistrations.TryRemove(k, out _);\n                }\n\n                // Clean up legacy registrations if empty\n                if (legacyClientRegistrations.TryGetValue(key, out var legacyBag) && legacyBag.IsEmpty)\n                {\n                    legacyClientRegistrations.TryRemove(key, out _);\n                }\n            };\n            return c;\n        }\n\n        /// <summary>\n        /// Registers a UiClient with its validated client ID after WebSocket connection\n        /// </summary>\n        /// <param name=\"client\">The UiClient to register</param>\n        /// <param name=\"clientId\">The validated client ID</param>\n        /// <param name=\"tokenKey\">The token key for validation</param>\n        /// <returns>True if registration successful, false if validation failed</returns>\n        public bool RegisterUiClient(UiClient client, string clientId, string tokenKey)\n        {\n            var registrationKey = $\"{tokenKey}-{clientId}\";\n\n            // Verify this clientId was generated during a join request for this token\n            if (!pendingClientRegistrations.TryRemove(registrationKey, out _))\n            {\n                this.LogWarning(\"Client attempted to connect with unregistered or expired clientId {clientId} for token {token}\", clientId, tokenKey);\n                return false;\n            }\n\n            // Registration is valid - add to active clients\n            uiClients.AddOrUpdate(clientId, client, (id, existingClient) =>\n            {\n                this.LogWarning(\"Replacing existing client with duplicate id {id}\", id);\n                return client;\n            });\n\n            this.LogInformation(\"Successfully registered UiClient with ID {clientId} for token {token}\", clientId, tokenKey);\n            return true;\n        }\n\n        /// <summary>\n        /// Updates a client's ID when a mismatch is detected between stored ID and message ID\n        /// </summary>\n        /// <param name=\"oldClientId\">The current/old client ID</param>\n        /// <param name=\"newClientId\">The new client ID from the message</param>\n        /// <param name=\"tokenKey\">The token key for validation</param>\n        /// <returns>True if update successful, false otherwise</returns>\n        public bool UpdateClientId(string oldClientId, string newClientId, string tokenKey)\n        {\n            if (string.IsNullOrEmpty(oldClientId) || string.IsNullOrEmpty(newClientId))\n            {\n                this.LogWarning(\"Cannot update client ID with null or empty values\");\n                return false;\n            }\n\n            if (oldClientId == newClientId)\n            {\n                return true; // No update needed\n            }\n\n            // Verify the new clientId was registered for this token\n            var registrationKey = $\"{tokenKey}-{newClientId}\";\n            if (!pendingClientRegistrations.TryRemove(registrationKey, out _))\n            {\n                this.LogWarning(\"Cannot update to unregistered clientId {newClientId} for token {token}\", newClientId, tokenKey);\n                return false;\n            }\n\n            // Get the existing client\n            if (!uiClients.TryRemove(oldClientId, out var client))\n            {\n                this.LogWarning(\"Cannot find client with old ID {oldClientId}\", oldClientId);\n                return false;\n            }\n\n            // Update the client's ID\n            client.UpdateId(newClientId);\n\n            // Re-add with new ID\n            if (!uiClients.TryAdd(newClientId, client))\n            {\n                // If add fails, try to restore old entry\n                uiClients.TryAdd(oldClientId, client);\n                client.UpdateId(oldClientId);\n                this.LogError(\"Failed to update client ID from {oldClientId} to {newClientId}\", oldClientId, newClientId);\n                return false;\n            }\n\n            this.LogInformation(\"Successfully updated client ID from {oldClientId} to {newClientId}\", oldClientId, newClientId);\n            return true;\n        }\n\n        /// <summary>\n        /// Registers a UiClient using legacy flow (for backwards compatibility with older clients)\n        /// </summary>\n        /// <param name=\"client\">The UiClient to register</param>\n        public void RegisterLegacyUiClient(UiClient client)\n        {\n            if (string.IsNullOrEmpty(client.Id))\n            {\n                this.LogError(\"Cannot register client with null or empty ID\");\n                return;\n            }\n\n            uiClients.AddOrUpdate(client.Id, client, (id, existingClient) =>\n            {\n                this.LogWarning(\"Replacing existing client with duplicate id {id} (legacy flow)\", id);\n                return client;\n            });\n\n            this.LogInformation(\"Successfully registered UiClient with ID {clientId} using legacy flow\", client.Id);\n        }\n\n        /// <summary>\n        /// Prints out the session data for each path\n        /// </summary>\n        public void PrintSessionData()\n        {\n            foreach (var path in _server.WebSocketServices.Paths)\n            {\n                this.LogInformation(\"Path: {path}\", path);\n                this.LogInformation(\"  Session Count: {sessionCount}\", _server.WebSocketServices[path].Sessions.Count);\n                this.LogInformation(\"  Active Session Count: {activeSessionCount}\", _server.WebSocketServices[path].Sessions.ActiveIDs.Count());\n                this.LogInformation(\"  Inactive Session Count: {inactiveSessionCount}\", _server.WebSocketServices[path].Sessions.InactiveIDs.Count());\n                this.LogInformation(\"  Active Clients:\");\n                foreach (var session in _server.WebSocketServices[path].Sessions.IDs)\n                {\n                    this.LogInformation(\"    Client ID: {id}\", (_server.WebSocketServices[path].Sessions[session] as UiClient)?.Id);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Removes all clients from the server\n        /// </summary>\n        public void RemoveAllTokens(string s)\n        {\n            if (s == \"?\" || string.IsNullOrEmpty(s))\n            {\n                CrestronConsole.ConsoleCommandResponse(@\"Remove all clients from the server.  To execute add 'confirm' to command\");\n                return;\n            }\n\n            if (s != \"confirm\")\n            {\n                CrestronConsole.ConsoleCommandResponse(@\"To remove all clients, add 'confirm' to the command\");\n                return;\n            }\n\n            foreach (var client in UiClientContexts)\n            {\n                if (client.Value.Client != null && client.Value.Client.Context.WebSocket.IsAlive)\n                {\n                    client.Value.Client.Context.WebSocket.Close(CloseStatusCode.Normal, \"Server Shutting Down\");\n                }\n\n                var path = _wsPath + client.Key;\n                if (_server.RemoveWebSocketService(path))\n                {\n                    CrestronConsole.ConsoleCommandResponse(string.Format(\"Client removed with token: {0}\", client.Key));\n                }\n                else\n                {\n                    CrestronConsole.ConsoleCommandResponse(string.Format(\"Unable to remove client with token : {0}\", client.Key));\n                }\n            }\n\n            UiClientContexts.Clear();\n\n            UpdateSecret();\n        }\n\n        /// <summary>\n        /// Removes a client with the specified token value\n        /// </summary>\n        /// <param name=\"s\"></param>\n        private void RemoveToken(string s)\n        {\n            if (s == \"?\" || string.IsNullOrEmpty(s))\n            {\n                CrestronConsole.ConsoleCommandResponse(@\"[token] Removes the client with the specified token value\");\n                return;\n            }\n\n            var key = s;\n\n            if (UiClientContexts.ContainsKey(key))\n            {\n                var uiClientContext = UiClientContexts[key];\n\n                if (uiClientContext.Client != null && uiClientContext.Client.Context.WebSocket.IsAlive)\n                {\n                    uiClientContext.Client.Context.WebSocket.Close(CloseStatusCode.Normal, \"Token removed from server\");\n                }\n\n                var path = _wsPath + key;\n                if (_server.RemoveWebSocketService(path))\n                {\n                    UiClientContexts.Remove(key);\n\n                    UpdateSecret();\n\n                    CrestronConsole.ConsoleCommandResponse(string.Format(\"Client removed with token: {0}\", key));\n                }\n                else\n                {\n                    CrestronConsole.ConsoleCommandResponse(string.Format(\"Unable to remove client with token : {0}\", key));\n                }\n            }\n            else\n            {\n                CrestronConsole.ConsoleCommandResponse(string.Format(\"Unable to find client with token: {0}\", key));\n            }\n        }\n\n        /// <summary>\n        /// Prints out info about current client IDs\n        /// </summary>\n        private void PrintClientInfo()\n        {\n            CrestronConsole.ConsoleCommandResponse(\"Mobile Control UI Client Info:\\r\");\n\n            CrestronConsole.ConsoleCommandResponse(string.Format(\"{0} clients found:\\r\", UiClientContexts.Count));\n\n            foreach (var client in UiClientContexts)\n            {\n                CrestronConsole.ConsoleCommandResponse(string.Format(\"RoomKey: {0} Token: {1}\\r\", client.Value.Token.RoomKey, client.Key));\n            }\n        }\n\n        private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)\n        {\n            if (programEventType == eProgramStatusEventType.Stopping)\n            {\n                foreach (var client in UiClients.Values)\n                {\n                    if (client != null && client.Context.WebSocket.IsAlive)\n                    {\n                        client.Context.WebSocket.Close(CloseStatusCode.Normal, \"Server Shutting Down\");\n                    }\n                }\n\n                StopServer();\n            }\n        }\n\n        /// <summary>\n        /// Handler for GET requests to server\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void Server_OnGet(object sender, HttpRequestEventArgs e)\n        {\n            try\n            {\n                var req = e.Request;\n                var res = e.Response;\n                res.ContentEncoding = Encoding.UTF8;\n\n                res.AddHeader(\"Access-Control-Allow-Origin\", \"*\");\n\n                var path = req.RawUrl;\n\n                this.LogVerbose(\"GET Request received at path: {path}\", path);\n\n                // Call for user app to join the room with a token\n                if (path.StartsWith(\"/mc/api/ui/joinroom\"))\n                {\n                    HandleJoinRequest(req, res);\n                }\n                // Call to get the server version\n                else if (path.StartsWith(\"/mc/api/version\"))\n                {\n                    HandleVersionRequest(res);\n                }\n                else if (path.StartsWith(\"/mc/app/logo\"))\n                {\n                    HandleImageRequest(req, res);\n                }\n                // Call to serve the user app\n                else if (path.StartsWith(_userAppBaseHref))\n                {\n                    HandleUserAppRequest(req, res, path);\n                }\n                else\n                {\n                    // All other paths\n                    res.StatusCode = 404;\n                    res.Close();\n                }\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Exception in OnGet handler: {message}\", ex.Message);\n                this.LogDebug(ex, \"Stack Trace\");\n            }\n        }\n\n        private async void Server_OnPost(object sender, HttpRequestEventArgs e)\n        {\n            try\n            {\n                var req = e.Request;\n                var res = e.Response;\n\n                res.AddHeader(\"Access-Control-Allow-Origin\", \"*\");\n\n                var path = req.RawUrl;\n                var ip = req.RemoteEndPoint.Address.ToString();\n\n                this.LogVerbose(\"POST Request received at path: {path} from host {host}\", path, ip);\n\n                var body = new StreamReader(req.InputStream).ReadToEnd();\n\n                if (path.StartsWith(\"/mc/api/log\"))\n                {\n                    res.StatusCode = 200;\n                    res.Close();\n\n                    var logRequest = new HttpRequestMessage(HttpMethod.Post, $\"http://{_parent.Config.DirectServer.Logging.Host}:{_parent.Config.DirectServer.Logging.Port}/logs\")\n                    {\n                        Content = new StringContent(body, Encoding.UTF8, \"application/json\"),\n                    };\n\n                    logRequest.Headers.Add(\"x-pepperdash-host\", ip);\n\n                    await LogClient.SendAsync(logRequest);\n\n                    this.LogVerbose(\"Log data sent to {host}:{port}\", _parent.Config.DirectServer.Logging.Host, _parent.Config.DirectServer.Logging.Port);\n                }\n                else\n                {\n                    res.StatusCode = 404;\n                    res.Close();\n                }\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex, \"Caught an exception in the OnPost handler\");\n            }\n        }\n\n        private void Server_OnOptions(object sender, HttpRequestEventArgs e)\n        {\n            try\n            {\n                var res = e.Response;\n\n                res.AddHeader(\"Access-Control-Allow-Origin\", \"*\");\n                res.AddHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n                res.AddHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Accept, X-Requested-With, remember-me\");\n\n                res.StatusCode = 200;\n                res.Close();\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Caught an exception in the OnPost handler\", this);\n            }\n        }\n\n        /// <summary>\n        /// Handle the request to join the room with a token\n        /// </summary>\n        /// <param name=\"req\"></param>\n        /// <param name=\"res\"></param>\n        private void HandleJoinRequest(HttpListenerRequest req, HttpListenerResponse res)\n        {\n            var qp = req.QueryString;\n            var token = qp[\"token\"];\n\n            this.LogVerbose(\"Join Room Request with token: {token}\", token);\n\n            byte[] body;\n\n            if (!UiClientContexts.TryGetValue(token, out UiClientContext clientContext))\n            {\n                var message = \"Token invalid or has expired\";\n                res.StatusCode = 401;\n                res.ContentType = \"application/json\";\n                this.LogVerbose(\"{message}\", message);\n                body = Encoding.UTF8.GetBytes(message);\n                res.ContentLength64 = body.LongLength;\n                res.Close(body, true);\n                return;\n            }\n\n            var bridge = _parent.GetRoomBridge(clientContext.Token.RoomKey);\n\n            if (bridge == null)\n            {\n                var message = string.Format(\"Unable to find bridge with key: {0}\", clientContext.Token.RoomKey);\n                res.StatusCode = 404;\n                res.ContentType = \"application/json\";\n                this.LogVerbose(\"{message}\", message);\n                body = Encoding.UTF8.GetBytes(message);\n                res.ContentLength64 = body.LongLength;\n                res.Close(body, true);\n                return;\n            }\n\n            res.StatusCode = 200;\n            res.ContentType = \"application/json\";\n\n            var devices = DeviceManager.GetDevices();\n            Dictionary<string, DeviceInterfaceInfo> deviceInterfaces = new Dictionary<string, DeviceInterfaceInfo>();\n\n            foreach (var device in devices)\n            {\n                var interfaces = device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List<string>();\n\n                deviceInterfaces.Add(device.Key, new DeviceInterfaceInfo\n                {\n                    Key = device.Key,\n                    Name = (device as IKeyName)?.Name ?? \"\",\n                    Interfaces = interfaces\n                });\n            }\n\n            // Generate a client ID for this join request\n            var clientId = $\"{Utilities.GetNextClientId()}\";\n            var now = DateTime.UtcNow;\n\n            // Store in pending registrations for new clients that send clientId via query param\n            var registrationKey = $\"{token}-{clientId}\";\n            pendingClientRegistrations.TryAdd(registrationKey, clientId);\n\n            // For legacy clients, store with timestamp instead of FIFO queue\n            var legacyBag = legacyClientRegistrations.GetOrAdd(token, _ => new ConcurrentBag<(string, DateTime)>());\n            legacyBag.Add((clientId, now));\n\n            this.LogVerbose(\"Assigning ClientId: {clientId} for token: {token} at {timestamp}\", clientId, token, now);\n\n            // Construct WebSocket URL with clientId query parameter\n            var wsProtocol = \"ws\";\n            var wsUrl = $\"{wsProtocol}://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{Port}{_wsPath}{token}?clientId={clientId}\";\n\n            // Construct the response object\n            JoinResponse jRes = new JoinResponse\n            {\n                ClientId = clientId,\n                RoomKey = bridge.RoomKey,\n                SystemUuid = _parent.SystemUuid,\n                RoomUuid = _parent.SystemUuid,\n                Config = _parent.GetConfigWithPluginVersion(),\n                CodeExpires = new DateTime().AddYears(1),\n                UserCode = bridge.UserCode,\n                UserAppUrl = string.Format(\"http://{0}:{1}/mc/app\",\n                CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),\n                Port),\n                WebSocketUrl = wsUrl,\n                EnableDebug = false,\n                DeviceInterfaceSupport = deviceInterfaces\n            };\n\n            // Serialize to JSON and convert to Byte[]\n            var json = JsonConvert.SerializeObject(jRes);\n            body = Encoding.UTF8.GetBytes(json);\n            res.ContentLength64 = body.LongLength;\n\n            // Send the response\n            res.Close(body, true);\n        }\n\n        /// <summary>\n        /// Handles a server version request\n        /// </summary>\n        /// <param name=\"res\"></param>\n        private void HandleVersionRequest(HttpListenerResponse res)\n        {\n            res.StatusCode = 200;\n            res.ContentType = \"application/json\";\n            var version = new Version() { ServerVersion = _parent.GetConfigWithPluginVersion().RuntimeInfo.PluginVersion };\n            var message = JsonConvert.SerializeObject(version);\n            this.LogVerbose(\"{message}\", message);\n\n            var body = Encoding.UTF8.GetBytes(message);\n            res.ContentLength64 = body.LongLength;\n            res.Close(body, true);\n        }\n\n        /// <summary>\n        /// Handler to return images requested by the user app\n        /// </summary>\n        /// <param name=\"req\"></param>\n        /// <param name=\"res\"></param>\n        private void HandleImageRequest(HttpListenerRequest req, HttpListenerResponse res)\n        {\n            var path = req.RawUrl;\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Requesting Image: {0}\", this, path);\n\n            var imageBasePath = Global.DirectorySeparator + \"html\" + Global.DirectorySeparator + \"logo\" + Global.DirectorySeparator;\n\n            var image = path.Split('/').Last();\n\n            var filePath = imageBasePath + image;\n\n            Debug.LogMessage(LogEventLevel.Verbose, \"Retrieving Image: {0}\", this, filePath);\n\n            if (File.Exists(filePath))\n            {\n                if (filePath.EndsWith(\".png\"))\n                {\n                    res.ContentType = \"image/png\";\n                }\n                else if (filePath.EndsWith(\".jpg\"))\n                {\n                    res.ContentType = \"image/jpeg\";\n                }\n                else if (filePath.EndsWith(\".gif\"))\n                {\n                    res.ContentType = \"image/gif\";\n                }\n                else if (filePath.EndsWith(\".svg\"))\n                {\n                    res.ContentType = \"image/svg+xml\";\n                }\n                byte[] contents = File.ReadAllBytes(filePath);\n                res.ContentLength64 = contents.LongLength;\n                res.Close(contents, true);\n            }\n            else\n            {\n                res.StatusCode = (int)HttpStatusCode.NotFound;\n                res.Close();\n            }\n        }\n\n        /// <summary>\n        /// Handles requests to serve files for the Angular single page app\n        /// </summary>\n        /// <param name=\"req\"></param>\n        /// <param name=\"res\"></param>\n        /// <param name=\"path\"></param>\n        private void HandleUserAppRequest(HttpListenerRequest req, HttpListenerResponse res, string path)\n        {\n            this.LogVerbose(\"Requesting User app file\");\n\n            string filePath = path.Split('?')[0];\n\n            // remove the token from the path if found\n            //string filePath = path.Replace(string.Format(\"?token={0}\", token), \"\");\n\n            // if there's no file suffix strip any extra path data after the base href\n            if (filePath != _userAppBaseHref && !filePath.Contains(\".\") && (!filePath.EndsWith(_userAppBaseHref) || !filePath.EndsWith(_userAppBaseHref += \"/\")))\n            {\n                var suffix = filePath.Substring(_userAppBaseHref.Length, filePath.Length - _userAppBaseHref.Length);\n                if (suffix != \"/\")\n                {\n                    //Debug.Console(2, this, \"Suffix: {0}\", suffix);\n                    filePath = filePath.Replace(suffix, \"\");\n                }\n            }\n\n            // swap the base href prefix for the file path prefix\n            filePath = filePath.Replace(_userAppBaseHref, _appPath);\n\n            this.LogVerbose(\"filepath: {filePath}\", filePath);\n\n\n            // append index.html if no specific file is specified\n            if (!filePath.Contains(\".\"))\n            {\n                if (filePath.EndsWith(\"/\"))\n                {\n                    filePath += \"index.html\";\n                }\n                else\n                {\n                    filePath += \"/index.html\";\n                }\n            }\n\n            // Set ContentType based on file type\n            if (filePath.EndsWith(\".html\"))\n            {\n                this.LogVerbose(\"Client requesting User App\");\n\n                res.ContentType = \"text/html\";\n            }\n            else\n            {\n                if (path.EndsWith(\".js\"))\n                {\n                    res.ContentType = \"application/javascript\";\n                }\n                else if (path.EndsWith(\".css\"))\n                {\n                    res.ContentType = \"text/css\";\n                }\n                else if (path.EndsWith(\".json\"))\n                {\n                    res.ContentType = \"application/json\";\n                }\n            }\n\n            this.LogVerbose(\"Attempting to serve file: {filePath}\", filePath);\n\n            var remoteIp = req.RemoteEndPoint.Address;\n\n            // Check if the request is coming from the CS LAN and if so, send the CS config instead of the LAN config\n            if (csSubnetMask != null && csIpAddress != null && remoteIp.IsInSameSubnet(csIpAddress, csSubnetMask) && filePath.Contains(appConfigFileName))\n            {\n                filePath = filePath.Replace(appConfigFileName, appConfigCsFileName);\n            }\n\n            byte[] contents;\n            if (File.Exists(filePath))\n            {\n                this.LogVerbose(\"File found: {filePath}\", filePath);\n                contents = File.ReadAllBytes(filePath);\n            }\n            else\n            {\n                this.LogWarning(\"File not found: {filePath}\", filePath);\n                res.StatusCode = (int)HttpStatusCode.NotFound;\n                res.Close();\n                return;\n            }\n\n            res.ContentLength64 = contents.LongLength;\n            res.Close(contents, true);\n        }\n\n        /// <summary>\n        /// StopServer method\n        /// </summary>\n        public void StopServer()\n        {\n            this.LogVerbose(\"Stopping WebSocket Server\");\n            _server.Stop(CloseStatusCode.Normal, \"Server Shutting Down\");\n        }\n\n        /// <summary>\n        /// Sends a message to all connectd clients\n        /// </summary>\n        /// <param name=\"message\"></param>\n        /// <summary>\n        /// SendMessageToAllClients method\n        /// </summary>\n        public void SendMessageToAllClients(string message)\n        {\n            foreach (var client in uiClients.Values)\n            {\n                if (!client.Context.WebSocket.IsAlive)\n                {\n                    continue;\n                }\n\n                client.Context.WebSocket.Send(message);\n            }\n        }\n\n        /// <summary>\n        /// Sends a message to a specific client\n        /// </summary>\n        /// <param name=\"clientId\"></param>\n        /// <param name=\"message\"></param>\n        /// <summary>\n        /// SendMessageToClient method\n        /// </summary>\n        public void SendMessageToClient(object clientId, string message)\n        {\n            if (clientId == null)\n            {\n                return;\n            }\n\n            if (uiClients.TryGetValue((string)clientId, out var client))\n            {\n                var socket = client.Context.WebSocket;\n\n                if (!socket.IsAlive)\n                {\n                    this.LogError(\"Unable to send message to client {id}. Client is disconnected: {message}\", clientId, message);\n                    return;\n                }\n                socket.Send(message);\n            }\n            else\n            {\n                this.LogWarning(\"Unable to find client with ID: {clientId}\", clientId);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs",
    "content": "using System.Collections.Generic;\n\n\nnamespace PepperDash.Essentials.WebSocketServer\n{\n  /// <summary>\n  /// Represents a ServerTokenSecrets\n  /// </summary>\n  public class ServerTokenSecrets\n  {\n    /// <summary>\n    /// Gets or sets the GrantCode\n    /// </summary>\n    public string GrantCode { get; set; }\n\n    /// <summary>\n    /// Gets or sets the Tokens for this server\n    /// </summary>\n    public Dictionary<string, JoinToken> Tokens { get; set; }\n\n    /// <summary>\n    /// Initialize a new instance of the <see cref=\"ServerTokenSecrets\"/> class with the provided grant code\n    /// </summary>\n    /// <param name=\"grantCode\">The grant code for this server</param>\n    public ServerTokenSecrets(string grantCode)\n    {\n      GrantCode = grantCode;\n      Tokens = new Dictionary<string, JoinToken>();\n    }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.RoomBridges;\nusing Serilog.Events;\nusing WebSocketSharp;\nusing WebSocketSharp.Server;\nusing ErrorEventArgs = WebSocketSharp.ErrorEventArgs;\n\n\nnamespace PepperDash.Essentials.WebSocketServer\n{\n    /// <summary>\n    /// Represents the behaviour to associate with a UiClient for WebSocket communication\n    /// </summary>\n    public class UiClient : WebSocketBehavior, IKeyed\n    {\n        /// <inheritdoc />\n        public string Key { get; private set; }\n\n        /// <summary>\n        /// Client ID used by client for this connection\n        /// </summary>\n        public string Id { get; private set; }\n\n        /// <summary>\n        /// Updates the client ID - only accessible from within the assembly (e.g., by the server)\n        /// </summary>\n        /// <param name=\"newId\">The new client ID</param>\n        internal void UpdateId(string newId)\n        {\n            Id = newId;\n        }\n\n        /// <summary>\n        /// Token associated with this client\n        /// </summary>\n        public string Token { get; private set; }\n\n        /// <summary>\n        /// The URL token key used to connect (from UiClientContexts dictionary key)\n        /// </summary>\n        public string TokenKey { get; set; }\n\n        /// <summary>\n        /// Touchpanel Key associated with this client\n        /// </summary>\n        public string TouchpanelKey { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the mobile control system controller that handles this client's messages\n        /// </summary>\n        public MobileControlSystemController Controller { get; set; }\n\n        /// <summary>\n        /// Gets or sets the server instance for client registration\n        /// </summary>\n        public MobileControlWebsocketServer Server { get; set; }\n\n        /// <summary>\n        /// Gets or sets the room key that this client is associated with\n        /// </summary>\n        public string RoomKey { get; set; }\n\n        /// <summary>\n        /// The timestamp when this client connection was established\n        /// </summary>\n        private DateTime _connectionTime;\n\n        /// <summary>\n        /// Gets the duration that this client has been connected. Returns zero if not currently connected.\n        /// </summary>\n        public TimeSpan ConnectedDuration\n        {\n            get\n            {\n                if (Context.WebSocket.IsAlive)\n                {\n                    return DateTime.Now - _connectionTime;\n                }\n                else\n                {\n                    return new TimeSpan(0);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Triggered when this client closes it's connection\n        /// </summary>\n        public event EventHandler<ConnectionClosedEventArgs> ConnectionClosed;\n\n        /// <summary>\n        /// Initializes a new instance of the UiClient class with the specified key\n        /// </summary>\n        /// <param name=\"key\">The unique key to identify this client</param>\n        /// <param name=\"id\">The client ID used by the client for this connection</param>\n        /// <param name=\"token\">The token associated with this client</param>\n        /// <param name=\"touchpanelKey\">The touchpanel key associated with this client</param>\n        public UiClient(string key, string id, string token, string touchpanelKey = \"\")\n        {\n            Key = key;\n            Id = id;\n            Token = token;\n            TouchpanelKey = touchpanelKey;\n        }\n\n        /// <inheritdoc />\n        protected override void OnOpen()\n        {\n            base.OnOpen();\n\n            _connectionTime = DateTime.Now;\n\n            Log.Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this);\n            Log.Level = LogLevel.Trace;\n\n            // Get clientId from query parameter\n            var queryString = Context.QueryString;\n            var clientId = queryString[\"clientId\"];\n\n            if (!string.IsNullOrEmpty(clientId))\n            {\n                // New behavior: Validate and register with the server using provided clientId\n                if (Server == null || !Server.RegisterUiClient(this, clientId, TokenKey))\n                {\n                    this.LogError(\"Failed to register client with ID {clientId}. Invalid or expired registration.\", clientId);\n                    Context.WebSocket.Close(CloseStatusCode.PolicyViolation, \"Invalid or expired clientId\");\n                    return;\n                }\n\n                // Update this client's ID to the validated one\n                Id = clientId;\n                Key = $\"uiclient-{TokenKey}-{RoomKey}-{clientId}\";\n\n                this.LogInformation(\"Client {clientId} successfully connected and registered (new flow)\", clientId);\n            }\n            else\n            {\n                // Legacy behavior: Use clientId from Token.Id (generated in HandleJoinRequest)\n                this.LogInformation(\"Client connected without clientId query parameter. Using legacy registration flow.\");\n\n                // Id is already set from Token in constructor, use it\n                if (string.IsNullOrEmpty(Id))\n                {\n                    this.LogError(\"Legacy client has no ID from token. Connection will be closed.\");\n                    Context.WebSocket.Close(CloseStatusCode.PolicyViolation, \"No client ID available\");\n                    return;\n                }\n\n                Key = $\"uiclient-{TokenKey}-{RoomKey}-{Id}\";\n\n                // Register directly to active clients (legacy flow)\n                if (Server != null)\n                {\n                    Server.RegisterLegacyUiClient(this);\n                }\n\n                this.LogInformation(\"Client {clientId} registered using legacy flow\", Id);\n            }\n\n            if (Controller == null)\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"WebSocket UiClient Controller is null\");\n                _connectionTime = DateTime.Now;\n            }\n\n            var clientJoinedMessage = new MobileControlMessage\n            {\n                Type = \"/system/clientJoined\",\n                Content = JToken.FromObject(new\n                {\n                    clientId = Id,\n                    roomKey = RoomKey,\n                    touchpanelKey = TouchpanelKey ?? string.Empty,\n                })\n            };\n\n            Controller.HandleClientMessage(JsonConvert.SerializeObject(clientJoinedMessage));\n\n            var bridge = Controller.GetRoomBridge(RoomKey);\n\n            if (bridge == null) return;\n\n            SendUserCodeToClient(bridge, Id);\n\n            bridge.UserCodeChanged -= Bridge_UserCodeChanged;\n            bridge.UserCodeChanged += Bridge_UserCodeChanged;\n\n            // TODO: Future: Check token to see if there's already an open session using that token and reject/close the session \n        }\n\n        /// <summary>\n        /// Handles the UserCodeChanged event from a room bridge and sends the updated user code to the client\n        /// </summary>\n        /// <param name=\"sender\">The room bridge that raised the event</param>\n        /// <param name=\"e\">Event arguments</param>\n        private void Bridge_UserCodeChanged(object sender, EventArgs e)\n        {\n            SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, Id);\n        }\n\n        /// <summary>\n        /// Sends the current user code and QR code URL to the specified client\n        /// </summary>\n        /// <param name=\"bridge\">The room bridge containing the user code information</param>\n        /// <param name=\"clientId\">The ID of the client to send the information to</param>\n        private void SendUserCodeToClient(MobileControlBridgeBase bridge, string clientId)\n        {\n            var content = new\n            {\n                userCode = bridge.UserCode,\n                qrUrl = bridge.QrCodeUrl,\n            };\n\n            var message = new MobileControlMessage\n            {\n                Type = \"/system/userCodeChanged\",\n                ClientId = clientId,\n                Content = JToken.FromObject(content)\n            };\n\n            Controller.SendMessageObjectToDirectClient(message);\n        }\n\n        /// <inheritdoc />\n        protected override void OnMessage(MessageEventArgs e)\n        {\n            base.OnMessage(e);\n\n            if (e.IsText && e.Data.Length > 0 && Controller != null)\n            {\n                // Forward the message to the controller to be put on the receive queue\n                Controller.HandleClientMessage(e.Data);\n            }\n        }\n\n        /// <inheritdoc />\n        protected override void OnClose(CloseEventArgs e)\n        {\n            base.OnClose(e);\n\n            this.LogInformation(\"WebSocket UiClient Closing: {code} reason: {reason}\", e.Code, e.Reason);\n\n            foreach (var messenger in Controller.Messengers)\n            {\n                messenger.Value.UnsubscribeClient(Id);\n            }\n\n            foreach (var messenger in Controller.DefaultMessengers)\n            {\n                messenger.Value.UnsubscribeClient(Id);\n            }\n\n            ConnectionClosed?.Invoke(this, new ConnectionClosedEventArgs(Id));\n        }\n\n        /// <inheritdoc />\n        protected override void OnError(ErrorEventArgs e)\n        {\n            base.OnError(e);\n\n            this.LogError(\"WebSocket UiClient Error: {message}\", e.Message);\n            this.LogDebug(e.Exception, \"Stack Trace\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs",
    "content": "namespace PepperDash.Essentials.WebSocketServer\n{\n  /// <summary>\n  /// Represents a UiClientContext\n  /// </summary>\n  public class UiClientContext\n  {\n    /// <summary>\n    /// Gets or sets the Client\n    /// </summary>\n    public UiClient Client { get; private set; }\n    /// <summary>\n    /// Gets or sets the Token\n    /// </summary>\n    public JoinToken Token { get; private set; }\n\n    /// <summary>\n    /// Initialize an instance of the <see cref=\"UiClientContext\"/> class with the provided token\n    /// </summary>\n    /// <param name=\"token\">token for this client</param>\n    public UiClientContext(JoinToken token)\n    {\n      Token = token;\n    }\n\n    /// <summary>\n    /// SetClient method\n    /// </summary>\n    public void SetClient(UiClient client)\n    {\n      Client = client;\n    }\n\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs",
    "content": "using Newtonsoft.Json;\n\n\nnamespace PepperDash.Essentials.WebSocketServer\n{\n  /// <summary>\n  /// Represents a Version\n  /// </summary>\n  public class Version\n  {\n    /// <summary>\n    /// Server version this Websocket is connected to \n    /// </summary>\n    [JsonProperty(\"serverVersion\")]\n    public string ServerVersion { get; set; }\n\n    /// <summary>\n    /// True if the server is on a processor\n    /// </summary>\n\n    [JsonProperty(\"serverIsRunningOnProcessorHardware\")]\n    public bool ServerIsRunningOnProcessorHardware { get; private set; }\n\n    /// <summary>\n    /// Initialize an instance of the <see cref=\"Version\"/> class\n    /// </summary>\n    /// <remarks>\n    /// The <see cref=\"ServerIsRunningOnProcessorHardware\"/> property is set to true by default.\n    /// </remarks>\n    public Version()\n    {\n      ServerIsRunningOnProcessorHardware = true;\n    }\n  }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl/WebSocketServer/WebSocketServerSecretProvider.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.WebSocketServer\n{\n    internal class WebSocketServerSecretProvider : CrestronLocalSecretsProvider\n    {\n        public WebSocketServerSecretProvider(string key)\n            : base(key)\n        {\n            Key = key;\n        }\n    }\n\n    /// <summary>\n    /// Stores a secret value using the provided secret store provider\n    /// </summary>\n    public class WebSocketServerSecret : ISecret\n    {\n        /// <summary>\n        /// Gets the Secret Provider associated with this secret\n        /// </summary>\n        public ISecretProvider Provider { get; private set; }\n\n        /// <summary>\n        /// Gets the Key associated with this secret\n        /// </summary>\n        public string Key { get; private set; }\n\n        /// <summary>\n        /// Gets the Value associated with this secret\n        /// </summary>\n        public object Value { get; private set; }\n\n        /// <summary>\n        /// Initialize and instance of the <see cref=\"WebSocketServerSecret\"/> class\n        /// </summary>\n        public WebSocketServerSecret(string key, object value, ISecretProvider provider)\n        {\n            Key = key;\n            Value = JsonConvert.SerializeObject(value);\n            Provider = provider;\n        }\n\n        /// <summary>\n        /// DeserializeSecret method\n        /// </summary>\n        public ServerTokenSecrets DeserializeSecret()\n        {\n            return JsonConvert.DeserializeObject<ServerTokenSecrets>(Value.ToString());\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/ContentTypes.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer\n{\n    /// <summary>\n    /// Represents a SourceSelectMessageContent\n    /// </summary>\n    public class SourceSelectMessageContent\n    {\n\n        [JsonProperty(\"sourceListItemKey\")]\n        /// <summary>\n        /// Gets or sets the SourceListItemKey\n        /// </summary>\n        public string SourceListItemKey { get; set; }\n        [JsonProperty(\"sourceListKey\")]\n        /// <summary>\n        /// Gets or sets the SourceListKey\n        /// </summary>\n        public string SourceListKey { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a DirectRoute\n    /// </summary>\n    public class DirectRoute\n    {\n\n        [JsonProperty(\"sourceKey\")]\n        /// <summary>\n        /// Gets or sets the SourceKey\n        /// </summary>\n        public string SourceKey { get; set; }\n        [JsonProperty(\"destinationKey\")]\n        /// <summary>\n        /// Gets or sets the DestinationKey\n        /// </summary>\n        public string DestinationKey { get; set; }\n        [JsonProperty(\"signalType\")]\n        /// <summary>\n        /// Gets or sets the SignalType\n        /// </summary>\n        public eRoutingSignalType SignalType { get; set; }\n    }\n\n    /// <summary>\n    /// \n    /// </summary>\n    /// <param name=\"b\"></param>\n    /// <summary>\n    /// Delegate for PressAndHoldAction\n    /// </summary>\n    public delegate void PressAndHoldAction(bool b);\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/DisplayBaseMessenger.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.AppServer;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing System.Linq;\nusing DisplayBase = PepperDash.Essentials.Devices.Common.Displays.DisplayBase;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a DisplayBaseMessenger\n    /// </summary>\n    public class DisplayBaseMessenger : MessengerBase\n    {\n        private readonly DisplayBase display;\n\n        public DisplayBaseMessenger(string key, string messagePath, DisplayBase device) : base(key, messagePath, device)\n        {\n            display = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            /*AddAction(\"/powerOn\", (id, content) => display.PowerOn());\n            AddAction(\"/powerOff\", (id, content) => display.PowerOff());\n            AddAction(\"/powerToggle\", (id, content) => display.PowerToggle());*/\n\n            AddAction(\"/inputSelect\", (id, content) =>\n            {\n                var s = content.ToObject<MobileControlSimpleContent<string>>();\n\n                var inputPort = display.InputPorts.FirstOrDefault(i => i.Key == s.Value);\n\n                if (inputPort == null)\n                {\n                    this.LogWarning(\"No input named {inputName} found for {deviceKey}\", s, display.Key);\n                    return;\n                }\n\n                display.ExecuteSwitch(inputPort.Selector);\n            });\n\n            AddAction(\"/inputs\", (id, content) =>\n            {\n                var inputsList = display.InputPorts.Select(p => p.Key).ToList();\n\n                var messageObject = new MobileControlMessage\n                {\n                    Type = MessagePath + \"/inputs\",\n                    Content = JToken.FromObject(new\n                    {\n                        inputKeys = inputsList,\n                    })\n                };\n\n                AppServerController.SendMessageObject(messageObject);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/IChannelMessenger.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a IChannelMessenger\n    /// </summary>\n    public class IChannelMessenger : MessengerBase\n    {\n        private readonly IChannel channelDevice;\n\n        public IChannelMessenger(string key, string messagePath, IChannel device) : base(key, messagePath, device as IKeyName)\n        {\n            channelDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/chanUp\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.ChannelUp(b)));\n\n            AddAction(\"/chanDown\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.ChannelDown(b)));\n            AddAction(\"/lastChan\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.LastChannel(b)));\n            AddAction(\"/guide\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.Guide(b)));\n            AddAction(\"/info\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.Info(b)));\n            AddAction(\"/exit\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => channelDevice?.Exit(b)));\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/IColorMessenger.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a IColorMessenger\n    /// </summary>\n    public class IColorMessenger : MessengerBase\n    {\n        private readonly IColor colorDevice;\n        public IColorMessenger(string key, string messagePath, IColor device) : base(key, messagePath, device as IKeyName)\n        {\n            colorDevice = device as IColor;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/red\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => colorDevice?.Red(b)));\n            AddAction(\"/green\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => colorDevice?.Green(b)));\n            AddAction(\"/yellow\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => colorDevice?.Yellow(b)));\n            AddAction(\"/blue\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => colorDevice?.Blue(b)));\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/IDPadMessenger.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a IDPadMessenger\n    /// </summary>\n    public class IDPadMessenger : MessengerBase\n    {\n        private readonly IDPad dpadDevice;\n        public IDPadMessenger(string key, string messagePath, IDPad device) : base(key, messagePath, device as IKeyName)\n        {\n            dpadDevice = device;\n        }\n\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/up\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Up(b)));\n            AddAction(\"/down\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Down(b)));\n            AddAction(\"/left\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Left(b)));\n            AddAction(\"/right\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Right(b)));\n            AddAction(\"/select\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Select(b)));\n            AddAction(\"/menu\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Menu(b)));\n            AddAction(\"/exit\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dpadDevice?.Exit(b)));\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/IDvrMessenger.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a IDvrMessenger\n    /// </summary>\n    public class IDvrMessenger : MessengerBase\n    {\n        private readonly IDvr dvrDevice;\n        public IDvrMessenger(string key, string messagePath, IDvr device) : base(key, messagePath, device as IKeyName)\n        {\n            dvrDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/dvrlist\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dvrDevice?.DvrList(b)));\n            AddAction(\"/record\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => dvrDevice?.Record(b)));\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/IHasPowerMessenger.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a IHasPowerMessenger\n    /// </summary>\n    public class IHasPowerMessenger : MessengerBase\n    {\n        private readonly IHasPowerControl powerDevice;\n        public IHasPowerMessenger(string key, string messagePath, IHasPowerControl device) : base(key, messagePath, device as IKeyName)\n        {\n            powerDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/powerOn\", (id, content) => powerDevice?.PowerOn());\n            AddAction(\"/powerOff\", (id, content) => powerDevice?.PowerOff());\n            AddAction(\"/powerToggle\", (id, content) => powerDevice?.PowerToggle());\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/INumericMessenger.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a INumericKeypadMessenger\n    /// </summary>\n    public class INumericKeypadMessenger : MessengerBase\n    {\n        private readonly INumericKeypad keypadDevice;\n        public INumericKeypadMessenger(string key, string messagePath, INumericKeypad device) : base(key, messagePath, device as IKeyName)\n        {\n            keypadDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/num0\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit0(b)));\n            AddAction(\"/num1\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit1(b)));\n            AddAction(\"/num2\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit2(b)));\n            AddAction(\"/num3\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit3(b)));\n            AddAction(\"/num4\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit4(b)));\n            AddAction(\"/num5\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit5(b)));\n            AddAction(\"/num6\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit6(b)));\n            AddAction(\"/num7\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit7(b)));\n            AddAction(\"/num8\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit8(b)));\n            AddAction(\"/num9\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.Digit9(b)));\n            AddAction(\"/numDash\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.KeypadAccessoryButton1(b)));\n            AddAction(\"/numEnter\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => keypadDevice?.KeypadAccessoryButton2(b)));\n            // Deal with the Accessory functions on the numpad later\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/ISetTopBoxControlsMessenger.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a ISetTopBoxControlsMessenger\n    /// </summary>\n    public class ISetTopBoxControlsMessenger : MessengerBase\n    {\n        private readonly ISetTopBoxControls stbDevice;\n        public ISetTopBoxControlsMessenger(string key, string messagePath, ISetTopBoxControls device) : base(key, messagePath, device as IKeyName)\n        {\n            stbDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n            AddAction(\"/fullStatus\", (id, content) => SendISetTopBoxControlsFullMessageObject());\n            AddAction(\"/dvrList\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => stbDevice?.DvrList(b)));\n            AddAction(\"/replay\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => stbDevice?.Replay(b)));\n        }\n        /// <summary>\n        /// Helper method to build call status for vtc\n        /// </summary>\n        /// <returns></returns>\n        private void SendISetTopBoxControlsFullMessageObject()\n        {\n\n            PostStatusMessage(new SetTopBoxControlsState());\n\n\n        }\n    }\n\n    public class SetTopBoxControlsState : DeviceStateMessageBase\n    {\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/DeviceTypeExtensions/ITransportMessenger.cs",
    "content": "﻿using PepperDash.Core;\nusing PepperDash.Essentials.AppServer.Messengers;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.Room.MobileControl\n{\n    /// <summary>\n    /// Represents a ITransportMessenger\n    /// </summary>\n    public class ITransportMessenger : MessengerBase\n    {\n        private readonly ITransport transportDevice;\n        public ITransportMessenger(string key, string messagePath, ITransport device) : base(key, messagePath, device as IKeyName)\n        {\n            transportDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/play\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Play(b)));\n            AddAction(\"/pause\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Pause(b)));\n            AddAction(\"/stop\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Stop(b)));\n            AddAction(\"/prevTrack\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.ChapPlus(b)));\n            AddAction(\"/nextTrack\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.ChapMinus(b)));\n            AddAction(\"/rewind\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Rewind(b)));\n            AddAction(\"/ffwd\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.FFwd(b)));\n            AddAction(\"/record\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => transportDevice?.Record(b)));\n        }\n\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/AudioCodecBaseMessenger.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Devices.Common.AudioCodec;\nusing PepperDash.Essentials.Devices.Common.Codec;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Provides a messaging bridge for an AudioCodecBase device\n    /// </summary>\n    public class AudioCodecBaseMessenger : MessengerBase\n    {\n        /// <summary>\n        /// Device being bridged\n        /// </summary>\n        public AudioCodecBase Codec { get; private set; }\n\n        /// <summary>\n        /// Constuctor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"codec\"></param>\n        /// <param name=\"messagePath\"></param>\n        public AudioCodecBaseMessenger(string key, AudioCodecBase codec, string messagePath)\n            : base(key, messagePath, codec)\n        {\n            Codec = codec ?? throw new ArgumentNullException(\"codec\");\n            codec.CallStatusChange += Codec_CallStatusChange;\n        }\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendAtcFullMessageObject(id));\n\n            AddAction(\"/audioDialerStatus\", (id, content) => SendAtcFullMessageObject(id));\n\n            AddAction(\"/dial\", (id, content) =>\n            {\n                var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n                Codec.Dial(msg.Value);\n            });\n\n            AddAction(\"/endCallById\", (id, content) =>\n            {\n                var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n                var call = GetCallWithId(msg.Value);\n                if (call != null)\n                    Codec.EndCall(call);\n            });\n\n            AddAction(\"/endAllCalls\", (id, content) => Codec.EndAllCalls());\n            AddAction(\"/dtmf\", (id, content) =>\n            {\n                var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n                Codec.SendDtmf(msg.Value);\n            });\n\n            AddAction(\"/rejectById\", (id, content) =>\n            {\n                var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n                var call = GetCallWithId(msg.Value);\n\n                if (call != null)\n                    Codec.RejectCall(call);\n            });\n\n            AddAction(\"/acceptById\", (id, content) =>\n            {\n                var msg = content.ToObject<MobileControlSimpleContent<string>>();\n                var call = GetCallWithId(msg.Value);\n                if (call != null)\n                    Codec.AcceptCall(call);\n            });\n        }\n\n        /// <summary>\n        /// Helper to grab a call with string ID\n        /// </summary>\n        /// <param name=\"id\"></param>\n        /// <returns></returns>\n        private CodecActiveCallItem GetCallWithId(string id)\n        {\n            return Codec.ActiveCalls.FirstOrDefault(c => c.Id == id);\n        }\n\n        private void Codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)\n        {\n            SendAtcFullMessageObject();\n        }\n\n        /// <summary>\n        /// Helper method to build call status for vtc\n        /// </summary>\n        /// <returns></returns>\n        private void SendAtcFullMessageObject(string id = null)\n        {\n            var info = Codec.CodecInfo;\n\n            PostStatusMessage(JToken.FromObject(new\n            {\n                isInCall = Codec.IsInCall,\n                calls = Codec.ActiveCalls,\n                info = new\n                {\n                    phoneNumber = info.PhoneNumber\n                }\n            }), id\n            );\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Devices.Common.Cameras;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Messenger for a CameraBase device\n    /// </summary>\n    public class CameraBaseMessenger<T> : MessengerBase where T : IKeyed\n    {\n        /// <summary>\n        /// Gets or sets the Camera\n        /// </summary>\n        public T Camera { get; set; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"camera\"></param>\n        /// <param name=\"messagePath\"></param>\n        public CameraBaseMessenger(string key, T camera, string messagePath)\n            : base(key, messagePath, camera as IKeyName)\n        {\n            if (camera == null)\n                throw new ArgumentNullException(nameof(camera));\n\n            Camera = camera;\n\n\n            if (Camera is IHasCameraPresets presetsCamera)\n            {\n                presetsCamera.PresetsListHasChanged += PresetsCamera_PresetsListHasChanged;\n            }\n        }\n\n        private void PresetsCamera_PresetsListHasChanged(object sender, EventArgs e)\n        {\n            var presetList = new List<CameraPreset>();\n\n            if (Camera is IHasCameraPresets presetsCamera)\n                presetList = presetsCamera.Presets;\n\n            PostStatusMessage(JToken.FromObject(new\n            {\n                presets = presetList\n            })\n            );\n        }\n\n        /// <summary>\n        /// Registers the actions for this messenger.  This is called by the base class\n        /// </summary>\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendCameraFullMessageObject(id));\n\n            AddAction(\"/cameraStatus\", (id, content) => SendCameraFullMessageObject(id));\n\n\n            if (Camera is IHasCameraPtzControl ptzCamera)\n            {\n                //  Need to evaluate how to pass through these P&H actions.  Need a method that takes a bool maybe?\n                AddAction(\"/cameraUp\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.TiltUp();\n                        return;\n                    }\n\n                    ptzCamera.TiltStop();\n                }));\n                AddAction(\"/cameraDown\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.TiltDown();\n                        return;\n                    }\n\n                    ptzCamera.TiltStop();\n                }));\n                AddAction(\"/cameraLeft\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.PanLeft();\n                        return;\n                    }\n\n                    ptzCamera.PanStop();\n                }));\n                AddAction(\"/cameraRight\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.PanRight();\n                        return;\n                    }\n\n                    ptzCamera.PanStop();\n                }));\n                AddAction(\"/cameraZoomIn\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.ZoomIn();\n                        return;\n                    }\n\n                    ptzCamera.ZoomStop();\n                }));\n                AddAction(\"/cameraZoomOut\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                {\n                    if (b)\n                    {\n                        ptzCamera.ZoomOut();\n                        return;\n                    }\n\n                    ptzCamera.ZoomStop();\n                }));\n            }\n\n            if (Camera is IHasCameraAutoMode)\n            {\n                AddAction(\"/cameraModeAuto\", (id, content) => (Camera as IHasCameraAutoMode).CameraAutoModeOn());\n\n                AddAction(\"/cameraModeManual\", (id, content) => (Camera as IHasCameraAutoMode).CameraAutoModeOff());\n\n            }\n\n            if (Camera is IHasPowerControl)\n            {\n                AddAction(\"/cameraModeOff\", (id, content) => (Camera as IHasPowerControl).PowerOff());\n                AddAction(\"/cameraModeManual\", (id, content) => (Camera as IHasPowerControl).PowerOn());\n            }\n\n\n            if (Camera is IHasCameraPresets presetsCamera)\n            {\n                AddAction(\"/recallPreset\", (id, content) =>\n                {\n                    var msg = content.ToObject<MobileControlSimpleContent<int>>();\n\n                    presetsCamera.PresetSelect(msg.Value);\n                });\n\n                AddAction(\"/storePreset\", (id, content) =>\n                {\n                    var msg = content.ToObject<MobileControlSimpleContent<int>>();\n\n                    presetsCamera.PresetStore(msg.Value, string.Empty);\n                });\n            }\n        }\n\n        private void HandleCameraPressAndHold(JToken content, Action<bool> cameraAction)\n        {\n            var state = content.ToObject<MobileControlSimpleContent<string>>();\n\n            var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);\n            if (timerHandler == null)\n            {\n                return;\n            }\n\n            timerHandler(Camera.Key, cameraAction);\n\n        }\n\n        /// <summary>\n        /// Helper method to update the full status of the camera\n        /// </summary>\n        private void SendCameraFullMessageObject(string id = null)\n        {\n            var presetList = new List<CameraPreset>();\n            CameraCapabilities capabilities = null;\n\n            if (Camera is IHasCameraPresets presetsCamera)\n                presetList = presetsCamera.Presets;\n\n            if (Camera is ICameraCapabilities cameraCapabilities)\n                capabilities = new CameraCapabilities\n                {\n                    CanPan = cameraCapabilities.CanPan,\n                    CanTilt = cameraCapabilities.CanTilt,\n                    CanZoom = cameraCapabilities.CanZoom,\n                    CanFocus = cameraCapabilities.CanFocus\n\n                };\n\n            if (Camera is CameraBase cameraBase)\n                capabilities = new CameraCapabilities\n                {\n                    CanPan = cameraBase.CanPan,\n                    CanTilt = cameraBase.CanTilt,\n                    CanZoom = cameraBase.CanZoom,\n                    CanFocus = cameraBase.CanFocus\n                    \n                };\n\n            var message = new CameraStateMessage\n            {\n                CameraManualSupported = Camera is IHasCameraControls,\n                CameraAutoSupported = Camera is IHasCameraAutoMode,\n                CameraOffSupported = Camera is IHasCameraOff,\n                CameraMode = (eCameraControlMode)Enum.Parse(typeof(eCameraControlMode), GetCameraMode(), true),\n                HasPresets = Camera is IHasCameraPresets,\n                Presets = presetList,\n                Capabilities = capabilities,\n                IsFarEnd = Camera is IAmFarEndCamera\n            };\n\n            PostStatusMessage(message, id\n            );\n        }\n\n        /// <summary>\n        /// Computes the current camera mode\n        /// </summary>\n        /// <returns></returns>\n        private string GetCameraMode()\n        {\n            string m;\n            if (Camera is IHasCameraAutoMode && (Camera as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.BoolValue)\n                m = eCameraControlMode.Auto.ToString().ToLower();\n            else if (Camera is IHasPowerControlWithFeedback && !(Camera as IHasPowerControlWithFeedback).PowerIsOnFeedback.BoolValue)\n                m = eCameraControlMode.Off.ToString().ToLower();\n            else\n                m = eCameraControlMode.Manual.ToString().ToLower();\n            return m;\n        }\n    }\n\n    /// <summary>\n    /// State message for a camera device\n    /// </summary>\n    public class CameraStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Indicates whether the camera supports manual control\n        /// </summary>\n        [JsonProperty(\"cameraManualSupported\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CameraManualSupported { get; set; }\n\n        /// <summary>\n        /// Indicates whether the camera supports auto control\n        /// </summary>\n        [JsonProperty(\"cameraAutoSupported\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CameraAutoSupported { get; set; }\n\n        /// <summary>\n        /// Indicates whether the camera supports off control\n        /// </summary>\n        [JsonProperty(\"cameraOffSupported\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool CameraOffSupported { get; set; }\n\n        /// <summary>\n        /// Indicates the current camera control mode\n        /// </summary>\n        [JsonProperty(\"cameraMode\", NullValueHandling = NullValueHandling.Ignore)]\n        [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]\n        public eCameraControlMode CameraMode { get; set; }\n\n        /// <summary>\n        /// Indicates whether the camera has presets\n        /// </summary>\n        [JsonProperty(\"hasPresets\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool HasPresets { get; set; }\n\n        /// <summary>\n        /// List of presets if the camera supports them\n        /// </summary>\n        [JsonProperty(\"presets\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<CameraPreset> Presets { get; set; }\n\n        /// <summary>\n        /// Indicates the capabilities of the camera\n        /// </summary>\n        [JsonProperty(\"capabilities\", NullValueHandling = NullValueHandling.Ignore)]\n        public CameraCapabilities Capabilities { get; set; }\n\n        /// <summary>\n        /// Indicates whether the camera is a far end camera\n        /// </summary>\n        [JsonProperty(\"isFarEnd\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool IsFarEnd { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs",
    "content": "using System.Collections.Generic;\r\nusing System.Linq;\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Converters;\r\nusing Newtonsoft.Json.Linq;\r\nusing PepperDash.Core;\r\nusing PepperDash.Essentials.Core;\r\nusing PepperDash.Essentials.Core.Routing;\r\n\r\nnamespace PepperDash.Essentials.AppServer.Messengers\r\n{\r\n  /// <summary>\r\n  /// Represents a IHasCurrentSourceInfoMessenger\r\n  /// </summary>\r\n  public class CurrentSourcesMessenger : MessengerBase\r\n  {\r\n    private readonly ICurrentSources sourceDevice;\r\n\r\n    /// <summary>\r\n    /// Initializes a new instance of the <see cref=\"CurrentSourcesMessenger\"/> class.\r\n    /// </summary>\r\n    /// <param name=\"key\">The key.</param>\r\n    /// <param name=\"messagePath\">The message path.</param>\r\n    /// <param name=\"device\">The device.</param>\r\n    public CurrentSourcesMessenger(string key, string messagePath, ICurrentSources device) : base(key, messagePath, device as IKeyName)\r\n    {\r\n      sourceDevice = device;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Registers the actions for the messenger.\r\n    /// </summary>\r\n    protected override void RegisterActions()\r\n    {\r\n      base.RegisterActions();\r\n\r\n      AddAction(\"/fullStatus\", (id, content) => SendCurrentSourceStatus(id));\r\n\r\n      AddAction(\"/currentSourceStatus\", (id, content) => SendCurrentSourceStatus(id));\r\n\r\n      sourceDevice.CurrentSourcesChanged += (sender, e) =>\r\n      {\r\n        // need to copy the dictionaries to avoid enumeration issues\r\n        var currentSourceKeys = sourceDevice.CurrentSourceKeys.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);\r\n        var currentSources = sourceDevice.CurrentSources.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);\r\n        \r\n        PostStatusMessage(JToken.FromObject(new\r\n        {\r\n          currentSourceKeys,\r\n          currentSources,\r\n        }));\r\n      };\r\n    }\r\n\r\n    private void SendCurrentSourceStatus(string id = null)\r\n    {\r\n      var message = new CurrentSourcesStateMessage\r\n      {\r\n        CurrentSourceKeys = sourceDevice.CurrentSourceKeys,\r\n        CurrentSources = sourceDevice.CurrentSources\r\n      };\r\n\r\n      PostStatusMessage(message, id);\r\n    }\r\n  }\r\n\r\n  /// <summary>\r\n  /// Represents a CurrentSourcesStateMessage\r\n  /// </summary>\r\n  public class CurrentSourcesStateMessage : DeviceStateMessageBase\r\n  {\r\n\r\n    /// <summary>\r\n    /// Gets or sets the CurrentSourceKey\r\n    /// </summary>\r\n    [JsonProperty(\"currentSourceKeys\", NullValueHandling = NullValueHandling.Ignore)]\r\n    public Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; set; }\r\n\r\n\r\n    /// <summary>\r\n    /// Gets or sets the CurrentSource\r\n    /// </summary>\r\n    [JsonProperty(\"currentSources\")]\r\n    public Dictionary<eRoutingSignalType, SourceListItem> CurrentSources { get; set; }\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceEventMessageBase.cs",
    "content": "using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n  /// <summary>\n  /// Base class for event messages that include the type of message and an event type\n  /// </summary>\n  public abstract class DeviceEventMessageBase : DeviceMessageBase\n  {\n    /// <summary>\n    /// The event type\n    /// </summary>\n    [JsonProperty(\"eventType\")]\n    public string EventType { get; set; }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceInfoMessenger.cs",
    "content": "﻿using System.Timers;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.DeviceInfo;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Facilitates communication of device information by providing mechanisms for status updates and  device\n    /// information reporting.\n    /// </summary>\n    /// <remarks>The <see cref=\"DeviceInfoMessenger\"/> class integrates with an <see\n    /// cref=\"IDeviceInfoProvider\"/> to  manage device-specific information. It uses a debounce timer to limit the\n    /// frequency of updates,  ensuring efficient communication. The timer is initialized with a 1-second interval and\n    /// is disabled  by default. This class also subscribes to device information change events and provides actions for\n    /// reporting full device status and triggering updates.</remarks>\n    public class DeviceInfoMessenger : MessengerBase\n    {\n        private readonly IDeviceInfoProvider _deviceInfoProvider;\n\n        private readonly Timer debounceTimer;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DeviceInfoMessenger\"/> class, which facilitates communication\n        /// of device information.\n        /// </summary>\n        /// <remarks>The messenger uses a debounce timer to limit the frequency of certain operations. The\n        /// timer is initialized with a 1-second interval and is disabled by default.</remarks>\n        /// <param name=\"key\">A unique identifier for the messenger instance.</param>\n        /// <param name=\"messagePath\">The path used for sending and receiving messages.</param>\n        /// <param name=\"device\">An implementation of <see cref=\"IDeviceInfoProvider\"/> that provides device-specific information.</param>\n        public DeviceInfoMessenger(string key, string messagePath, IDeviceInfoProvider device) : base(key, messagePath, device as Device)\n        {\n            _deviceInfoProvider = device;\n\n            debounceTimer = new Timer(1000)\n            {\n                Enabled = false,\n                AutoReset = false\n            };\n\n            debounceTimer.Elapsed += DebounceTimer_Elapsed;\n        }\n\n        private void DebounceTimer_Elapsed(object sender, ElapsedEventArgs e)\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                deviceInfo = _deviceInfoProvider.DeviceInfo\n            }));\n        }\n\n        /// <summary>\n        /// Registers actions and event handlers for device information updates and status reporting.\n        /// </summary>\n        /// <remarks>This method sets up actions for handling device status updates and reporting full\n        /// device status. It also subscribes to the <see cref=\"IDeviceInfoProvider.DeviceInfoChanged\"/> event to\n        /// trigger debounced updates when the device information changes.</remarks>\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            _deviceInfoProvider.DeviceInfoChanged += (o, a) =>\n            {\n                debounceTimer.Stop();\n                debounceTimer.Start();\n            };\n\n            AddAction(\"/fullStatus\", (id, context) => SendFullStatus(id));\n\n            AddAction(\"/deviceInfo\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/update\", (id, context) => _deviceInfoProvider.UpdateDeviceInfo());\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            PostStatusMessage(new DeviceInfoStateMessage\n            {\n                DeviceInfo = _deviceInfoProvider.DeviceInfo\n            }, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a message containing the state information of a device, including detailed device information.\n    /// </summary>\n    /// <remarks>This class is used to encapsulate the state of a device along with its associated\n    /// information. It extends <see cref=\"DeviceStateMessageBase\"/> to provide additional details about the\n    /// device.</remarks>\n    /// <summary>\n    /// Represents a DeviceInfoStateMessage\n    /// </summary>\n    public class DeviceInfoStateMessage : DeviceStateMessageBase\n    {\n        [JsonProperty(\"deviceInfo\")]\n        public DeviceInfo DeviceInfo { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceMessageBase.cs",
    "content": "using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n  /// <summary>\n  /// Base class for device messages that include the type of message\n  /// </summary>\n  public abstract class DeviceMessageBase\n  {\n    /// <summary>\n    /// The device key\n    /// </summary>\n    [JsonProperty(\"key\")]\n    /// <summary>\n    /// Gets or sets the Key\n    /// </summary>\n    public string Key { get; set; }\n\n    /// <summary>\n    /// The device name\n    /// </summary>\n    [JsonProperty(\"name\")]\n    public string Name { get; set; }\n\n    /// <summary>\n    /// The type of the message class\n    /// </summary>\n    [JsonProperty(\"messageType\")]\n    public string MessageType => GetType().Name;\n\n    /// <summary>\n    /// Gets or sets the MessageBasePath\n    /// </summary>\n    [JsonProperty(\"messageBasePath\")]\n\n    public string MessageBasePath { get; set; }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DevicePresetsModelMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Core.Presets;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a DevicePresetsModelMessenger\n    /// </summary>\n    public class DevicePresetsModelMessenger : MessengerBase\n    {\n        private readonly ITvPresetsProvider _presetsDevice;\n\n        /// <summary>\n        /// Constructor for DevicePresetsModelMessenger\n        /// </summary>\n        /// <param name=\"key\">The key.</param>\n        /// <param name=\"messagePath\">The message path.</param>\n        /// <param name=\"presetsDevice\">The presets device.</param>\n        public DevicePresetsModelMessenger(string key, string messagePath, ITvPresetsProvider presetsDevice)\n            : base(key, messagePath, presetsDevice as Device)\n        {\n            _presetsDevice = presetsDevice;\n        }\n\n        private void SendPresets(string id = null)\n        {\n            PostStatusMessage(new PresetStateMessage\n            {\n                Favorites = _presetsDevice.TvPresets.PresetsList\n            }, id);\n        }\n\n        private void RecallPreset(ISetTopBoxNumericKeypad device, string channel)\n        {\n            _presetsDevice.TvPresets.Dial(channel, device);\n        }\n\n        private void SavePresets(List<PresetChannel> presets)\n        {\n            _presetsDevice.TvPresets.UpdatePresets(presets);\n        }\n\n\n        #region Overrides of MessengerBase\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n\n        {\n            AddAction(\"/fullStatus\", (id, content) =>\n            {\n                this.LogInformation(\"getting full status for client {id}\", id);\n                try\n                {\n                    SendPresets(id);\n                }\n                catch (Exception ex)\n                {\n                    Debug.LogMessage(ex, \"Exception sending preset full status\", this);\n                }\n            });\n\n            AddAction(\"/presetsStatus\", (id, content) => SendPresets(id));\n\n            AddAction(\"/recall\", (id, content) =>\n            {\n                var p = content.ToObject<PresetChannelMessage>();\n\n\n                if (!(DeviceManager.GetDeviceForKey(p.DeviceKey) is ISetTopBoxNumericKeypad dev))\n                {\n                    this.LogDebug(\"Unable to find device with key {0}\", p.DeviceKey);\n                    return;\n                }\n\n                RecallPreset(dev, p.Preset.Channel);\n            });\n\n            AddAction(\"/save\", (id, content) =>\n            {\n                var presets = content.ToObject<List<PresetChannel>>();\n\n                SavePresets(presets);\n            });\n\n            _presetsDevice.TvPresets.PresetsSaved += (p) => SendPresets();\n        }\n\n        #endregion\n    }\n\n    /// <summary>\n    /// Represents a PresetChannelMessage\n    /// </summary>\n    public class PresetChannelMessage\n    {\n        /// <summary>\n        /// Gets or sets the Preset\n        /// </summary>\n        [JsonProperty(\"preset\")]\n        public PresetChannel Preset;\n\n        /// <summary>\n        /// Gets or sets the DeviceKey\n        /// </summary>\n        [JsonProperty(\"deviceKey\")]\n        public string DeviceKey;\n    }\n\n    /// <summary>\n    /// Represents a PresetStateMessage\n    /// </summary>\n    public class PresetStateMessage : DeviceStateMessageBase\n    {\n\n        /// <summary>\n        /// Gets or sets the Favorites\n        /// </summary>\n        [JsonProperty(\"favorites\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<PresetChannel> Favorites { get; set; } = new List<PresetChannel>();\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceStateMessageBase.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n  /// <summary>\n  /// Represents a DeviceStateMessageBase\n  /// </summary>\n  public class DeviceStateMessageBase : DeviceMessageBase\n  {\n    /// <summary>\n    /// The interfaces implmented by the device sending the messsage\n    /// </summary>\n    [JsonProperty(\"interfaces\")]\n    public List<string> Interfaces { get; private set; }\n\n    /// <summary>\n    /// Sets the interfaces implemented by the device sending the message\n    /// </summary>\n    /// <param name=\"interfaces\"></param>\n    [Obsolete(\"SetInterfaces is no longer supported and will be removed in a future release.  Interfaces for all devices are now retrieved via the /joinroom endpoint in the MobileControlWebsocketServer\")]\n    public void SetInterfaces(List<string> interfaces)\n    {\n      Interfaces = interfaces;\n    }\n  }\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a DeviceVolumeMessenger\n    /// </summary>\n    public class DeviceVolumeMessenger : MessengerBase\n    {\n        private readonly IBasicVolumeControls device;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DeviceVolumeMessenger\"/> class.\n        /// </summary>\n        /// <param name=\"key\">The key.</param>\n        /// <param name=\"messagePath\">The message path.</param>\n        /// <param name=\"device\">The device.</param>\n        public DeviceVolumeMessenger(string key, string messagePath, IBasicVolumeControls device)\n            : base(key, messagePath, device)\n        {\n            this.device = device;\n        }\n\n        private void SendStatus(string id = null)\n        {\n            try\n            {\n                if (!(device is IBasicVolumeWithFeedback feedbackDevice))\n                {\n                    return;\n                }\n\n                var messageObj = new VolumeStateMessage\n                {\n                    Volume = new Volume\n                    {\n                        Level = feedbackDevice?.VolumeLevelFeedback.IntValue ?? -1,\n                        Muted = feedbackDevice?.MuteFeedback.BoolValue ?? false,\n                        HasMute = true,  // assume all devices have mute for now\n                    }\n                };\n\n                if (device is IBasicVolumeWithFeedbackAdvanced volumeAdvanced)\n                {\n                    messageObj.Volume.RawValue = volumeAdvanced.RawVolumeLevel.ToString();\n                    messageObj.Volume.Units = volumeAdvanced.Units;\n                }\n\n                PostStatusMessage(messageObj, id);\n            }\n            catch (Exception ex)\n            {\n                Debug.LogMessage(ex, \"Exception sending full status\", this);\n            }\n        }\n\n        #region Overrides of MessengerBase\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n        {\n            AddAction(\"/volumeUp\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) =>\n                        {\n                            Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, \"Calling {localDevice} volume up with {value}\", DeviceKey, b);\n                            try\n                            {\n                                device.VolumeUp(b);\n                            }\n                            catch (Exception ex)\n                            {\n                                Debug.LogMessage(ex, \"Got exception during volume up: {Exception}\", null, ex);\n                            }\n                        }));\n\n            AddAction(\"/muteToggle\", (id, content) =>\n                        {\n                            device.MuteToggle();\n                        });\n\n            AddAction(\"/volumeDown\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) =>\n            {\n                Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, \"Calling {localDevice} volume down with {value}\", DeviceKey, b);\n\n                try\n                {\n                    device.VolumeDown(b);\n                }\n                catch (Exception ex)\n                {\n                    Debug.LogMessage(ex, \"Got exception during volume down: {Exception}\", null, ex);\n                }\n            }));\n\n            if (!(device is IBasicVolumeWithFeedback feedback))\n            {\n                this.LogDebug(\"Skipping feedback methods for {deviceKey}\", (device as IKeyName)?.Key);\n                return;\n            }\n\n            AddAction(\"/fullStatus\", (id, content) => SendStatus(id));\n\n            AddAction(\"/volumeStatus\", (id, content) => SendStatus(id));\n\n            AddAction(\"/level\", (id, content) =>\n            {\n                var volume = content.ToObject<MobileControlSimpleContent<ushort>>();\n\n                feedback.SetVolume(volume.Value);\n            });\n\n\n\n            AddAction(\"/muteOn\", (id, content) =>\n            {\n                feedback.MuteOn();\n            });\n\n            AddAction(\"/muteOff\", (id, content) =>\n            {\n                feedback.MuteOff();\n            });\n\n\n\n            feedback.MuteFeedback.OutputChange += (sender, args) =>\n            {\n                var message = new VolumeStateMessage\n                {\n                    Volume = new Volume\n                    {\n                        Muted = args.BoolValue\n                    }\n                };\n\n                PostStatusMessage(JToken.FromObject(message));\n            };\n\n            feedback.VolumeLevelFeedback.OutputChange += (sender, args) =>\n            {\n                var message = new VolumeStateMessage\n                {\n                    Volume = new Volume\n                    {\n                        Level = args.IntValue,\n                    }\n                };\n\n                if (device is IBasicVolumeWithFeedbackAdvanced volumeAdvanced)\n                {\n                    message.Volume.RawValue = volumeAdvanced.RawVolumeLevel.ToString();\n                    message.Volume.Units = volumeAdvanced.Units;\n                }\n\n                PostStatusMessage(JToken.FromObject(message));\n            };\n        }\n\n        #endregion\n    }\n\n    /// <summary>\n    /// Represents a VolumeStateMessage\n    /// </summary>\n    public class VolumeStateMessage : DeviceStateMessageBase\n    {\n        [JsonProperty(\"volume\", NullValueHandling = NullValueHandling.Ignore)]\n        /// <summary>\n        /// Gets or sets the Volume\n        /// </summary>\n        public Volume Volume { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a Volume\n    /// </summary>\n    public class Volume\n    {\n        [JsonProperty(\"level\", NullValueHandling = NullValueHandling.Ignore)]\n        public int? Level { get; set; }\n\n        [JsonProperty(\"hasMute\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasMute { get; set; }\n\n        [JsonProperty(\"muted\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? Muted { get; set; }\n\n        [JsonProperty(\"label\", NullValueHandling = NullValueHandling.Ignore)]\n        /// <summary>\n        /// Gets or sets the Label\n        /// </summary>\n        public string Label { get; set; }\n\n        [JsonProperty(\"rawValue\", NullValueHandling = NullValueHandling.Ignore)]\n        /// <summary>\n        /// Gets or sets the RawValue\n        /// </summary>\n        public string RawValue { get; set; }\n\n        [JsonConverter(typeof(StringEnumConverter))]\n        [JsonProperty(\"units\", NullValueHandling = NullValueHandling.Ignore)]\n        public eVolumeLevelUnits? Units { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/GenericMessenger.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a GenericMessenger\n    /// </summary>\n    public class GenericMessenger : MessengerBase\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"GenericMessenger\"/> class.\n        /// </summary>\n        /// <param name=\"key\">The key.</param>\n        /// <param name=\"device\">The device.</param>\n        /// <param name=\"messagePath\">The message path.</param>\n        public GenericMessenger(string key, EssentialsDevice device, string messagePath) : base(key, messagePath, device)\n        {\n        }\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var state = new DeviceStateMessageBase();\n\n            PostStatusMessage(state, id);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IBasicVideoMuteWithFeedbackMessenger.cs",
    "content": "﻿using System.Collections.Generic;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IBasicVideoMuteWithFeedbackMessenger\n    /// </summary>\n    public class IBasicVideoMuteWithFeedbackMessenger : MessengerBase\n    {\n        private readonly IBasicVideoMuteWithFeedback device;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"IBasicVideoMuteWithFeedbackMessenger\"/> class.\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"messagePath\"></param>\n        /// <param name=\"device\"></param>\n        public IBasicVideoMuteWithFeedbackMessenger(string key, string messagePath, IBasicVideoMuteWithFeedback device)\n            : base(key, messagePath, device as IKeyName)\n        {\n            this.device = device;\n        }\n\n        /// <summary>\n        /// SendFullStatus method\n        /// </summary>\n        public void SendFullStatus(string id = null)\n        {\n            var messageObj = new IBasicVideoMuteWithFeedbackMessage\n            {\n                VideoMuteState = device.VideoMuteIsOn.BoolValue\n            };\n\n            PostStatusMessage(messageObj, id);\n        }\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/videoMuteStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/videoMuteToggle\", (id, content) =>\n            {\n                device.VideoMuteToggle();\n            });\n\n            AddAction(\"/videoMuteOn\", (id, content) =>\n            {\n                device.VideoMuteOn();\n            });\n\n            AddAction(\"/videoMuteOff\", (id, content) =>\n            {\n                device.VideoMuteOff();\n            });\n\n            device.VideoMuteIsOn.OutputChange += VideoMuteIsOnFeedback_OutputChange;\n        }\n\n        private void VideoMuteIsOnFeedback_OutputChange(object sender, FeedbackEventArgs args)\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                videoMuteState = args.BoolValue\n            })\n            );\n        }\n    }\n\n    /// <summary>\n    /// Represents a IBasicVideoMuteWithFeedbackMessage\n    /// </summary>\n    public class IBasicVideoMuteWithFeedbackMessage : DeviceStateMessageBase\n    {\n        [JsonProperty(\"videoMuteState\")]\n        public bool VideoMuteState { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ICommunicationMonitorMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a ICommunicationMonitorMessenger\n    /// </summary>\n    public class ICommunicationMonitorMessenger : MessengerBase\n    {\n        private readonly ICommunicationMonitor _communicationMonitor;\n\n        public ICommunicationMonitorMessenger(string key, string messagePath, ICommunicationMonitor device) : base(key, messagePath, device as IKeyName)\n        {\n            _communicationMonitor = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) =>\n            {\n                SendFullStatus(id);\n            });\n\n            AddAction(\"/commStatus\", (id, content) =>\n            {\n                SendFullStatus(id);\n            });\n\n            _communicationMonitor.CommunicationMonitor.StatusChange += (sender, args) =>\n            {\n                PostStatusMessage(JToken.FromObject(new\n                {\n                    commMonitor = new CommunicationMonitorProps\n                    {\n                        IsOnline = _communicationMonitor.CommunicationMonitor.IsOnline,\n                        Status = _communicationMonitor.CommunicationMonitor.Status\n                    }\n                }));\n            };\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            PostStatusMessage(new CommunicationMonitorState\n            {\n                CommunicationMonitor = new CommunicationMonitorProps\n                {\n                    IsOnline = _communicationMonitor.CommunicationMonitor.IsOnline,\n                    Status = _communicationMonitor.CommunicationMonitor.Status\n                },\n            }, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a CommunicationMonitorState\n    /// </summary>\n    public class CommunicationMonitorState : DeviceStateMessageBase\n    {\n        [JsonProperty(\"commMonitor\", NullValueHandling = NullValueHandling.Ignore)]\n        /// <summary>\n        /// Gets or sets the CommunicationMonitor\n        /// </summary>\n        public CommunicationMonitorProps CommunicationMonitor { get; set; }\n\n    }\n\n    public class CommunicationMonitorProps\n    {        /// <summary>\n             /// For devices that implement ICommunicationMonitor, reports the online status of the device\n             /// </summary>\n        [JsonProperty(\"isOnline\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsOnline { get; set; }\n\n        /// <summary>\n        /// For devices that implement ICommunicationMonitor, reports the online status of the device\n        /// </summary>\n        [JsonProperty(\"status\", NullValueHandling = NullValueHandling.Ignore)]\n        [JsonConverter(typeof(StringEnumConverter))]\n        public MonitorStatus Status { get; set; }\n\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IDspPresetsMessenger.cs",
    "content": "﻿using System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IDspPresetsMessenger\n    /// </summary>\n    public class IDspPresetsMessenger : MessengerBase\n    {\n        private readonly IDspPresets device;\n\n        public IDspPresetsMessenger(string key, string messagePath, IDspPresets device)\n            : base(key, messagePath, device as IKeyName)\n        {\n            this.device = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/dspPresetStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/recallPreset\", (id, content) =>\n            {\n                var presetKey = content.ToObject<string>();\n\n\n                if (!string.IsNullOrEmpty(presetKey))\n                {\n                    device.RecallPreset(presetKey);\n                }\n            });\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var message = new IHasDspPresetsStateMessage\n            {\n                Presets = device.Presets\n            };\n\n            PostStatusMessage(message, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a IHasDspPresetsStateMessage\n    /// </summary>\n    public class IHasDspPresetsStateMessage : DeviceStateMessageBase\n    {\n        [JsonProperty(\"presets\")]\n        public Dictionary<string, IKeyName> Presets { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IEssentialsRoomCombinerMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Provides messaging functionality for managing room combination scenarios and partition states in an  <see\n    /// cref=\"IEssentialsRoomCombiner\"/> instance. Enables external systems to interact with the room combiner  via\n    /// predefined actions and status updates.\n    /// </summary>\n    /// <remarks>This class facilitates communication with an <see cref=\"IEssentialsRoomCombiner\"/> by\n    /// exposing actions  for toggling modes, managing partitions, and setting room combination scenarios. It also\n    /// listens for  feedback changes and broadcasts status updates to connected systems.   Typical usage involves\n    /// registering actions for external commands and handling feedback events to  synchronize state changes.</remarks>\n    public class IEssentialsRoomCombinerMessenger : MessengerBase\n    {\n        private readonly IEssentialsRoomCombiner _roomCombiner;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"IEssentialsRoomCombinerMessenger\"/> class,  which facilitates\n        /// messaging for an <see cref=\"IEssentialsRoomCombiner\"/> instance.\n        /// </summary>\n        /// <remarks>This class is designed to enable communication and interaction with an <see\n        /// cref=\"IEssentialsRoomCombiner\"/>  through the specified messaging path. Ensure that the <paramref\n        /// name=\"roomCombiner\"/> parameter is not null  when creating an instance.</remarks>\n        /// <param name=\"key\">The unique key identifying this messenger instance.</param>\n        /// <param name=\"messagePath\">The path used for messaging operations.</param>\n        /// <param name=\"roomCombiner\">The <see cref=\"IEssentialsRoomCombiner\"/> instance associated with this messenger.</param>\n        public IEssentialsRoomCombinerMessenger(string key, string messagePath, IEssentialsRoomCombiner roomCombiner)\n            : base(key, messagePath, roomCombiner as IKeyName)\n        {\n            _roomCombiner = roomCombiner;\n        }\n\n        /// <summary>\n        /// Registers actions and event handlers for managing room combination scenarios and partition states.\n        /// </summary>\n        /// <remarks>This method sets up various actions that can be triggered via specific endpoints,\n        /// such as toggling modes,  setting room combination scenarios, and managing partition states. It also\n        /// subscribes to feedback events  to update the status when changes occur in room combination scenarios or\n        /// partition states.</remarks>\n        protected override void RegisterActions()\n        {\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/combinerStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/setAutoMode\", (id, content) =>\n            {\n                _roomCombiner.SetAutoMode();\n            });\n\n            AddAction(\"/setManualMode\", (id, content) =>\n            {\n                _roomCombiner.SetManualMode();\n            });\n\n            AddAction(\"/toggleMode\", (id, content) =>\n            {\n                _roomCombiner.ToggleMode();\n            });\n\n            AddAction(\"/togglePartitionState\", (id, content) =>\n            {\n                try\n                {\n                    var partitionKey = content.ToObject<string>();\n\n                    _roomCombiner.TogglePartitionState(partitionKey);\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $\"Error toggling partition state: {e}\", this);\n                }\n            });\n\n            AddAction(\"/setRoomCombinationScenario\", (id, content) =>\n            {\n                try\n                {\n                    var scenarioKey = content.ToObject<string>();\n\n                    _roomCombiner.SetRoomCombinationScenario(scenarioKey);\n                }\n                catch (Exception e)\n                {\n                    Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $\"Error toggling partition state: {e}\", this);\n                }\n            });\n\n            _roomCombiner.RoomCombinationScenarioChanged += (sender, args) =>\n            {\n                SendFullStatus();\n            };\n\n            _roomCombiner.IsInAutoModeFeedback.OutputChange += (sender, args) =>\n            {\n                var message = new\n                {\n                    isInAutoMode = _roomCombiner.IsInAutoModeFeedback.BoolValue\n                };\n\n                PostStatusMessage(JToken.FromObject(message));\n            };\n\n            foreach (var partition in _roomCombiner.Partitions)\n            {\n                partition.PartitionPresentFeedback.OutputChange += (sender, args) =>\n                {\n                    var message = new\n                    {\n                        partitions = _roomCombiner.Partitions\n                    };\n\n                    PostStatusMessage(JToken.FromObject(message));\n                };\n            }\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            try\n            {\n                var rooms = new List<IKeyName>();\n\n                foreach (var room in _roomCombiner.Rooms)\n                {\n                    rooms.Add(new RoomCombinerRoom { Key = room.Key, Name = room.Name });\n                }\n\n                var message = new IEssentialsRoomCombinerStateMessage\n                {\n                    DisableAutoMode = _roomCombiner.DisableAutoMode,\n                    IsInAutoMode = _roomCombiner.IsInAutoMode,\n                    CurrentScenario = _roomCombiner.CurrentScenario,\n                    Rooms = rooms,\n                    RoomCombinationScenarios = _roomCombiner.RoomCombinationScenarios,\n                    Partitions = _roomCombiner.Partitions\n                };\n\n                PostStatusMessage(message, id);\n            }\n            catch (Exception e)\n            {\n                this.LogException(e, \"Error sending full status\");\n            }\n        }\n\n        private class RoomCombinerRoom : IKeyName\n        {\n            [JsonProperty(\"key\")]\n            /// <summary>\n            /// Gets or sets the Key\n            /// </summary>\n            public string Key { get; set; }\n\n            [JsonProperty(\"name\")]\n            /// <summary>\n            /// Gets or sets the Name\n            /// </summary>\n            public string Name { get; set; }\n        }\n    }\n\n    /// <summary>\n    /// Represents the state message for a room combiner system, providing information about the current configuration, \n    /// operational mode, and associated rooms, partitions, and scenarios.\n    /// </summary>\n    /// <remarks>This class is used to encapsulate the state of a room combiner system, including its current\n    /// mode of operation,  active room combination scenario, and the list of rooms and partitions involved. It is\n    /// typically serialized  and transmitted to communicate the state of the system.</remarks>\n    public class IEssentialsRoomCombinerStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Gets or sets a value indicating whether automatic mode is disabled.\n        /// </summary>\n        [JsonProperty(\"disableAutoMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool DisableAutoMode { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether the system is operating in automatic mode.\n        /// </summary>\n        [JsonProperty(\"isInAutoMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool IsInAutoMode { get; set; }\n\n        /// <summary>\n        /// Gets or sets the current room combination scenario.\n        /// </summary>\n        [JsonProperty(\"currentScenario\", NullValueHandling = NullValueHandling.Ignore)]\n        public IRoomCombinationScenario CurrentScenario { get; set; }\n\n        /// <summary>\n        /// Gets or sets the collection of rooms associated with the entity.\n        /// </summary>\n        [JsonProperty(\"rooms\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<IKeyName> Rooms { get; set; }\n\n        /// <summary>\n        /// Gets or sets the collection of room combination scenarios.\n        /// </summary>\n        [JsonProperty(\"roomCombinationScenarios\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<IRoomCombinationScenario> RoomCombinationScenarios { get; set; }\n\n        /// <summary>\n        /// Gets or sets the collection of partition controllers.\n        /// </summary>\n        [JsonProperty(\"partitions\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<IPartitionController> Partitions { get; set; }\n    }\n\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Messenger for devices that implement the IHasCameras interface.\n    /// </summary>\n    [Obsolete(\"Use IHasCamerasWithControlsMessenger instead. This class will be removed in a future version\")]\n    public class IHasCamerasMessenger : MessengerBase\n    {\n        /// <summary>\n        /// Device being bridged that implements IHasCameras interface.\n        /// </summary>\n        public IHasCameras CameraController { get; private set; }\n\n        /// <summary>\n        /// Messenger for devices that implement IHasCameras interface.\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"cameraController\"></param>\n        /// <param name=\"messagePath\"></param>\n        /// <exception cref=\"ArgumentNullException\"></exception>\n        public IHasCamerasMessenger(string key, string messagePath, IHasCameras cameraController)\n            : base(key, messagePath, cameraController)\n        {\n            CameraController = cameraController ?? throw new ArgumentNullException(\"cameraController\");\n            CameraController.CameraSelected += CameraController_CameraSelected;\n        }\n\n        private void CameraController_CameraSelected(object sender, CameraSelectedEventArgs e)\n        {\n            PostStatusMessage(new IHasCamerasStateMessage\n            {\n                SelectedCamera = e.SelectedCamera\n            });\n        }\n\n        /// <summary>\n        /// Registers the actions for this messenger.\n        /// </summary>\n        /// <exception cref=\"ArgumentException\"></exception>\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, context) => SendFullStatus(id));\n\n            AddAction(\"/cameraListStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/selectCamera\", (id, content) =>\n            {\n                var cameraKey = content?.ToObject<string>();\n\n                if (!string.IsNullOrEmpty(cameraKey))\n                {\n                    CameraController.SelectCamera(cameraKey);\n                }\n                else\n                {\n                    throw new ArgumentException(\"Content must be a string representing the camera key\");\n                }\n            });\n        }\n\n        private void SendFullStatus(string clientId)\n        {\n            var state = new IHasCamerasStateMessage\n            {\n                CameraList = CameraController.Cameras,\n                SelectedCamera = CameraController.SelectedCamera\n            };\n\n            PostStatusMessage(state, clientId);\n        }\n\n\n    }\n\n    /// <summary>\n    /// State message for devices that implement the IHasCameras interface.\n    /// </summary>\n    public class IHasCamerasStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// List of cameras available in the device.\n        /// </summary>\n        [JsonProperty(\"cameraList\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<CameraBase> CameraList { get; set; }\n\n        /// <summary>\n        /// The currently selected camera on the device.\n        /// </summary>\n        [JsonProperty(\"selectedCamera\", NullValueHandling = NullValueHandling.Ignore)]\n        public CameraBase SelectedCamera { get; set; }\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCamerasWithControlMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Messenger for devices that implement the IHasCameras interface.\n    /// </summary>\n    public class IHasCamerasWithControlMessenger : MessengerBase\n    {\n        /// <summary>\n        /// Device being bridged that implements IHasCameras interface.\n        /// </summary>\n        public IHasCamerasWithControls CameraController { get; private set; }\n\n        /// <summary>\n        /// Messenger for devices that implement IHasCameras interface.\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"cameraController\"></param>\n        /// <param name=\"messagePath\"></param>\n        /// <exception cref=\"ArgumentNullException\"></exception>\n        public IHasCamerasWithControlMessenger(string key, string messagePath, IHasCamerasWithControls cameraController)\n            : base(key, messagePath, cameraController)\n        {\n            CameraController = cameraController ?? throw new ArgumentNullException(\"cameraController\");\n            CameraController.CameraSelected += CameraController_CameraSelected;\n        }\n\n        private void CameraController_CameraSelected(object sender, CameraSelectedEventArgs<IHasCameraControls> e)\n        {\n            var selectedCamera = new KeyName\n            {\n                Key = e.SelectedCamera.Key,\n                Name = e.SelectedCamera.Name\n            };\n\n            PostStatusMessage(new IHasCamerasWithControlsStateMessage\n            {\n                SelectedCamera = selectedCamera\n            });\n        }\n\n        /// <summary>\n        /// Registers the actions for this messenger.\n        /// </summary>\n        /// <exception cref=\"ArgumentException\"></exception>\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, context) => SendFullStatus(id));\n\n            AddAction(\"/cameraListStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/selectCamera\", (id, content) =>\n            {\n                var cameraKey = content?.ToObject<string>();\n\n                if (!string.IsNullOrEmpty(cameraKey))\n                {\n                    CameraController.SelectCamera(cameraKey);\n                }\n                else\n                {\n                    throw new ArgumentException(\"Content must be a string representing the camera key\");\n                }\n            });\n        }\n\n        private void SendFullStatus(string clientId)\n        {\n            var cameraList = new List<IKeyName>();\n            KeyName selectedCamera = null;\n\n            foreach (var cam in CameraController.Cameras)\n            {\n                cameraList.Add(new KeyName{\n                    Key = cam.Key,\n                    Name = cam.Name\n                });\n            }\n\n            if (CameraController.SelectedCamera != null)\n            {\n                selectedCamera = new KeyName\n                {\n                    Key = CameraController.SelectedCamera.Key,\n                    Name = CameraController.SelectedCamera.Name\n                };\n            }\n\n            var state = new IHasCamerasWithControlsStateMessage\n            {\n                CameraList = cameraList,\n                SelectedCamera = selectedCamera\n            };\n\n            PostStatusMessage(state, clientId);\n        }\n    }\n\n    /// <summary>\n    /// State message for devices that implement the IHasCameras interface.\n    /// </summary>\n    public class IHasCamerasWithControlsStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// List of cameras available in the device.\n        /// </summary>\n        [JsonProperty(\"cameraList\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<IKeyName> CameraList { get; set; }\n\n        /// <summary>\n        /// The currently selected camera on the device.\n        /// </summary>\n        [JsonProperty(\"selectedCamera\", NullValueHandling = NullValueHandling.Ignore)]\n        public IKeyName SelectedCamera { get; set; }\n    }\n\n    class KeyName : IKeyName\n    {\n        public string Key { get; set; }\n        public string Name { get; set; }\n        public KeyName()\n        {\n            Key = \"\";\n            Name = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasCurrentSourceInfoMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IHasCurrentSourceInfoMessenger\n    /// </summary>\n    public class IHasCurrentSourceInfoMessenger : MessengerBase\n    {\n        private readonly IHasCurrentSourceInfoChange sourceDevice;\n\n        public IHasCurrentSourceInfoMessenger(string key, string messagePath, IHasCurrentSourceInfoChange device) : base(key, messagePath, device as IKeyName)\n        {\n            sourceDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/currentSourceInfoStatus\", (id, content) => SendFullStatus(id));\n\n            sourceDevice.CurrentSourceChange += (sender, e) =>\n            {\n                switch (e)\n                {\n                    case ChangeType.DidChange:\n                        {\n                            PostStatusMessage(JToken.FromObject(new\n                            {\n                                currentSourceKey = string.IsNullOrEmpty(sourceDevice.CurrentSourceInfoKey) ? string.Empty : sourceDevice.CurrentSourceInfoKey,\n                                currentSource = sourceDevice.CurrentSourceInfo\n                            }));\n                            break;\n                        }\n                }\n            };\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var message = new CurrentSourceStateMessage\n            {\n                CurrentSourceKey = sourceDevice.CurrentSourceInfoKey,\n                CurrentSource = sourceDevice.CurrentSourceInfo\n            };\n\n            PostStatusMessage(message, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a CurrentSourceStateMessage\n    /// </summary>\n    public class CurrentSourceStateMessage : DeviceStateMessageBase\n    {\n\n        /// <summary>\n        /// Gets or sets the CurrentSourceKey\n        /// </summary>\n        [JsonProperty(\"currentSourceKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string CurrentSourceKey { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the CurrentSource\n        /// </summary>\n        [JsonProperty(\"currentSource\")]\n        public SourceListItem CurrentSource { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasInputsMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IHasInputsMessenger\n    /// </summary>\n    public class IHasInputsMessenger<TKey> : MessengerBase\n    {\n        private readonly IHasInputs<TKey> itemDevice;\n\n\n        /// <summary>\n        /// Constructs a messenger for a device that implements IHasInputs<typeparamref name=\"TKey\"/>\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"messagePath\"></param>\n        /// <param name=\"device\"></param>\n        public IHasInputsMessenger(string key, string messagePath, IHasInputs<TKey> device) : base(key, messagePath, device)\n        {\n            itemDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, context) => SendFullStatus(id));\n\n            AddAction(\"/inputStatus\", (id, content) => SendFullStatus(id));\n\n            itemDevice.Inputs.ItemsUpdated += (sender, args) =>\n            {\n                SendFullStatus();\n            };\n\n            itemDevice.Inputs.CurrentItemChanged += (sender, args) =>\n            {\n                SendFullStatus();\n            };\n\n            foreach (var input in itemDevice.Inputs.Items)\n            {\n                var key = input.Key;\n                var localItem = input.Value;\n\n                AddAction($\"/{key}\", (id, content) =>\n                {\n                    localItem.Select();\n                });\n\n                localItem.ItemUpdated += (sender, args) =>\n                {\n                    SendFullStatus();\n                };\n            }\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            try\n            {\n                this.LogInformation(\"Sending full status\");\n\n                var stateObject = new IHasInputsStateMessage<TKey>\n                {\n                    Inputs = new Inputs<TKey>\n                    {\n                        Items = itemDevice.Inputs.Items,\n                        CurrentItem = itemDevice.Inputs.CurrentItem\n                    }\n                };\n\n                PostStatusMessage(stateObject, id);\n            }\n            catch (Exception e)\n            {\n                this.LogError(\"Error sending full status: {0}\", e.Message);\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a IHasInputsStateMessage\n    /// </summary>\n    public class IHasInputsStateMessage<TKey> : DeviceStateMessageBase\n    {\n        [JsonProperty(\"inputs\")]\n        /// <summary>\n        /// Gets or sets the Inputs\n        /// </summary>\n        public Inputs<TKey> Inputs { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a Inputs\n    /// </summary>\n    public class Inputs<TKey>\n    {\n        [JsonProperty(\"items\")]\n        public Dictionary<TKey, ISelectableItem> Items { get; set; }\n\n        [JsonProperty(\"currentItem\")]\n        /// <summary>\n        /// Gets or sets the CurrentItem\n        /// </summary>\n        public TKey CurrentItem { get; set; }\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasPowerControlWithFeedbackMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IHasPowerControlWithFeedbackMessenger\n    /// </summary>\n    public class IHasPowerControlWithFeedbackMessenger : MessengerBase\n    {\n        private readonly IHasPowerControlWithFeedback _powerControl;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"IHasPowerControlWithFeedbackMessenger\"/> class.\n        /// </summary>\n        /// <param name=\"key\">The key.</param>\n        /// <param name=\"messagePath\">The message path.</param>\n        /// <param name=\"powerControl\">The power control device</param>\n        public IHasPowerControlWithFeedbackMessenger(string key, string messagePath, IHasPowerControlWithFeedback powerControl)\n            : base(key, messagePath, powerControl as IKeyName)\n        {\n            _powerControl = powerControl;\n        }\n\n        /// <summary>\n        /// SendFullStatus method\n        /// </summary>\n        public void SendFullStatus(string id = null)\n        {\n            var messageObj = new PowerControlWithFeedbackStateMessage\n            {\n                PowerState = _powerControl.PowerIsOnFeedback.BoolValue\n            };\n\n            PostStatusMessage(messageObj, id);\n        }\n\n        /// <inheritdoc />\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/powerStatus\", (id, content) => SendFullStatus(id));\n\n            _powerControl.PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; ;\n        }\n\n        private void PowerIsOnFeedback_OutputChange(object sender, FeedbackEventArgs args)\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                powerState = args.BoolValue\n            })\n            );\n        }\n    }\n\n    /// <summary>\n    /// Represents a PowerControlWithFeedbackStateMessage\n    /// </summary>\n    public class PowerControlWithFeedbackStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Power State\n        /// </summary>\n        [JsonProperty(\"powerState\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? PowerState { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHasScheduleAwarenessMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Devices.Common.Codec;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IHasScheduleAwarenessMessenger\n    /// </summary>\n    public class IHasScheduleAwarenessMessenger : MessengerBase\n    {\n        /// <summary>\n        /// Gets or sets the ScheduleSource\n        /// </summary>\n        public IHasScheduleAwareness ScheduleSource { get; private set; }\n\n        public IHasScheduleAwarenessMessenger(string key, IHasScheduleAwareness scheduleSource, string messagePath)\n            : base(key, messagePath, scheduleSource as IKeyName)\n        {\n            ScheduleSource = scheduleSource ?? throw new ArgumentNullException(\"scheduleSource\");\n            ScheduleSource.CodecSchedule.MeetingsListHasChanged += new EventHandler<EventArgs>(CodecSchedule_MeetingsListHasChanged);\n            ScheduleSource.CodecSchedule.MeetingEventChange += new EventHandler<MeetingEventArgs>(CodecSchedule_MeetingEventChange);\n        }\n\n        protected override void RegisterActions()\n        {\n            AddAction(\"/schedule/fullStatus\", (id, content) => SendFullScheduleObject(id));\n\n            AddAction(\"/schedule/status\", (id, content) => SendFullScheduleObject(id));\n        }\n\n        private void CodecSchedule_MeetingEventChange(object sender, MeetingEventArgs e)\n        {\n            PostStatusMessage(JToken.FromObject(new MeetingChangeMessage\n            {\n                MeetingChange = new MeetingChange\n                {\n                    ChangeType = e.ChangeType.ToString(),\n                    Meeting = e.Meeting\n                }\n            })\n            );\n        }\n\n        private void CodecSchedule_MeetingsListHasChanged(object sender, EventArgs e)\n        {\n            SendFullScheduleObject();\n        }\n\n        /// <summary>\n        /// Helper method to send the full schedule data\n        /// </summary>\n        private void SendFullScheduleObject(string id = null)\n        {\n            PostStatusMessage(new FullScheduleMessage\n            {\n                Meetings = ScheduleSource.CodecSchedule.Meetings,\n                MeetingWarningMinutes = ScheduleSource.CodecSchedule.MeetingWarningMinutes\n            }, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a FullScheduleMessage\n    /// </summary>\n    public class FullScheduleMessage : DeviceStateMessageBase\n    {\n\n        /// <summary>\n        /// Gets or sets the Meetings\n        /// </summary>\n        [JsonProperty(\"meetings\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<Meeting> Meetings { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the MeetingWarningMinutes\n        /// </summary>\n        [JsonProperty(\"meetingWarningMinutes\", NullValueHandling = NullValueHandling.Ignore)]\n        public int MeetingWarningMinutes { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a MeetingChangeMessage\n    /// </summary>\n    public class MeetingChangeMessage\n    {\n\n        /// <summary>\n        /// Gets or sets the MeetingChange\n        /// </summary>\n        [JsonProperty(\"meetingChange\", NullValueHandling = NullValueHandling.Ignore)]\n        public MeetingChange MeetingChange { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a MeetingChange\n    /// </summary>\n    public class MeetingChange\n    {\n\n        /// <summary>\n        /// Gets or sets the ChangeType\n        /// </summary>\n        [JsonProperty(\"changeType\", NullValueHandling = NullValueHandling.Ignore)]\n        public string ChangeType { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Meeting\n        /// </summary>\n        [JsonProperty(\"meeting\", NullValueHandling = NullValueHandling.Ignore)]\n        public Meeting Meeting { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IHumiditySensor.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IHumiditySensorMessenger\n    /// </summary>\n    public class IHumiditySensorMessenger : MessengerBase\n    {\n        private readonly IHumiditySensor device;\n\n        public IHumiditySensorMessenger(string key, IHumiditySensor device, string messagePath)\n            : base(key, messagePath, device as IKeyName)\n        {\n            this.device = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/humidityStatus\", (id, content) => SendFullStatus(id));\n\n            device.HumidityFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>((o, a) => SendFullStatus());\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var state = new IHumiditySensorStateMessage\n            {\n                Humidity = string.Format(\"{0}%\", device.HumidityFeedback.UShortValue)\n            };\n\n            PostStatusMessage(state, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a IHumiditySensorStateMessage\n    /// </summary>\n    public class IHumiditySensorStateMessage : DeviceStateMessageBase\n    {\n\n        /// <summary>\n        /// Gets or sets the Humidity\n        /// </summary>\n        [JsonProperty(\"humidity\")]\n        public string Humidity { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ILevelControlsMessenger.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a ILevelControlsMessenger\n    /// </summary>\n    public class ILevelControlsMessenger : MessengerBase\n    {\n        private ILevelControls levelControlsDevice;\n\n        public ILevelControlsMessenger(string key, string messagePath, ILevelControls device) : base(key, messagePath, device as IKeyName)\n        {\n            levelControlsDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, context) => SendFullStatus(id));\n\n            AddAction(\"/levelStats\", (id, content) => SendFullStatus(id));\n\n            foreach (var levelControl in levelControlsDevice.LevelControlPoints)\n            {\n                // reassigning here just in case of lambda closure issues\n                var key = levelControl.Key;\n                var control = levelControl.Value;\n\n                AddAction($\"/{key}/level\", (id, content) =>\n                {\n                    var request = content.ToObject<MobileControlSimpleContent<ushort>>();\n\n                    control.SetVolume(request.Value);\n                });\n\n                AddAction($\"/{key}/muteToggle\", (id, content) =>\n                {\n                    control.MuteToggle();\n                });\n\n                AddAction($\"/{key}/muteOn\", (id, content) => control.MuteOn());\n\n                AddAction($\"/{key}/muteOff\", (id, content) => control.MuteOff());\n\n                AddAction($\"/{key}/volumeUp\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => control.VolumeUp(b)));\n\n                AddAction($\"/{key}/volumeDown\", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) => control.VolumeDown(b)));\n\n                control.VolumeLevelFeedback.OutputChange += (o, a) => PostStatusMessage(JToken.FromObject(new\n                {\n                    levelControls = new Dictionary<string, Volume>\n                    {\n                        {key, new Volume{Level = a.IntValue} }\n                    }\n                }));\n\n                control.MuteFeedback.OutputChange += (o, a) => PostStatusMessage(JToken.FromObject(new\n                {\n                    levelControls = new Dictionary<string, Volume>\n                    {\n                        {key, new Volume{Muted = a.BoolValue} }\n                    }\n                }));\n            }\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var message = new LevelControlStateMessage\n            {\n                Levels = levelControlsDevice.LevelControlPoints.ToDictionary(kv => kv.Key, kv => new Volume { Level = kv.Value.VolumeLevelFeedback.IntValue, Muted = kv.Value.MuteFeedback.BoolValue })\n            };\n\n            PostStatusMessage(message, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a LevelControlStateMessage\n    /// </summary>\n    public class LevelControlStateMessage : DeviceStateMessageBase\n    {\n        [JsonProperty(\"levelControls\")]\n        public Dictionary<string, Volume> Levels { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a LevelControlRequestMessage\n    /// </summary>\n    public class LevelControlRequestMessage\n    {\n        [JsonProperty(\"key\")]\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        public string Key { get; set; }\n\n        [JsonProperty(\"level\", NullValueHandling = NullValueHandling.Ignore)]\n        public ushort? Level { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IMatrixRoutingMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Routing;\nusing Serilog.Events;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Messenger for devices that implment IMatrixRouting\n    /// </summary>\n    public class IMatrixRoutingMessenger : MessengerBase\n    {\n        private readonly IMatrixRouting matrixDevice;\n        public IMatrixRoutingMessenger(string key, string messagePath, IMatrixRouting device) : base(key, messagePath, device as IKeyName)\n        {\n            matrixDevice = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/matrixStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/route\", (id, content) =>\n            {\n                var request = content.ToObject<MatrixRouteRequest>();\n\n                matrixDevice.Route(request.InputKey, request.OutputKey, request.RouteType);\n            });\n\n            foreach (var output in matrixDevice.OutputSlots)\n            {\n                var key = output.Key;\n                var outputSlot = output.Value;\n\n                outputSlot.OutputSlotChanged += (sender, args) =>\n                {\n                    PostStatusMessage(JToken.FromObject(new\n                    {\n                        outputs = matrixDevice.OutputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingOutput(kvp.Value))\n                    }));\n                };\n            }\n\n            foreach (var input in matrixDevice.InputSlots)\n            {\n                var key = input.Key;\n                var inputSlot = input.Value;\n\n                inputSlot.VideoSyncChanged += (sender, args) =>\n                {\n                    PostStatusMessage(JToken.FromObject(new\n                    {\n                        inputs = matrixDevice.InputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingInput(kvp.Value))\n                    }));\n                };\n\n                inputSlot.IsOnline.OutputChange += (sender, args) =>\n                {\n                    PostStatusMessage(JToken.FromObject(new\n                    {\n                        inputs = matrixDevice.InputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingInput(kvp.Value))\n                    }));\n                };\n            }\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            try\n            {\n                Debug.LogMessage(LogEventLevel.Verbose, \"InputCount: {inputCount}, OutputCount: {outputCount}\", this, matrixDevice.InputSlots.Count, matrixDevice.OutputSlots.Count);\n                var message = new MatrixStateMessage\n                {\n                    Outputs = matrixDevice.OutputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingOutput(kvp.Value)),\n                    Inputs = matrixDevice.InputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingInput(kvp.Value)),\n                };\n\n\n                PostStatusMessage(message, id);\n            }\n            catch (Exception e)\n            {\n                Debug.LogMessage(e, \"Exception Getting full status: {@exception}\", this, e);\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a MatrixStateMessage\n    /// </summary>\n    public class MatrixStateMessage : DeviceStateMessageBase\n    {\n        [JsonProperty(\"outputs\")]\n        public Dictionary<string, RoutingOutput> Outputs;\n\n        [JsonProperty(\"inputs\")]\n        public Dictionary<string, RoutingInput> Inputs;\n    }\n\n    /// <summary>\n    /// Represents a RoutingInput\n    /// </summary>\n    public class RoutingInput\n    {\n        private IRoutingInputSlot _input;\n\n        [JsonProperty(\"txDeviceKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string TxDeviceKey => _input?.TxDeviceKey;\n\n        [JsonProperty(\"slotNumber\", NullValueHandling = NullValueHandling.Ignore)]\n        public int? SlotNumber => _input?.SlotNumber;\n\n        [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]\n        [JsonProperty(\"supportedSignalTypes\", NullValueHandling = NullValueHandling.Ignore)]\n        public eRoutingSignalType? SupportedSignalTypes => _input?.SupportedSignalTypes;\n\n        [JsonProperty(\"name\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Name => _input?.Name;\n\n        [JsonProperty(\"isOnline\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsOnline => _input?.IsOnline.BoolValue;\n\n        [JsonProperty(\"videoSyncDetected\", NullValueHandling = NullValueHandling.Ignore)]\n\n        public bool? VideoSyncDetected => _input?.VideoSyncDetected;\n\n        [JsonProperty(\"key\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Key => _input?.Key;\n\n        public RoutingInput(IRoutingInputSlot input)\n        {\n            _input = input;\n        }\n    }\n\n    /// <summary>\n    /// Represents a RoutingOutput\n    /// </summary>\n    public class RoutingOutput\n    {\n        private IRoutingOutputSlot _output;\n\n\n        public RoutingOutput(IRoutingOutputSlot output)\n        {\n            _output = output;\n        }\n\n        [JsonProperty(\"rxDeviceKey\")]\n        public string RxDeviceKey => _output.RxDeviceKey;\n\n        [JsonProperty(\"currentRoutes\")]\n        public Dictionary<string, RoutingInput> CurrentRoutes => _output.CurrentRoutes.ToDictionary(kvp => kvp.Key.ToString(), kvp => new RoutingInput(kvp.Value));\n\n        [JsonProperty(\"slotNumber\")]\n        public int SlotNumber => _output.SlotNumber;\n\n        [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]\n        [JsonProperty(\"supportedSignalTypes\")]\n        public eRoutingSignalType SupportedSignalTypes => _output.SupportedSignalTypes;\n\n        [JsonProperty(\"name\")]\n        public string Name => _output.Name;\n\n        [JsonProperty(\"key\")]\n        public string Key => _output.Key;\n    }\n\n    /// <summary>\n    /// Represents a MatrixRouteRequest\n    /// </summary>\n    public class MatrixRouteRequest\n    {\n        [JsonProperty(\"outputKey\")]\n        /// <summary>\n        /// Gets or sets the OutputKey\n        /// </summary>\n        public string OutputKey { get; set; }\n\n        [JsonProperty(\"inputKey\")]\n        /// <summary>\n        /// Gets or sets the InputKey\n        /// </summary>\n        public string InputKey { get; set; }\n\n        [JsonProperty(\"routeType\")]\n        /// <summary>\n        /// Gets or sets the RouteType\n        /// </summary>\n        public eRoutingSignalType RouteType { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IProjectorScreenLiftControlMessenger.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IProjectorScreenLiftControlMessenger\n    /// </summary>\n    public class IProjectorScreenLiftControlMessenger : MessengerBase\n    {\n        private readonly IProjectorScreenLiftControl device;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"IProjectorScreenLiftControlMessenger\"/> class.\n        /// </summary>\n        /// <param name=\"key\">message key</param>\n        /// <param name=\"messagePath\">message path</param>\n        /// <param name=\"screenLiftDevice\">screen lift device</param>\n        public IProjectorScreenLiftControlMessenger(string key, string messagePath, IProjectorScreenLiftControl screenLiftDevice)\n            : base(key, messagePath, screenLiftDevice as IKeyName)\n        {\n            device = screenLiftDevice;\n        }\n\n        /// <summary>\n        /// Registers the actions for the messenger.\n        /// </summary>\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/screenliftStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/raise\", (id, content) =>\n            {\n\n                device.Raise();\n\n            });\n\n            AddAction(\"/lower\", (id, content) =>\n            {\n\n                device.Lower();\n\n            });\n\n            device.PositionChanged += Device_PositionChanged;\n\n        }\n\n        private void Device_PositionChanged(object sender, EventArgs e)\n        {\n            var state = new\n            {\n                inUpPosition = device.InUpPosition\n            };\n            PostStatusMessage(JToken.FromObject(state));\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var state = new ScreenLiftStateMessage\n            {\n                InUpPosition = device.InUpPosition,\n                Type = device.Type,\n                DisplayDeviceKey = device.DisplayDeviceKey\n            };\n\n            PostStatusMessage(state, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a ScreenLiftStateMessage\n    /// </summary>\n    public class ScreenLiftStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Gets or sets the InUpPosition\n        /// </summary>\n        [JsonProperty(\"inUpPosition\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? InUpPosition { get; set; }\n\n        /// <summary>\n        /// Gets or sets the DisplayDeviceKey\n        /// </summary>\n        [JsonProperty(\"displayDeviceKey\", NullValueHandling = NullValueHandling.Ignore)]\n        public string DisplayDeviceKey { get; set; }\n\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        [JsonConverter(typeof(StringEnumConverter))]\n        [JsonProperty(\"type\", NullValueHandling = NullValueHandling.Ignore)]\n        public eScreenLiftControlType Type { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IRunRouteActionMessenger.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\n\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a RunRouteActionMessenger\n    /// </summary>\n    public class RunRouteActionMessenger : MessengerBase\n    {\n        /// <summary>\n        /// Gets or sets the RoutingDevice\n        /// </summary>\n        public IRunRouteAction RoutingDevice { get; private set; }\n\n        public RunRouteActionMessenger(string key, IRunRouteAction routingDevice, string messagePath)\n            : base(key, messagePath, routingDevice as IKeyName)\n        {\n            RoutingDevice = routingDevice ?? throw new ArgumentNullException(\"routingDevice\");\n\n\n            if (RoutingDevice is IRoutingSink routingSink)\n            {\n                routingSink.CurrentSourceChange += RoutingSink_CurrentSourceChange;\n            }\n        }\n\n        private void RoutingSink_CurrentSourceChange(SourceListItem info, ChangeType type)\n        {\n            SendRoutingFullMessageObject();\n        }\n\n        protected override void RegisterActions()\n        {\n            AddAction(\"/fullStatus\", (id, content) => SendRoutingFullMessageObject(id));\n\n            AddAction(\"/routingStatus\", (id, content) => SendRoutingFullMessageObject(id));\n\n            AddAction(\"/source\", (id, content) =>\n                {\n                    var c = content.ToObject<SourceSelectMessageContent>();\n                    // assume no sourceListKey\n                    var sourceListKey = string.Empty;\n\n                    if (!string.IsNullOrEmpty(c.SourceListKey))\n                    {\n                        // Check for source list in content of message\n                        sourceListKey = c.SourceListKey;\n                    }\n\n                    RoutingDevice.RunRouteAction(c.SourceListItemKey, sourceListKey);\n                });\n\n            if (RoutingDevice is IRoutingSink sinkDevice)\n            {\n                sinkDevice.CurrentSourceChange += (o, a) => SendRoutingFullMessageObject();\n            }\n        }\n\n        /// <summary>\n        /// Helper method to update full status of the routing device\n        /// </summary>\n        private void SendRoutingFullMessageObject(string id = null)\n        {\n            if (RoutingDevice is IRoutingSink sinkDevice)\n            {\n                var sourceKey = sinkDevice.CurrentSourceInfoKey;\n\n                if (string.IsNullOrEmpty(sourceKey))\n                    sourceKey = \"none\";\n\n                PostStatusMessage(new RoutingStateMessage\n                {\n                    SelectedSourceKey = sourceKey\n                });\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a RoutingStateMessage\n    /// </summary>\n    public class RoutingStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Gets or sets the SelectedSourceKey\n        /// </summary>\n        [JsonProperty(\"selectedSourceKey\")]\n        public string SelectedSourceKey { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISelectableItemsMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a ISelectableItemsMessenger\n    /// </summary>\n    public class ISelectableItemsMessenger<TKey> : MessengerBase\n    {\n        private readonly ISelectableItems<TKey> itemDevice;\n\n        private readonly string _propName;\n\n        private List<string> _itemKeys = new List<string>();\n\n        /// <summary>\n        /// Constructs a messenger for a device that implements ISelectableItems<typeparamref name=\"TKey\"/>\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"messagePath\"></param>\n        /// <param name=\"device\"></param>\n        /// <param name=\"propName\"></param>\n        public ISelectableItemsMessenger(string key, string messagePath, ISelectableItems<TKey> device, string propName) : base(key, messagePath, device as IKeyName)\n        {\n            itemDevice = device;\n            _propName = propName;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, context) =>\n                SendFullStatus(id)\n            );\n\n            AddAction(\"/itemsStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/selectItem\", (id, content) =>\n            {\n                try\n                {\n                    var key = content.ToObject<TKey>();\n\n                    if (key == null)\n                    {\n                        this.LogError(\"No key specified to select\");\n                        return;\n                    }\n                    if (itemDevice.Items.ContainsKey((TKey)Convert.ChangeType(key, typeof(TKey))))\n                    {\n                        itemDevice.Items[(TKey)Convert.ChangeType(key, typeof(TKey))].Select();\n                    }\n                    else\n                    {\n                        this.LogError(\"Key {0} not found in items\", key);\n                    }\n                }\n                catch (Exception e)\n                {\n                    this.LogError(\"Error selecting item: {0}\", e.Message);\n                }\n            });\n\n            itemDevice.ItemsUpdated += (sender, args) =>\n            {\n                SetItems();\n            };\n\n            itemDevice.CurrentItemChanged += (sender, args) =>\n            {\n                SendFullStatus();\n            };\n\n            SetItems();\n        }\n\n        /// <summary>\n        /// Sets the items and registers their update events\n        /// </summary>\n        private void SetItems()\n        {\n            if (_itemKeys != null && _itemKeys.Count > 0)\n            {\n                /// Clear out any existing item actions\n                foreach (var item in _itemKeys)\n                {\n                    RemoveAction($\"/{item}\");\n                }\n\n                _itemKeys.Clear();\n            }\n\n            foreach (var item in itemDevice.Items)\n            {\n                var key = item.Key;\n                var localItem = item.Value;\n\n                AddAction($\"/{key}\", (id, content) =>\n                {\n                    localItem.Select();\n                });\n\n                _itemKeys.Add(key.ToString());\n\n                localItem.ItemUpdated -= LocalItem_ItemUpdated;\n                localItem.ItemUpdated += LocalItem_ItemUpdated;\n            }\n        }\n\n        private void LocalItem_ItemUpdated(object sender, EventArgs e)\n        {\n            SendFullStatus();\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            try\n            {\n                this.LogInformation(\"Sending full status\");\n\n                var stateObject = new ISelectableItemsStateMessage<TKey>\n                {\n                    Items = itemDevice.Items,\n                    CurrentItem = itemDevice.CurrentItem\n                };\n\n                PostStatusMessage(stateObject, id);\n            }\n            catch (Exception e)\n            {\n                this.LogError(\"Error sending full status: {0}\", e.Message);\n            }\n        }\n    }\n\n    /// <summary>\n    /// Represents a ISelectableItemsStateMessage\n    /// </summary>\n    public class ISelectableItemsStateMessage<TKey> : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Gets or sets the Items\n        /// </summary>\n        [JsonProperty(\"items\")]\n        public Dictionary<TKey, ISelectableItem> Items { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the CurrentItem\n        /// </summary>\n        [JsonProperty(\"currentItem\")]\n        public TKey CurrentItem { get; set; }\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/IShutdownPromptTimerMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IShutdownPromptTimerMessenger\n    /// </summary>\n    public class IShutdownPromptTimerMessenger : MessengerBase\n    {\n        private readonly IShutdownPromptTimer _room;\n\n        public IShutdownPromptTimerMessenger(string key, string messagePath, IShutdownPromptTimer room)\n            : base(key, messagePath, room as IKeyName)\n        {\n            _room = room;\n        }\n\n        protected override void RegisterActions()\n        {\n            AddAction(\"/status\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/shutdownPromptStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/setShutdownPromptSeconds\", (id, content) =>\n            {\n                var response = content.ToObject<int>();\n\n                _room.SetShutdownPromptSeconds(response);\n\n                SendFullStatus();\n            });\n\n            AddAction(\"/shutdownStart\", (id, content) => _room.StartShutdown(eShutdownType.Manual));\n\n            AddAction(\"/shutdownEnd\", (id, content) => _room.ShutdownPromptTimer.Finish());\n\n            AddAction(\"/shutdownCancel\", (id, content) => _room.ShutdownPromptTimer.Cancel());\n\n\n            _room.ShutdownPromptTimer.HasStarted += (sender, args) =>\n            {\n                PostEventMessage(\"timerStarted\");\n            };\n\n            _room.ShutdownPromptTimer.HasFinished += (sender, args) =>\n            {\n                PostEventMessage(\"timerFinished\");\n            };\n\n            _room.ShutdownPromptTimer.WasCancelled += (sender, args) =>\n            {\n                PostEventMessage(\"timerCancelled\");\n            };\n\n            _room.ShutdownPromptTimer.SecondsRemainingFeedback.OutputChange += (sender, args) =>\n            {\n                var status = new\n                {\n                    secondsRemaining = _room.ShutdownPromptTimer.SecondsRemainingFeedback.IntValue,\n                    percentageRemaining = _room.ShutdownPromptTimer.PercentFeedback.UShortValue\n                };\n\n                PostStatusMessage(JToken.FromObject(status));\n            };\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var status = new IShutdownPromptTimerStateMessage\n            {\n                ShutdownPromptSeconds = _room.ShutdownPromptTimer.SecondsToCount,\n                SecondsRemaining = _room.ShutdownPromptTimer.SecondsRemainingFeedback.IntValue,\n                PercentageRemaining = _room.ShutdownPromptTimer.PercentFeedback.UShortValue\n            };\n\n            PostStatusMessage(status, id);\n        }\n    }\n\n\n    /// <summary>\n    /// Represents a IShutdownPromptTimerStateMessage\n    /// </summary>\n    public class IShutdownPromptTimerStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Gets or sets the SecondsRemaining\n        /// </summary>\n        [JsonProperty(\"secondsRemaining\")]\n        public int SecondsRemaining { get; set; }\n\n        /// <summary>\n        /// Gets or sets the PercentageRemaining\n        /// </summary>\n        [JsonProperty(\"percentageRemaining\")]\n        public int PercentageRemaining { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ShutdownPromptSeconds\n        /// </summary>\n        [JsonProperty(\"shutdownPromptSeconds\")]\n        public int ShutdownPromptSeconds { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ISwitchedOutputMessenger.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.CrestronIO;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a ISwitchedOutputMessenger\n    /// </summary>\n    public class ISwitchedOutputMessenger : MessengerBase\n    {\n\n        private readonly ISwitchedOutput device;\n\n        public ISwitchedOutputMessenger(string key, ISwitchedOutput device, string messagePath)\n            : base(key, messagePath, device as IKeyName)\n        {\n            this.device = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/switchedOutputStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/on\", (id, content) =>\n            {\n\n                device.On();\n\n            });\n\n            AddAction(\"/off\", (id, content) =>\n            {\n\n                device.Off();\n\n            });\n\n            device.OutputIsOnFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>((o, a) => SendFullStatus());\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var state = new ISwitchedOutputStateMessage\n            {\n                IsOn = device.OutputIsOnFeedback.BoolValue\n            };\n\n            PostStatusMessage(state, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a ISwitchedOutputStateMessage\n    /// </summary>\n    public class ISwitchedOutputStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Gets or sets the IsOn\n        /// </summary>\n        [JsonProperty(\"isOn\")]\n        public bool IsOn { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a ITechPasswordMessenger\n    /// </summary>\n    public class ITechPasswordMessenger : MessengerBase\n    {\n        private readonly ITechPassword _room;\n\n        public ITechPasswordMessenger(string key, string messagePath, ITechPassword room)\n            : base(key, messagePath, room as IKeyName)\n        {\n            _room = room;\n        }\n\n        protected override void RegisterActions()\n        {\n\n            AddAction(\"/status\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/techPasswordStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/validateTechPassword\", (id, content) =>\n            {\n                var password = content.Value<string>(\"password\");\n\n                _room.ValidateTechPassword(password);\n            });\n\n            AddAction(\"/setTechPassword\", (id, content) =>\n            {\n                var response = content.ToObject<SetTechPasswordContent>();\n\n                _room.SetTechPassword(response.OldPassword, response.NewPassword);\n            });\n\n            _room.TechPasswordChanged += (sender, args) =>\n            {\n                PostEventMessage(\"passwordChangedSuccessfully\");\n            };\n\n            _room.TechPasswordValidateResult += (sender, args) =>\n            {\n                var evt = new ITechPasswordEventMessage\n                {\n                    IsValid = args.IsValid\n                };\n\n                PostEventMessage(evt, \"passwordValidationResult\");\n            };\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            var status = new ITechPasswordStateMessage\n            {\n                TechPasswordLength = _room.TechPasswordLength\n            };\n\n            PostStatusMessage(status, id);\n        }\n\n    }\n\n    /// <summary>\n    /// Represents a ITechPasswordStateMessage\n    /// </summary>\n    public class ITechPasswordStateMessage : DeviceStateMessageBase\n    {\n        [JsonProperty(\"techPasswordLength\", NullValueHandling = NullValueHandling.Ignore)]\n        public int? TechPasswordLength { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a ITechPasswordEventMessage\n    /// </summary>\n    public class ITechPasswordEventMessage : DeviceEventMessageBase\n    {\n        [JsonProperty(\"isValid\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsValid { get; set; }\n    }\n\n    internal class SetTechPasswordContent\n    {\n        [JsonProperty(\"oldPassword\")]\n        /// <summary>\n        /// Gets or sets the OldPassword\n        /// </summary>\n        public string OldPassword { get; set; }\n\n        [JsonProperty(\"newPassword\")]\n        /// <summary>\n        /// Gets or sets the NewPassword\n        /// </summary>\n        public string NewPassword { get; set; }\n    }\n\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITemperatureSensorMessenger.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a ITemperatureSensorMessenger\n    /// </summary>\n    public class ITemperatureSensorMessenger : MessengerBase\n    {\n        private readonly ITemperatureSensor device;\n\n        public ITemperatureSensorMessenger(string key, ITemperatureSensor device, string messagePath)\n            : base(key, messagePath, device as IKeyName)\n        {\n            this.device = device;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/temperatureStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/setTemperatureUnitsToCelcius\", (id, content) =>\n            {\n                device.SetTemperatureFormat(true);\n            });\n\n            AddAction(\"/setTemperatureUnitsToFahrenheit\", (id, content) =>\n            {\n                device.SetTemperatureFormat(false);\n            });\n\n            device.TemperatureFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>((o, a) => SendFullStatus());\n            device.TemperatureInCFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>((o, a) => SendFullStatus());\n        }\n\n        private void SendFullStatus(string id = null)\n        {\n            // format the temperature to a string with one decimal place\n            var tempString = string.Format(\"{0}.{1}\", device.TemperatureFeedback.UShortValue / 10, device.TemperatureFeedback.UShortValue % 10);\n\n            var state = new ITemperatureSensorStateMessage\n            {\n                Temperature = tempString,\n                TemperatureInCelsius = device.TemperatureInCFeedback.BoolValue\n            };\n\n            PostStatusMessage(state, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a ITemperatureSensorStateMessage\n    /// </summary>\n    public class ITemperatureSensorStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Gets or sets the Temperature\n        /// </summary>\n        [JsonProperty(\"temperature\")]\n        public string Temperature { get; set; }\n\n        /// <summary>\n        /// Gets or sets the TemperatureInCelsius\n        /// </summary>\n        [JsonProperty(\"temperatureInCelsius\")]\n        public bool TemperatureInCelsius { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/LightingBaseMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Lighting;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a ILightingScenesMessenger\n    /// </summary>\n    public class ILightingScenesMessenger : MessengerBase\n    {\n        private ILightingScenes lightingScenesDevice;\n\n        public ILightingScenesMessenger(string key, ILightingScenes device, string messagePath)\n            : base(key, messagePath, device as IKeyName)\n        {\n            lightingScenesDevice = device ?? throw new ArgumentNullException(\"device\");\n\n            lightingScenesDevice.LightingSceneChange += new EventHandler<LightingSceneChangeEventArgs>(LightingDevice_LightingSceneChange);\n        }\n\n        private void LightingDevice_LightingSceneChange(object sender, LightingSceneChangeEventArgs e)\n        {\n            var state = new LightingBaseStateMessage\n            {\n                CurrentLightingScene = e.CurrentLightingScene\n            };\n\n            PostStatusMessage(state);\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/lightingStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/selectScene\", (id, content) =>\n            {\n                var s = content.ToObject<LightingScene>();\n                lightingScenesDevice.SelectScene(s);\n            });\n\n            if (!(lightingScenesDevice is ILightingScenesDynamic lightingScenesDynamic))\n                return;\n\n            lightingScenesDynamic.LightingScenesUpdated += (s, e) => SendFullStatus();\n        }\n\n\n        private void SendFullStatus(string id = null)\n        {\n            var state = new LightingBaseStateMessage\n            {\n                Scenes = lightingScenesDevice.LightingScenes,\n                CurrentLightingScene = lightingScenesDevice.CurrentLightingScene\n            };\n\n            PostStatusMessage(state, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a LightingBaseStateMessage\n    /// </summary>\n    public class LightingBaseStateMessage : DeviceStateMessageBase\n    {\n        /// <summary>\n        /// Gets or sets the Scenes\n        /// </summary>\n        [JsonProperty(\"scenes\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<LightingScene> Scenes { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the CurrentLightingScene\n        /// </summary>\n        [JsonProperty(\"currentLightingScene\", NullValueHandling = NullValueHandling.Ignore)]\n        public LightingScene CurrentLightingScene { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Crestron.SimplSharp.Net;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Provides a messaging bridge\n    /// </summary>\n    public abstract class MessengerBase : EssentialsDevice, IMobileControlMessengerWithSubscriptions\n    {\n        /// <summary>\n        /// The device this messenger is associated with\n        /// </summary>\n        protected IKeyName _device;\n\n        /// <summary>\n        /// Enable subscriptions\n        /// </summary>\n        protected bool enableMessengerSubscriptions;\n\n        /// <summary>\n        /// List of clients subscribed to this messenger\n        /// </summary>\n        /// <remarks>\n        /// Unsoliciited feedback from a device in a messenger will ONLY be sent to devices in this subscription list. When a client disconnects, it's ID will be removed from the collection.\n        /// </remarks>\n        private readonly HashSet<string> subscriberIds = new HashSet<string>();\n\n        /// <summary>\n        /// Lock object for thread-safe access to SubscriberIds\n        /// </summary>\n        private readonly object _subscriberLock = new object();\n\n        private readonly List<string> _deviceInterfaces;\n\n        private readonly Dictionary<string, Action<string, JToken>> _actions = new Dictionary<string, Action<string, JToken>>();\n\n        /// <summary>\n        /// Gets the DeviceKey\n        /// </summary>\n        public string DeviceKey => _device?.Key ?? \"\";\n\n\n        /// <summary>\n        /// Gets or sets the AppServerController\n        /// </summary>\n        public IMobileControl AppServerController { get; private set; }\n\n        /// <summary>\n        /// Gets or sets the MessagePath\n        /// </summary>\n        public string MessagePath { get; private set; }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"messagePath\"></param>\n        protected MessengerBase(string key, string messagePath)\n            : base(key)\n        {\n            Key = key;\n\n            if (string.IsNullOrEmpty(messagePath))\n                throw new ArgumentException(\"messagePath must not be empty or null\");\n\n            MessagePath = messagePath;\n        }\n\n        /// <summary>\n        /// Constructor for a messenger associated with a device\n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"messagePath\"></param>\n        /// <param name=\"device\"></param>\n        protected MessengerBase(string key, string messagePath, IKeyName device)\n            : this(key, messagePath)\n        {\n            _device = device;\n\n            _deviceInterfaces = GetInterfaces(_device as Device);\n        }\n\n        /// <summary>\n        /// Gets the interfaces implmented on the device\n        /// </summary>\n        /// <param name=\"device\"></param>\n        /// <returns></returns>\n        private List<string> GetInterfaces(Device device)\n        {\n            return device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List<string>();\n        }\n\n        /// <summary>\n        /// Registers this messenger with appserver controller\n        /// </summary>\n        /// <param name=\"appServerController\"></param>\n        public void RegisterWithAppServer(IMobileControl appServerController)\n        {\n            AppServerController = appServerController ?? throw new ArgumentNullException(\"appServerController\");\n\n            AppServerController.AddAction(this, HandleMessage);\n\n            RegisterActions();\n        }\n\n        /// <summary>\n        /// Register this messenger with appserver controller\n        /// </summary>\n        /// <param name=\"appServerController\">Parent controller for this messenger</param>\n        /// <param name=\"enableMessengerSubscriptions\">Enable subscriptions</param>\n        public void RegisterWithAppServer(IMobileControl appServerController, bool enableMessengerSubscriptions)\n        {\n            this.enableMessengerSubscriptions = enableMessengerSubscriptions;\n            AppServerController = appServerController ?? throw new ArgumentNullException(\"appServerController\");\n\n            AppServerController.AddAction(this, HandleMessage);\n\n            RegisterActions();\n        }\n\n        private void HandleMessage(string path, string id, JToken content)\n        {\n            // replace base path with empty string. Should leave something like /fullStatus\n            var route = path.Replace(MessagePath, string.Empty);\n\n            if (!_actions.TryGetValue(route, out var action))\n            {\n                return;\n            }\n\n            this.LogDebug(\"Executing action for path {path}\", path);\n\n            action(id, content);\n        }\n\n        /// <summary>\n        /// Adds an action for a given path\n        /// </summary>\n        /// <param name=\"path\"></param>\n        /// <param name=\"action\"></param>\n        protected void AddAction(string path, Action<string, JToken> action)\n        {\n            if (_actions.ContainsKey(path))\n            {\n                return;\n            }\n\n            _actions.Add(path, action);\n        }\n\n        /// <summary>\n        /// GetActionPaths method\n        /// </summary>\n        public List<string> GetActionPaths()\n        {\n            return _actions.Keys.ToList();\n        }\n\n        /// <summary>\n        /// Removes an action for a given path\n        /// </summary>\n        /// <param name=\"path\"></param>\n        protected void RemoveAction(string path)\n        {\n            if (!_actions.ContainsKey(path))\n            {\n                return;\n            }\n\n            _actions.Remove(path);\n        }\n\n        /// <summary>\n        /// Implemented in extending classes. Wire up API calls and feedback here\n        /// </summary>\n        protected virtual void RegisterActions()\n        {\n\n        }\n\n        /// <summary>\n        /// Add client to the susbscription list for unsolicited feedback\n        /// </summary>\n        /// <param name=\"clientId\">Client ID to add</param>\n        protected void SubscribeClient(string clientId)\n        {\n            if (!enableMessengerSubscriptions)\n            {\n                return;\n            }\n\n            lock (_subscriberLock)\n            {\n                if (!subscriberIds.Add(clientId))\n                {\n                    this.LogVerbose(\"Client {clientId} already subscribed\", clientId);\n                    return;\n                }\n            }\n\n            this.LogDebug(\"Client {clientId} subscribed\", clientId);\n        }\n\n        /// <summary>\n        /// Remove a client from the subscription list\n        /// </summary>\n        /// <param name=\"clientId\">Client ID to remove</param>\n        public void UnsubscribeClient(string clientId)\n        {\n            if (!enableMessengerSubscriptions)\n            {\n                return;\n            }\n\n            bool wasSubscribed;\n            lock (_subscriberLock)\n            {\n                wasSubscribed = subscriberIds.Contains(clientId);\n                if (wasSubscribed)\n                {\n                    subscriberIds.Remove(clientId);\n                }\n            }\n\n            if (!wasSubscribed)\n            {\n                this.LogVerbose(\"Client with ID {clientId} is not subscribed\", clientId);\n                return;\n            }\n\n            this.LogDebug(\"Client with ID {clientId} unsubscribed\", clientId);\n        }\n\n        /// <summary>\n        /// Helper for posting status message\n        /// </summary>\n        /// <param name=\"message\"></param>\n        /// <param name=\"clientId\">Optional client id that will direct the message back to only that client</param>\n        protected void PostStatusMessage(DeviceStateMessageBase message, string clientId = null)\n        {\n            try\n            {\n                if (message == null)\n                {\n                    throw new ArgumentNullException(\"message\");\n                }\n\n                if (_device == null)\n                {\n                    throw new ArgumentNullException(\"device\");\n                }\n\n                message.SetInterfaces(_deviceInterfaces);\n\n                message.Key = _device.Key;\n\n                message.Name = _device.Name;\n\n                var token = JToken.FromObject(message);\n\n                PostStatusMessage(token, MessagePath, clientId);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Exception posting status message for {messagePath} to {clientId}: {message}\", MessagePath, clientId ?? \"all clients\", ex.Message);\n                this.LogDebug(ex, \"Stack trace: \");\n            }\n        }\n\n        /// <summary>\n        /// Helper for posting status message\n        /// </summary>\n        /// <param name=\"type\"></param>\n        /// <param name=\"deviceState\"></param>\n        /// <param name=\"clientId\">Optional client id that will direct the message back to only that client</param>\n        protected void PostStatusMessage(string type, DeviceStateMessageBase deviceState, string clientId = null)\n        {\n            try\n            {\n                //Debug.Console(2, this, \"*********************Setting DeviceStateMessageProperties on MobileControlResponseMessage\");\n                deviceState.SetInterfaces(_deviceInterfaces);\n\n                deviceState.Key = _device.Key;\n\n                deviceState.Name = _device.Name;\n\n                deviceState.MessageBasePath = MessagePath;\n\n                var token = JToken.FromObject(deviceState);\n\n                PostStatusMessage(token, type, clientId);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Exception posting status message for {type} to {clientId}: {message}\", type, clientId ?? \"all clients\", ex.Message);\n                this.LogDebug(ex, \"Stack trace: \");\n            }\n        }\n\n        /// <summary>\n        /// Helper for posting status message\n        /// </summary>\n        /// <param name=\"content\"></param>\n        /// <param name=\"type\"></param>\n        /// <param name=\"clientId\">Optional client id that will direct the message back to only that client</param>\n        protected void PostStatusMessage(JToken content, string type = \"\", string clientId = null)\n        {\n            try\n            {\n                // Allow for legacy method to continue without subscriptions\n                if (!enableMessengerSubscriptions)\n                {\n                    AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = clientId, Content = content });\n                    return;\n                }\n\n                // handle subscription feedback\n                // If client is null or empty, this message is unsolicited feedback. Iterate through the subscriber list and send to all interested parties\n                if (string.IsNullOrEmpty(clientId))\n                {\n                    // Create a snapshot of subscribers to avoid collection modification during iteration\n                    List<string> subscriberSnapshot;\n                    lock (_subscriberLock)\n                    {\n                        subscriberSnapshot = new List<string>(subscriberIds);\n                    }\n\n                    foreach (var client in subscriberSnapshot)\n                    {\n                        AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = client, Content = content });\n                    }\n\n                    return;\n                }\n\n                SubscribeClient(clientId);\n\n                AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = clientId, Content = content });\n            }\n            catch (Exception ex)\n            {\n                this.LogError(\"Exception posting status message: {message}\", ex.Message);\n                this.LogDebug(ex, \"Stack Trace: \");\n            }\n        }\n\n        /// <summary>\n        /// Helper for posting event message\n        /// </summary>\n        /// <param name=\"message\"></param>\n        protected void PostEventMessage(DeviceEventMessageBase message)\n        {\n            message.Key = _device.Key;\n\n            message.Name = _device.Name;\n\n            AppServerController?.SendMessageObject(new MobileControlMessage\n            {\n                Type = $\"/event{MessagePath}/{message.EventType}\",\n                Content = JToken.FromObject(message),\n            });\n        }\n\n        /// <summary>\n        /// Helper for posting event message\n        /// </summary>\n        /// <param name=\"message\"></param>\n        /// <param name=\"eventType\"></param>\n        protected void PostEventMessage(DeviceEventMessageBase message, string eventType)\n        {\n            message.Key = _device.Key;\n\n            message.Name = _device.Name;\n\n            message.EventType = eventType;\n\n            AppServerController?.SendMessageObject(new MobileControlMessage\n            {\n                Type = $\"/event{MessagePath}/{eventType}\",\n                Content = JToken.FromObject(message),\n            });\n        }\n\n        /// <summary>\n        /// Helper for posting event message with no content\n        /// </summary>\n        /// <param name=\"eventType\"></param>\n        protected void PostEventMessage(string eventType)\n        {\n            AppServerController?.SendMessageObject(new MobileControlMessage\n            {\n                Type = $\"/event{MessagePath}/{eventType}\",\n                Content = JToken.FromObject(new { }),\n            });\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/PressAndHoldHandler.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Handler for press/hold/release messages\n    /// </summary>\n    public static class PressAndHoldHandler\n    {\n        private const long ButtonHeartbeatInterval = 1000;\n\n        private static readonly Dictionary<string, CTimer> _pushedActions = new Dictionary<string, CTimer>();\n\n        private static readonly Dictionary<string, Action<string, Action<bool>>> _pushedActionHandlers;\n\n        static PressAndHoldHandler()\n        {\n            _pushedActionHandlers = new Dictionary<string, Action<string, Action<bool>>>\n            {\n                {\"pressed\", AddTimer },\n                {\"held\", ResetTimer },\n                {\"released\", StopTimer }\n            };\n        }\n\n        private static void AddTimer(string deviceKey, Action<bool> action)\n        {\n            Debug.LogDebug(\"Attempting to add timer for {deviceKey}\", deviceKey);\n\n            if (_pushedActions.TryGetValue(deviceKey, out CTimer cancelTimer))\n            {\n                Debug.LogDebug(\"Timer for {deviceKey} already exists\", deviceKey);\n                return;\n            }\n\n            Debug.LogDebug(\"Adding timer for {deviceKey} with due time {dueTime}\", deviceKey, ButtonHeartbeatInterval);\n\n            action(true);\n\n            cancelTimer = new CTimer(o =>\n            {\n                Debug.LogDebug(\"Timer expired for {deviceKey}\", deviceKey);\n\n                action(false);\n\n                _pushedActions.Remove(deviceKey);\n            }, ButtonHeartbeatInterval);\n\n            _pushedActions.Add(deviceKey, cancelTimer);\n        }\n\n        private static void ResetTimer(string deviceKey, Action<bool> action)\n        {\n            Debug.LogDebug(\"Attempting to reset timer for {deviceKey}\", deviceKey);\n\n            if (!_pushedActions.TryGetValue(deviceKey, out CTimer cancelTimer))\n            {\n                Debug.LogDebug(\"Timer for {deviceKey} not found\", deviceKey);\n                return;\n            }\n\n            Debug.LogDebug(\"Resetting timer for {deviceKey} with due time {dueTime}\", deviceKey, ButtonHeartbeatInterval);\n\n            cancelTimer.Reset(ButtonHeartbeatInterval);\n        }\n\n        private static void StopTimer(string deviceKey, Action<bool> action)\n        {\n            Debug.LogDebug(\"Attempting to stop timer for {deviceKey}\", deviceKey);\n\n            if (!_pushedActions.TryGetValue(deviceKey, out CTimer cancelTimer))\n            {\n                Debug.LogDebug(\"Timer for {deviceKey} not found\", deviceKey);\n                return;\n            }\n\n            Debug.LogDebug(\"Stopping timer for {deviceKey} with due time {dueTime}\", deviceKey, ButtonHeartbeatInterval);\n\n            action(false);\n            cancelTimer.Stop();\n            _pushedActions.Remove(deviceKey);\n        }\n\n        public static Action<string, Action<bool>> GetPressAndHoldHandler(string value)\n        {\n            Debug.LogDebug(\"Getting press and hold handler for {value}\", value);\n\n            if (!_pushedActionHandlers.TryGetValue(value, out Action<string, Action<bool>> handler))\n            {\n                Debug.LogDebug(\"Press and hold handler for {value} not found\", value);\n                return null;\n            }\n\n            Debug.LogDebug(\"Got handler for {value}\", value);\n\n            return handler;\n        }\n\n        /// <summary>\n        /// HandlePressAndHold method\n        /// </summary>\n        public static void HandlePressAndHold(string deviceKey, JToken content, Action<bool> action)\n        {\n            var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n            Debug.LogDebug(\"Handling press and hold message of {type} for {deviceKey}\", msg.Value, deviceKey);\n\n            var timerHandler = GetPressAndHoldHandler(msg.Value);\n\n            if (timerHandler == null)\n            {\n                return;\n            }\n\n            timerHandler(deviceKey, action);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/RoomEventScheduleMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Room.Config;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a RoomEventScheduleMessenger\n    /// </summary>\n    public class RoomEventScheduleMessenger : MessengerBase\n    {\n        private readonly IRoomEventSchedule _room;\n\n\n        public RoomEventScheduleMessenger(string key, string messagePath, IRoomEventSchedule room)\n            : base(key, messagePath, room as IKeyName)\n        {\n            _room = room;\n        }\n\n        #region Overrides of MessengerBase\n\n        protected override void RegisterActions()\n        {\n            AddAction(\"/saveScheduledEvents\", (id, content) => SaveScheduledEvents(content.ToObject<List<ScheduledEventConfig>>()));\n            AddAction(\"/status\", (id, content) =>\n            {\n                var events = _room.GetScheduledEvents();\n\n                SendFullStatus(events, id);\n            });\n\n            AddAction(\"/scheduledEventsStatus\", (id, content) =>\n            {\n                var events = _room.GetScheduledEvents();\n\n                SendFullStatus(events, id);\n            });\n\n            _room.ScheduledEventsChanged += (sender, args) => SendFullStatus(args.ScheduledEvents);\n        }\n\n        #endregion\n\n        private void SaveScheduledEvents(List<ScheduledEventConfig> events)\n        {\n            foreach (var evt in events)\n            {\n                SaveScheduledEvent(evt);\n            }\n        }\n\n        private void SaveScheduledEvent(ScheduledEventConfig eventConfig)\n        {\n            try\n            {\n                _room.AddOrUpdateScheduledEvent(eventConfig);\n            }\n            catch (Exception ex)\n            {\n                this.LogException(ex, \"Exception saving event\");\n            }\n        }\n\n        private void SendFullStatus(List<ScheduledEventConfig> events, string id = null)\n        {\n\n            var message = new RoomEventScheduleStateMessage\n            {\n                ScheduleEvents = events,\n            };\n\n            PostStatusMessage(message, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a RoomEventScheduleStateMessage\n    /// </summary>\n    public class RoomEventScheduleStateMessage : DeviceStateMessageBase\n    {\n        [JsonProperty(\"scheduleEvents\")]\n        /// <summary>\n        /// Gets or sets the ScheduleEvents\n        /// </summary>\n        public List<ScheduledEventConfig> ScheduleEvents { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLAtcMessenger.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Devices.Common.Codec;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    // ReSharper disable once InconsistentNaming\n    /// <summary>\n    /// Represents a SIMPLAtcMessenger\n    /// </summary>\n    public class SIMPLAtcMessenger : MessengerBase\n    {\n        private readonly BasicTriList _eisc;\n\n        /// <summary>\n        /// Gets or sets the JoinMap\n        /// </summary>\n        public SIMPLAtcJoinMap JoinMap { get; private set; }\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        private readonly CodecActiveCallItem _currentCallItem;\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"eisc\"></param>\n        /// <param name=\"messagePath\"></param>\n        public SIMPLAtcMessenger(string key, BasicTriList eisc, string messagePath)\n            : base(key, messagePath)\n        {\n            _eisc = eisc;\n\n            JoinMap = new SIMPLAtcJoinMap(201);\n\n            _currentCallItem = new CodecActiveCallItem { Type = eCodecCallType.Audio, Id = \"-audio-\" };\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void SendFullStatus()\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                calls = GetCurrentCallList(),\n                currentCallString = _eisc.GetString(JoinMap.CurrentCallName.JoinNumber),\n                currentDialString = _eisc.GetString(JoinMap.CurrentDialString.JoinNumber),\n                isInCall = _eisc.GetString(JoinMap.HookState.JoinNumber) == \"Connected\"\n            })\n            );\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"appServerController\"></param>\n        protected override void RegisterActions()\n        {\n            //EISC.SetStringSigAction(SCurrentDialString, s => PostStatusMessage(new { currentDialString = s }));\n\n            _eisc.SetStringSigAction(JoinMap.HookState.JoinNumber, s =>\n            {\n                _currentCallItem.Status = (eCodecCallStatus)Enum.Parse(typeof(eCodecCallStatus), s, true);\n                //GetCurrentCallList();\n                SendFullStatus();\n            });\n\n            _eisc.SetStringSigAction(JoinMap.CurrentCallNumber.JoinNumber, s =>\n            {\n                _currentCallItem.Number = s;\n                SendCallsList();\n            });\n\n            _eisc.SetStringSigAction(JoinMap.CurrentCallName.JoinNumber, s =>\n            {\n                _currentCallItem.Name = s;\n                SendCallsList();\n            });\n\n            _eisc.SetStringSigAction(JoinMap.CallDirection.JoinNumber, s =>\n            {\n                _currentCallItem.Direction = (eCodecCallDirection)Enum.Parse(typeof(eCodecCallDirection), s, true);\n                SendCallsList();\n            });\n\n            // Add press and holds using helper\n            //Action<string, uint> addPhAction = (s, u) => \n            //    AppServerController.AddAction(MessagePath + s, new PressAndHoldAction(b => _eisc.SetBool(u, b)));\n\n            // Add straight pulse calls\n            void addAction(string s, uint u) =>\n                AddAction(s, (id, content) => _eisc.PulseBool(u, 100));\n            addAction(\"/endCallById\", JoinMap.EndCall.JoinNumber);\n            addAction(\"/endAllCalls\", JoinMap.EndCall.JoinNumber);\n            addAction(\"/acceptById\", JoinMap.IncomingAnswer.JoinNumber);\n            addAction(\"/rejectById\", JoinMap.IncomingReject.JoinNumber);\n\n            var speeddialStart = JoinMap.SpeedDialStart.JoinNumber;\n            var speeddialEnd = JoinMap.SpeedDialStart.JoinNumber + JoinMap.SpeedDialStart.JoinSpan;\n\n            var speedDialIndex = 1;\n            for (uint i = speeddialStart; i < speeddialEnd; i++)\n            {\n                addAction(string.Format(\"/speedDial{0}\", speedDialIndex), i);\n                speedDialIndex++;\n            }\n\n            // Get status\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus());\n            // Dial on string\n            AddAction(\"/dial\",\n                (id, content) =>\n                {\n                    var msg = content.ToObject<MobileControlSimpleContent<string>>();\n                    _eisc.SetString(JoinMap.CurrentDialString.JoinNumber, msg.Value);\n                });\n            // Pulse DTMF\n            AddAction(\"/dtmf\", (id, content) =>\n            {\n                var s = content.ToObject<MobileControlSimpleContent<string>>();\n\n                var join = JoinMap.Joins[s.Value];\n                if (join != null)\n                {\n                    if (join.JoinNumber > 0)\n                    {\n                        _eisc.PulseBool(join.JoinNumber, 100);\n                    }\n                }\n            });\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void SendCallsList()\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                calls = GetCurrentCallList(),\n            })\n            );\n        }\n\n        /// <summary>\n        /// Turns the \n        /// </summary>\n        /// <returns></returns>\n        private List<CodecActiveCallItem> GetCurrentCallList()\n        {\n            return _currentCallItem.Status == eCodecCallStatus.Disconnected\n                ? new List<CodecActiveCallItem>()\n                : new List<CodecActiveCallItem> { _currentCallItem };\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLCameraMessenger.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.Bridges;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    // ReSharper disable once InconsistentNaming\n    /// <summary>\n    /// Represents a SIMPLCameraMessenger\n    /// </summary>\n    public class SIMPLCameraMessenger : MessengerBase\n    {\n        private readonly BasicTriList _eisc;\n\n        private readonly CameraControllerJoinMap _joinMap;\n\n\n        public SIMPLCameraMessenger(string key, BasicTriList eisc, string messagePath, uint joinStart)\n            : base(key, messagePath)\n        {\n            _eisc = eisc;\n\n            _joinMap = new CameraControllerJoinMap(joinStart);\n\n            _eisc.SetUShortSigAction(_joinMap.NumberOfPresets.JoinNumber, u => SendCameraFullMessageObject());\n\n            _eisc.SetBoolSigAction(_joinMap.CameraModeAuto.JoinNumber, b => PostCameraMode());\n            _eisc.SetBoolSigAction(_joinMap.CameraModeManual.JoinNumber, b => PostCameraMode());\n            _eisc.SetBoolSigAction(_joinMap.CameraModeOff.JoinNumber, b => PostCameraMode());\n        }\n\n\n        protected override void RegisterActions()\n        {\n            AddAction(\"/fullStatus\", (id, content) => SendCameraFullMessageObject());\n\n            // Add press and holds using helper action\n            void addPhAction(string s, uint u) =>\n                AddAction(s, (id, content) => HandleCameraPressAndHold(content, b => _eisc.SetBool(u, b)));\n            addPhAction(\"/cameraUp\", _joinMap.TiltUp.JoinNumber);\n            addPhAction(\"/cameraDown\", _joinMap.TiltDown.JoinNumber);\n            addPhAction(\"/cameraLeft\", _joinMap.PanLeft.JoinNumber);\n            addPhAction(\"/cameraRight\", _joinMap.PanRight.JoinNumber);\n            addPhAction(\"/cameraZoomIn\", _joinMap.ZoomIn.JoinNumber);\n            addPhAction(\"/cameraZoomOut\", _joinMap.ZoomOut.JoinNumber);\n\n            void addAction(string s, uint u) =>\n                AddAction(s, (id, content) => _eisc.PulseBool(u, 100));\n\n            addAction(\"/cameraModeAuto\", _joinMap.CameraModeAuto.JoinNumber);\n            addAction(\"/cameraModeManual\", _joinMap.CameraModeManual.JoinNumber);\n            addAction(\"/cameraModeOff\", _joinMap.CameraModeOff.JoinNumber);\n\n            var presetStart = _joinMap.PresetRecallStart.JoinNumber;\n            var presetEnd = _joinMap.PresetRecallStart.JoinNumber + _joinMap.PresetRecallStart.JoinSpan;\n\n            int presetId = 1;\n            // camera presets\n            for (uint i = presetStart; i <= presetEnd; i++)\n            {\n                addAction(\"/cameraPreset\" + (presetId), i);\n                presetId++;\n            }\n        }\n\n        private void HandleCameraPressAndHold(JToken content, Action<bool> cameraAction)\n        {\n            var state = content.ToObject<MobileControlSimpleContent<string>>();\n\n            var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);\n            if (timerHandler == null)\n            {\n                return;\n            }\n\n            timerHandler(state.Value, cameraAction);\n\n            cameraAction(state.Value.Equals(\"true\", StringComparison.InvariantCultureIgnoreCase));\n        }\n\n        /// <summary>\n        /// CustomUnregisterWithAppServer method\n        /// </summary>\n        public void CustomUnregisterWithAppServer(IMobileControl appServerController)\n        {\n            appServerController.RemoveAction(MessagePath + \"/fullStatus\");\n\n            appServerController.RemoveAction(MessagePath + \"/cameraUp\");\n            appServerController.RemoveAction(MessagePath + \"/cameraDown\");\n            appServerController.RemoveAction(MessagePath + \"/cameraLeft\");\n            appServerController.RemoveAction(MessagePath + \"/cameraRight\");\n            appServerController.RemoveAction(MessagePath + \"/cameraZoomIn\");\n            appServerController.RemoveAction(MessagePath + \"/cameraZoomOut\");\n            appServerController.RemoveAction(MessagePath + \"/cameraModeAuto\");\n            appServerController.RemoveAction(MessagePath + \"/cameraModeManual\");\n            appServerController.RemoveAction(MessagePath + \"/cameraModeOff\");\n\n            _eisc.SetUShortSigAction(_joinMap.NumberOfPresets.JoinNumber, null);\n\n            _eisc.SetBoolSigAction(_joinMap.CameraModeAuto.JoinNumber, null);\n            _eisc.SetBoolSigAction(_joinMap.CameraModeManual.JoinNumber, null);\n            _eisc.SetBoolSigAction(_joinMap.CameraModeOff.JoinNumber, null);\n        }\n\n        /// <summary>\n        /// Helper method to update the full status of the camera\n        /// </summary>\n        private void SendCameraFullMessageObject()\n        {\n            var presetList = new List<CameraPreset>();\n\n            // Build a list of camera presets based on the names and count \n            if (_eisc.GetBool(_joinMap.SupportsPresets.JoinNumber))\n            {\n                var presetStart = _joinMap.PresetLabelStart.JoinNumber;\n                var presetEnd = _joinMap.PresetLabelStart.JoinNumber + _joinMap.NumberOfPresets.JoinNumber;\n\n                var presetId = 1;\n                for (uint i = presetStart; i < presetEnd; i++)\n                {\n                    var presetName = _eisc.GetString(i);\n                    var preset = new CameraPreset(presetId, presetName, string.IsNullOrEmpty(presetName), true);\n                    presetList.Add(preset);\n                    presetId++;\n                }\n            }\n\n            PostStatusMessage(JToken.FromObject(new\n            {\n                cameraMode = GetCameraMode(),\n                hasPresets = _eisc.GetBool(_joinMap.SupportsPresets.JoinNumber),\n                presets = presetList\n            })\n            );\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void PostCameraMode()\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                cameraMode = GetCameraMode()\n            }));\n        }\n\n        /// <summary>\n        /// Computes the current camera mode\n        /// </summary>\n        /// <returns></returns>\n        private string GetCameraMode()\n        {\n            string m;\n            if (_eisc.GetBool(_joinMap.CameraModeAuto.JoinNumber)) m = eCameraControlMode.Auto.ToString().ToLower();\n            else if (_eisc.GetBool(_joinMap.CameraModeManual.JoinNumber))\n                m = eCameraControlMode.Manual.ToString().ToLower();\n            else m = eCameraControlMode.Off.ToString().ToLower();\n            return m;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLDirectRouteMessenger.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a SimplDirectRouteMessenger\n    /// </summary>\n    public class SimplDirectRouteMessenger : MessengerBase\n    {\n        private readonly BasicTriList _eisc;\n\n        /// <summary>\n        /// Gets or sets the JoinMap\n        /// </summary>\n        public MobileControlSIMPLRunDirectRouteActionJoinMap JoinMap { get; private set; }\n\n        public Dictionary<string, DestinationListItem> DestinationList { get; set; }\n\n        public SimplDirectRouteMessenger(string key, BasicTriList eisc, string messagePath) : base(key, messagePath)\n        {\n            _eisc = eisc;\n\n            JoinMap = new MobileControlSIMPLRunDirectRouteActionJoinMap(851);\n\n            DestinationList = new Dictionary<string, DestinationListItem>();\n        }\n\n        #region Overrides of MessengerBase\n\n        protected override void RegisterActions()\n        {\n            Debug.Console(2, \"********** Direct Route Messenger CustomRegisterWithAppServer **********\");\n\n\n            //Audio source\n            _eisc.SetStringSigAction(JoinMap.SourceForDestinationAudio.JoinNumber,\n                s => PostStatusMessage(JToken.FromObject(new\n                {\n                    selectedSourceKey = s,\n                })\n                ));\n\n            AddAction(\"/programAudio/selectSource\", (id, content) =>\n            {\n                var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n                _eisc.StringInput[JoinMap.SourceForDestinationAudio.JoinNumber].StringValue = msg.Value;\n            });\n\n            AddAction(\"/fullStatus\", (id, content) =>\n            {\n                foreach (var dest in DestinationList)\n                {\n                    var key = dest.Key;\n                    var item = dest.Value;\n\n                    var source =\n                        _eisc.StringOutput[(uint)(JoinMap.SourceForDestinationJoinStart.JoinNumber + item.Order)].StringValue;\n\n                    UpdateSourceForDestination(source, key);\n                }\n\n                PostStatusMessage(JToken.FromObject(new\n                {\n                    selectedSourceKey = _eisc.StringOutput[JoinMap.SourceForDestinationAudio.JoinNumber].StringValue\n                })\n                );\n\n                PostStatusMessage(JToken.FromObject(new\n                {\n                    advancedSharingActive = _eisc.BooleanOutput[JoinMap.AdvancedSharingModeFb.JoinNumber].BoolValue\n                })\n                );\n            });\n\n            AddAction(\"/advancedSharingMode\", (id, content) =>\n            {\n                var b = content.ToObject<MobileControlSimpleContent<bool>>();\n\n                Debug.Console(1, \"Current Sharing Mode: {2}\\r\\nadvanced sharing mode: {0} join number: {1}\", b.Value,\n                    JoinMap.AdvancedSharingModeOn.JoinNumber,\n                    _eisc.BooleanOutput[JoinMap.AdvancedSharingModeOn.JoinNumber].BoolValue);\n\n                _eisc.SetBool(JoinMap.AdvancedSharingModeOn.JoinNumber, b.Value);\n                _eisc.SetBool(JoinMap.AdvancedSharingModeOff.JoinNumber, !b.Value);\n                _eisc.PulseBool(JoinMap.AdvancedSharingModeToggle.JoinNumber);\n            });\n\n            _eisc.SetBoolSigAction(JoinMap.AdvancedSharingModeFb.JoinNumber,\n                (b) => PostStatusMessage(JToken.FromObject(new\n                {\n                    advancedSharingActive = b\n                })\n                ));\n        }\n\n        /// <summary>\n        /// RegisterForDestinationPaths method\n        /// </summary>\n        public void RegisterForDestinationPaths()\n        {\n            //handle routing feedback from SIMPL\n            foreach (var destination in DestinationList)\n            {\n                var key = destination.Key;\n                var dest = destination.Value;\n\n                _eisc.SetStringSigAction((uint)(JoinMap.SourceForDestinationJoinStart.JoinNumber + dest.Order),\n                    s => UpdateSourceForDestination(s, key));\n\n                AddAction($\"/{key}/selectSource\", (id, content) =>\n                {\n                    var s = content.ToObject<MobileControlSimpleContent<string>>();\n\n                    _eisc.StringInput[(uint)(JoinMap.SourceForDestinationJoinStart.JoinNumber + dest.Order)].StringValue = s.Value;\n                });\n            }\n        }\n\n        #endregion\n\n        private void UpdateSourceForDestination(string sourceKey, string destKey)\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                selectedSourceKey = sourceKey\n            }), $\"{MessagePath}/{destKey}/currentSource\");\n        }\n    }\n\n\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLRouteMessenger.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n\n    /// <summary>\n    /// Represents a SIMPLRouteMessenger\n    /// </summary>\n    public class SIMPLRouteMessenger : MessengerBase\n    {\n        private readonly BasicTriList _eisc;\n\n        private readonly uint _joinStart;\n\n        /// <summary>\n        /// Represents a StringJoin\n        /// </summary>\n        public class StringJoin\n        {\n            /// <summary>\n            /// 1\n            /// </summary>\n            public const uint CurrentSource = 1;\n        }\n\n        public SIMPLRouteMessenger(string key, BasicTriList eisc, string messagePath, uint joinStart)\n            : base(key, messagePath)\n        {\n            _eisc = eisc;\n            _joinStart = joinStart - 1;\n\n            _eisc.SetStringSigAction(_joinStart + StringJoin.CurrentSource, SendRoutingFullMessageObject);\n        }\n\n        protected override void RegisterActions()\n        {\n            AddAction(\"/fullStatus\",\n                (id, content) => SendRoutingFullMessageObject(_eisc.GetString(_joinStart + StringJoin.CurrentSource)));\n\n            AddAction(\"/source\", (id, content) =>\n            {\n                var c = content.ToObject<SourceSelectMessageContent>();\n\n                _eisc.SetString(_joinStart + StringJoin.CurrentSource, c.SourceListItemKey);\n            });\n        }\n\n        /// <summary>\n        /// CustomUnregisterWithAppServer method\n        /// </summary>\n        public void CustomUnregisterWithAppServer(IMobileControl appServerController)\n        {\n            appServerController.RemoveAction(MessagePath + \"/fullStatus\");\n            appServerController.RemoveAction(MessagePath + \"/source\");\n\n            _eisc.SetStringSigAction(_joinStart + StringJoin.CurrentSource, null);\n        }\n\n        /// <summary>\n        /// Helper method to update full status of the routing device\n        /// </summary>\n        private void SendRoutingFullMessageObject(string sourceKey)\n        {\n            if (string.IsNullOrEmpty(sourceKey))\n                sourceKey = \"none\";\n\n            PostStatusMessage(JToken.FromObject(new\n            {\n                selectedSourceKey = sourceKey\n            })\n            );\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SIMPLVtcMessenger.cs",
    "content": "﻿using Crestron.SimplSharpPro.DeviceSupport;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing PepperDash.Essentials.Devices.Common.Codec;\nusing System;\nusing System.Collections.Generic;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    // ReSharper disable once InconsistentNaming\n    /// <summary>\n    /// Represents a SIMPLVtcMessenger\n    /// </summary>\n    public class SIMPLVtcMessenger : MessengerBase\n    {\n        private readonly BasicTriList _eisc;\n\n        /// <summary>\n        /// Gets or sets the JoinMap\n        /// </summary>\n        public SIMPLVtcJoinMap JoinMap { get; private set; }\n\n        private readonly CodecActiveCallItem _currentCallItem;\n\n        private CodecActiveCallItem _incomingCallItem;\n\n        private ushort _previousDirectoryLength = 701;\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"eisc\"></param>\n        /// <param name=\"messagePath\"></param>\n        public SIMPLVtcMessenger(string key, BasicTriList eisc, string messagePath)\n            : base(key, messagePath)\n        {\n            _eisc = eisc;\n\n            JoinMap = new SIMPLVtcJoinMap(1001);\n\n            _currentCallItem = new CodecActiveCallItem { Type = eCodecCallType.Video, Id = \"-video-\" };\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"appServerController\"></param>\n        protected override void RegisterActions()\n        {\n            _eisc.SetStringSigAction(JoinMap.HookState.JoinNumber, s =>\n            {\n                _currentCallItem.Status = (eCodecCallStatus)Enum.Parse(typeof(eCodecCallStatus), s, true);\n                PostFullStatus(); // SendCallsList();\n            });\n\n            _eisc.SetStringSigAction(JoinMap.CurrentCallNumber.JoinNumber, s =>\n            {\n                _currentCallItem.Number = s;\n                PostCallsList();\n            });\n\n            _eisc.SetStringSigAction(JoinMap.CurrentCallName.JoinNumber, s =>\n            {\n                _currentCallItem.Name = s;\n                PostCallsList();\n            });\n\n            _eisc.SetStringSigAction(JoinMap.CallDirection.JoinNumber, s =>\n            {\n                _currentCallItem.Direction = (eCodecCallDirection)Enum.Parse(typeof(eCodecCallDirection), s, true);\n                PostCallsList();\n            });\n\n            _eisc.SetBoolSigAction(JoinMap.IncomingCall.JoinNumber, b =>\n            {\n                if (b)\n                {\n                    var ica = new CodecActiveCallItem\n                    {\n                        Direction = eCodecCallDirection.Incoming,\n                        Id = \"-video-incoming\",\n                        Name = _eisc.GetString(JoinMap.IncomingCallName.JoinNumber),\n                        Number = _eisc.GetString(JoinMap.IncomingCallNumber.JoinNumber),\n                        Status = eCodecCallStatus.Ringing,\n                        Type = eCodecCallType.Video\n                    };\n                    _incomingCallItem = ica;\n                }\n                else\n                {\n                    _incomingCallItem = null;\n                }\n                PostCallsList();\n            });\n\n            _eisc.SetStringSigAction(JoinMap.IncomingCallName.JoinNumber, s =>\n                {\n                    if (_incomingCallItem != null)\n                    {\n                        _incomingCallItem.Name = s;\n                        PostCallsList();\n                    }\n                });\n\n            _eisc.SetStringSigAction(JoinMap.IncomingCallNumber.JoinNumber, s =>\n            {\n                if (_incomingCallItem != null)\n                {\n                    _incomingCallItem.Number = s;\n                    PostCallsList();\n                }\n            });\n\n            _eisc.SetBoolSigAction(JoinMap.CameraSupportsAutoMode.JoinNumber, b => PostStatusMessage(JToken.FromObject(new\n            {\n                cameraSupportsAutoMode = b\n            })));\n            _eisc.SetBoolSigAction(JoinMap.CameraSupportsOffMode.JoinNumber, b => PostStatusMessage(JToken.FromObject(new\n            {\n                cameraSupportsOffMode = b\n            })));\n\n            // Directory insanity\n            _eisc.SetUShortSigAction(JoinMap.DirectoryRowCount.JoinNumber, u =>\n            {\n                // The length of the list comes in before the list does.\n                // Splice the sig change operation onto the last string sig that will be changing\n                // when the directory entries make it through.\n                if (_previousDirectoryLength > 0)\n                {\n                    _eisc.ClearStringSigAction(JoinMap.DirectoryEntriesStart.JoinNumber + _previousDirectoryLength - 1);\n                }\n                _eisc.SetStringSigAction(JoinMap.DirectoryEntriesStart.JoinNumber + u - 1, s => PostDirectory());\n                _previousDirectoryLength = u;\n            });\n\n            _eisc.SetStringSigAction(JoinMap.DirectoryEntrySelectedName.JoinNumber, s => PostStatusMessage(JToken.FromObject(new\n            {\n                directoryContactSelected = new\n                {\n                    name = _eisc.GetString(JoinMap.DirectoryEntrySelectedName.JoinNumber),\n                }\n            })));\n\n            _eisc.SetStringSigAction(JoinMap.DirectoryEntrySelectedNumber.JoinNumber, s => PostStatusMessage(JToken.FromObject(new\n            {\n                directoryContactSelected = new\n                {\n                    number = _eisc.GetString(JoinMap.DirectoryEntrySelectedNumber.JoinNumber),\n                }\n            })));\n\n            _eisc.SetStringSigAction(JoinMap.DirectorySelectedFolderName.JoinNumber, s => PostStatusMessage(JToken.FromObject(new\n            {\n                directorySelectedFolderName = _eisc.GetString(JoinMap.DirectorySelectedFolderName.JoinNumber)\n            })));\n\n            _eisc.SetSigTrueAction(JoinMap.CameraModeAuto.JoinNumber, PostCameraMode);\n            _eisc.SetSigTrueAction(JoinMap.CameraModeManual.JoinNumber, PostCameraMode);\n            _eisc.SetSigTrueAction(JoinMap.CameraModeOff.JoinNumber, PostCameraMode);\n\n            _eisc.SetBoolSigAction(JoinMap.CameraSelfView.JoinNumber, b => PostStatusMessage(JToken.FromObject(new\n            {\n                cameraSelfView = b\n            })));\n\n            _eisc.SetUShortSigAction(JoinMap.CameraNumberSelect.JoinNumber, u => PostSelectedCamera());\n\n\n            // Add press and holds using helper action\n            void addPhAction(string s, uint u) =>\n                AddAction(s, (id, content) => HandleCameraPressAndHold(content, b => _eisc.SetBool(u, b)));\n            addPhAction(\"/cameraUp\", JoinMap.CameraTiltUp.JoinNumber);\n            addPhAction(\"/cameraDown\", JoinMap.CameraTiltDown.JoinNumber);\n            addPhAction(\"/cameraLeft\", JoinMap.CameraPanLeft.JoinNumber);\n            addPhAction(\"/cameraRight\", JoinMap.CameraPanRight.JoinNumber);\n            addPhAction(\"/cameraZoomIn\", JoinMap.CameraZoomIn.JoinNumber);\n            addPhAction(\"/cameraZoomOut\", JoinMap.CameraZoomOut.JoinNumber);\n\n            // Add straight pulse calls using helper action\n            void addAction(string s, uint u) =>\n                AddAction(s, (id, content) => _eisc.PulseBool(u, 100));\n            addAction(\"/endCallById\", JoinMap.EndCall.JoinNumber);\n            addAction(\"/endAllCalls\", JoinMap.EndCall.JoinNumber);\n            addAction(\"/acceptById\", JoinMap.IncomingAnswer.JoinNumber);\n            addAction(\"/rejectById\", JoinMap.IncomingReject.JoinNumber);\n\n            var speeddialStart = JoinMap.SpeedDialStart.JoinNumber;\n            var speeddialEnd = JoinMap.SpeedDialStart.JoinNumber + JoinMap.SpeedDialStart.JoinSpan;\n\n            var speedDialIndex = 1;\n            for (uint i = speeddialStart; i < speeddialEnd; i++)\n            {\n                addAction(string.Format(\"/speedDial{0}\", speedDialIndex), i);\n                speedDialIndex++;\n            }\n\n            addAction(\"/cameraModeAuto\", JoinMap.CameraModeAuto.JoinNumber);\n            addAction(\"/cameraModeManual\", JoinMap.CameraModeManual.JoinNumber);\n            addAction(\"/cameraModeOff\", JoinMap.CameraModeOff.JoinNumber);\n            addAction(\"/cameraSelfView\", JoinMap.CameraSelfView.JoinNumber);\n            addAction(\"/cameraLayout\", JoinMap.CameraLayout.JoinNumber);\n\n            AddAction(\"/cameraSelect\", (id, content) =>\n            {\n                var s = content.ToObject<MobileControlSimpleContent<string>>();\n                SelectCamera(s.Value);\n            });\n\n            // camera presets\n            for (uint i = 0; i < 6; i++)\n            {\n                addAction(\"/cameraPreset\" + (i + 1), JoinMap.CameraPresetStart.JoinNumber + i);\n            }\n\n            AddAction(\"/isReady\", (id, content) => PostIsReady());\n            // Get status\n            AddAction(\"/fullStatus\", (id, content) => PostFullStatus());\n            // Dial on string\n            AddAction(\"/dial\", (id, content) =>\n            {\n                var s = content.ToObject<MobileControlSimpleContent<string>>();\n\n                _eisc.SetString(JoinMap.CurrentDialString.JoinNumber, s.Value);\n            });\n            // Pulse DTMF\n            AddAction(\"/dtmf\", (id, content) =>\n            {\n                var s = content.ToObject<MobileControlSimpleContent<string>>();\n                var join = JoinMap.Joins[s.Value];\n                if (join != null)\n                {\n                    if (join.JoinNumber > 0)\n                    {\n                        _eisc.PulseBool(join.JoinNumber, 100);\n                    }\n                }\n            });\n\n            // Directory madness\n            AddAction(\"/directoryRoot\",\n                (id, content) => _eisc.PulseBool(JoinMap.DirectoryRoot.JoinNumber));\n            AddAction(\"/directoryBack\",\n                (id, content) => _eisc.PulseBool(JoinMap.DirectoryFolderBack.JoinNumber));\n            AddAction(\"/directoryById\", (id, content) =>\n            {\n                var s = content.ToObject<MobileControlSimpleContent<string>>();\n                // the id should contain the line number to forward to simpl\n                try\n                {\n                    var u = ushort.Parse(s.Value);\n                    _eisc.SetUshort(JoinMap.DirectorySelectRow.JoinNumber, u);\n                    _eisc.PulseBool(JoinMap.DirectoryLineSelected.JoinNumber);\n                }\n                catch (Exception)\n                {\n                    Debug.Console(1, this, Debug.ErrorLogLevel.Warning,\n                        \"/directoryById request contains non-numeric ID incompatible with SIMPL bridge\");\n                }\n            });\n            AddAction(\"/directorySelectContact\", (id, content) =>\n            {\n                var s = content.ToObject<MobileControlSimpleContent<string>>();\n                try\n                {\n                    var u = ushort.Parse(s.Value);\n                    _eisc.SetUshort(JoinMap.DirectorySelectRow.JoinNumber, u);\n                    _eisc.PulseBool(JoinMap.DirectoryLineSelected.JoinNumber);\n                }\n                catch\n                {\n                    Debug.Console(2, this, \"Error parsing contact from {0} for path /directorySelectContact\", s);\n                }\n            });\n            AddAction(\"/directoryDialContact\",\n                (id, content) => _eisc.PulseBool(JoinMap.DirectoryDialSelectedLine.JoinNumber));\n            AddAction(\"/getDirectory\", (id, content) =>\n            {\n                if (_eisc.GetUshort(JoinMap.DirectoryRowCount.JoinNumber) > 0)\n                {\n                    PostDirectory();\n                }\n                else\n                {\n                    _eisc.PulseBool(JoinMap.DirectoryRoot.JoinNumber);\n                }\n            });\n        }\n\n        private void HandleCameraPressAndHold(JToken content, Action<bool> cameraAction)\n        {\n            var state = content.ToObject<MobileControlSimpleContent<string>>();\n\n            var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);\n            if (timerHandler == null)\n            {\n                return;\n            }\n\n            timerHandler(state.Value, cameraAction);\n\n            cameraAction(state.Value.Equals(\"true\", StringComparison.InvariantCultureIgnoreCase));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// \n        private void PostFullStatus()\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                calls = GetCurrentCallList(),\n                cameraMode = GetCameraMode(),\n                cameraSelfView = _eisc.GetBool(JoinMap.CameraSelfView.JoinNumber),\n                cameraSupportsAutoMode = _eisc.GetBool(JoinMap.CameraSupportsAutoMode.JoinNumber),\n                cameraSupportsOffMode = _eisc.GetBool(JoinMap.CameraSupportsOffMode.JoinNumber),\n                currentCallString = _eisc.GetString(JoinMap.CurrentCallNumber.JoinNumber),\n                currentDialString = _eisc.GetString(JoinMap.CurrentDialString.JoinNumber),\n                directoryContactSelected = new\n                {\n                    name = _eisc.GetString(JoinMap.DirectoryEntrySelectedName.JoinNumber),\n                    number = _eisc.GetString(JoinMap.DirectoryEntrySelectedNumber.JoinNumber)\n                },\n                directorySelectedFolderName = _eisc.GetString(JoinMap.DirectorySelectedFolderName.JoinNumber),\n                isInCall = _eisc.GetString(JoinMap.HookState.JoinNumber) == \"Connected\",\n                hasDirectory = true,\n                hasDirectorySearch = false,\n                hasRecents = !_eisc.BooleanOutput[502].BoolValue,\n                hasCameras = true,\n                showCamerasWhenNotInCall = _eisc.BooleanOutput[503].BoolValue,\n                selectedCamera = GetSelectedCamera(),\n            }));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void PostDirectory()\n        {\n            var u = _eisc.GetUshort(JoinMap.DirectoryRowCount.JoinNumber);\n            var items = new List<object>();\n            for (uint i = 0; i < u; i++)\n            {\n                var name = _eisc.GetString(JoinMap.DirectoryEntriesStart.JoinNumber + i);\n                var id = (i + 1).ToString();\n                // is folder or contact?\n                if (name.StartsWith(\"[+]\"))\n                {\n                    items.Add(new\n                    {\n                        folderId = id,\n                        name\n                    });\n                }\n                else\n                {\n                    items.Add(new\n                    {\n                        contactId = id,\n                        name\n                    });\n                }\n            }\n\n            var directoryMessage = new\n            {\n                currentDirectory = new\n                {\n                    isRootDirectory = _eisc.GetBool(JoinMap.DirectoryIsRoot.JoinNumber),\n                    directoryResults = items\n                }\n            };\n            PostStatusMessage(JToken.FromObject(directoryMessage));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void PostCameraMode()\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                cameraMode = GetCameraMode()\n            }));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private string GetCameraMode()\n        {\n            string m;\n            if (_eisc.GetBool(JoinMap.CameraModeAuto.JoinNumber)) m = eCameraControlMode.Auto.ToString().ToLower();\n            else if (_eisc.GetBool(JoinMap.CameraModeManual.JoinNumber))\n                m = eCameraControlMode.Manual.ToString().ToLower();\n            else m = eCameraControlMode.Off.ToString().ToLower();\n            return m;\n        }\n\n        private void PostSelectedCamera()\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                selectedCamera = GetSelectedCamera()\n            }));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private string GetSelectedCamera()\n        {\n            var num = _eisc.GetUshort(JoinMap.CameraNumberSelect.JoinNumber);\n            string m;\n            if (num == 100)\n            {\n                m = \"cameraFar\";\n            }\n            else\n            {\n                m = \"camera\" + num;\n            }\n            return m;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void PostIsReady()\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                isReady = true\n            }));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void PostCallsList()\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                calls = GetCurrentCallList(),\n            }));\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"s\"></param>\n        private void SelectCamera(string s)\n        {\n            var cam = s.Substring(6);\n            _eisc.SetUshort(JoinMap.CameraNumberSelect.JoinNumber,\n                (ushort)(cam.ToLower() == \"far\" ? 100 : ushort.Parse(cam)));\n        }\n\n        /// <summary>\n        /// Turns the \n        /// </summary>\n        /// <returns></returns>\n        private List<CodecActiveCallItem> GetCurrentCallList()\n        {\n            var list = new List<CodecActiveCallItem>();\n            if (_currentCallItem.Status != eCodecCallStatus.Disconnected)\n            {\n                list.Add(_currentCallItem);\n            }\n            if (_eisc.GetBool(JoinMap.IncomingCall.JoinNumber))\n            {\n                list.Add(_incomingCallItem);\n            }\n            return list;\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ShadeBaseMessenger.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Shades;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a IShadesOpenCloseStopMessenger\n    /// </summary>\n    public class IShadesOpenCloseStopMessenger : MessengerBase\n    {\n        private readonly IShadesOpenCloseStop device;\n\n        public IShadesOpenCloseStopMessenger(string key, IShadesOpenCloseStop shades, string messagePath)\n            : base(key, messagePath, shades as IKeyName)\n        {\n            device = shades;\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n            AddAction(\"/shadesStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/shadeUp\", (id, content) =>\n                {\n\n                    device.Open();\n\n                });\n\n            AddAction(\"/shadeDown\", (id, content) =>\n                {\n\n                    device.Close();\n\n                });\n\n            var stopDevice = device;\n            if (stopDevice != null)\n            {\n                AddAction(\"/stopOrPreset\", (id, content) =>\n                {\n                    stopDevice.Stop();\n                });\n            }\n\n            if (device is IShadesOpenClosedFeedback feedbackDevice)\n            {\n                feedbackDevice.ShadeIsOpenFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>(ShadeIsOpenFeedback_OutputChange);\n                feedbackDevice.ShadeIsClosedFeedback.OutputChange += new EventHandler<Core.FeedbackEventArgs>(ShadeIsClosedFeedback_OutputChange);\n            }\n        }\n\n        private void ShadeIsOpenFeedback_OutputChange(object sender, Core.FeedbackEventArgs e)\n        {\n            var state = new ShadeBaseStateMessage\n            {\n                IsOpen = e.BoolValue\n            };\n\n            PostStatusMessage(state);\n        }\n\n        private void ShadeIsClosedFeedback_OutputChange(object sender, Core.FeedbackEventArgs e)\n        {\n            var state = new ShadeBaseStateMessage\n            {\n                IsClosed = e.BoolValue\n            };\n\n            PostStatusMessage(state);\n        }\n\n\n        private void SendFullStatus(string id = null)\n        {\n            var state = new ShadeBaseStateMessage();\n\n            if (device is IShadesOpenClosedFeedback feedbackDevice)\n            {\n                state.IsOpen = feedbackDevice.ShadeIsOpenFeedback.BoolValue;\n                state.IsClosed = feedbackDevice.ShadeIsClosedFeedback.BoolValue;\n            }\n\n            PostStatusMessage(state, id);\n        }\n    }\n\n    /// <summary>\n    /// Represents a ShadeBaseStateMessage\n    /// </summary>\n    public class ShadeBaseStateMessage : DeviceStateMessageBase\n    {\n\n        /// <summary>\n        /// Gets or sets the MiddleButtonLabel\n        /// </summary>\n        [JsonProperty(\"middleButtonLabel\", NullValueHandling = NullValueHandling.Ignore)]\n        public string MiddleButtonLabel { get; set; }\n\n        [JsonProperty(\"isOpen\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsOpen { get; set; }\n\n        [JsonProperty(\"isClosed\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsClosed { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SimplMessengerPropertiesConfig.cs",
    "content": "﻿using PepperDash.Essentials.Core.Bridges;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a SimplMessengerPropertiesConfig\n    /// </summary>\n    public class SimplMessengerPropertiesConfig : EiscApiPropertiesConfig.ApiDevicePropertiesConfig\n    {\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/SystemMonitorMessenger.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Essentials.Core.Monitoring;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a SystemMonitorMessenger\n    /// </summary>\n    public class SystemMonitorMessenger : MessengerBase\n    {\n        private readonly SystemMonitorController systemMonitor;\n\n        public SystemMonitorMessenger(string key, SystemMonitorController sysMon, string messagePath)\n            : base(key, messagePath, sysMon)\n        {\n            systemMonitor = sysMon ?? throw new ArgumentNullException(\"sysMon\");\n\n            systemMonitor.SystemMonitorPropertiesChanged += SysMon_SystemMonitorPropertiesChanged;\n\n            foreach (var p in systemMonitor.ProgramStatusFeedbackCollection)\n            {\n                p.Value.ProgramInfoChanged += ProgramInfoChanged;\n            }\n\n            CrestronConsole.AddNewConsoleCommand(s => SendFullStatusMessage(), \"SendFullSysMonStatus\",\n                \"Sends the full System Monitor Status\", ConsoleAccessLevelEnum.AccessOperator);\n        }\n\n        /// <summary>\n        /// Posts the program information message\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void ProgramInfoChanged(object sender, ProgramInfoEventArgs e)\n        {\n            if (e.ProgramInfo != null)\n            {\n                //Debug.Console(1, \"Posting Status Message: {0}\", e.ProgramInfo.ToString());\n                PostStatusMessage(JToken.FromObject(e.ProgramInfo)\n                );\n            }\n        }\n\n        /// <summary>\n        /// Posts the system monitor properties\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void SysMon_SystemMonitorPropertiesChanged(object sender, EventArgs e)\n        {\n            SendSystemMonitorStatusMessage();\n        }\n\n        private void SendFullStatusMessage(string id = null)\n        {\n            SendSystemMonitorStatusMessage(id);\n\n            foreach (var p in systemMonitor.ProgramStatusFeedbackCollection)\n            {\n                PostStatusMessage(JToken.FromObject(p.Value.ProgramInfo), id);\n            }\n        }\n\n        private void SendSystemMonitorStatusMessage(string id = null)\n        {\n            // This takes a while, launch a new thread\n\n            Task.Run(() => PostStatusMessage(JToken.FromObject(new SystemMonitorStateMessage\n            {\n                TimeZone = systemMonitor.TimeZoneFeedback.IntValue,\n                TimeZoneName = systemMonitor.TimeZoneTextFeedback.StringValue,\n                IoControllerVersion = systemMonitor.IoControllerVersionFeedback.StringValue,\n                SnmpVersion = systemMonitor.SnmpVersionFeedback.StringValue,\n                BacnetVersion = systemMonitor.BaCnetAppVersionFeedback.StringValue,\n                ControllerVersion = systemMonitor.ControllerVersionFeedback.StringValue\n            }), id\n            ));\n        }\n\n        protected override void RegisterActions()\n        {\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatusMessage(id));\n\n            AddAction(\"/systemStatus\", (id, content) => SendFullStatusMessage(id));\n        }\n    }\n\n    /// <summary>\n    /// Represents a SystemMonitorStateMessage\n    /// </summary>\n    public class SystemMonitorStateMessage\n    {\n        /// <summary>\n        /// Gets or sets the TimeZone\n        /// </summary>\n        [JsonProperty(\"timeZone\", NullValueHandling = NullValueHandling.Ignore)]\n        public int TimeZone { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the TimeZoneName\n        /// </summary>\n        [JsonProperty(\"timeZoneName\", NullValueHandling = NullValueHandling.Ignore)]\n        public string TimeZoneName { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the IoControllerVersion\n        /// </summary>\n        [JsonProperty(\"ioControllerVersion\", NullValueHandling = NullValueHandling.Ignore)]\n        public string IoControllerVersion { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the SnmpVersion\n        /// </summary>\n        [JsonProperty(\"snmpVersion\", NullValueHandling = NullValueHandling.Ignore)]\n        public string SnmpVersion { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the BacnetVersion\n        /// </summary>\n        [JsonProperty(\"bacnetVersion\", NullValueHandling = NullValueHandling.Ignore)]\n        public string BacnetVersion { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the ControllerVersion\n        /// </summary>\n        [JsonProperty(\"controllerVersion\", NullValueHandling = NullValueHandling.Ignore)]\n        public string ControllerVersion { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/TwoWayDisplayBaseMessenger.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Devices.Common.Displays;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a TwoWayDisplayBaseMessenger\n    /// </summary>\n    public class TwoWayDisplayBaseMessenger : MessengerBase\n    {\n        private readonly TwoWayDisplayBase _display;\n\n        public TwoWayDisplayBaseMessenger(string key, string messagePath, TwoWayDisplayBase display)\n            : base(key, messagePath, display)\n        {\n            _display = display;\n        }\n\n        #region Overrides of MessengerBase\n\n        /// <summary>\n        /// SendFullStatus method\n        /// </summary>\n        public void SendFullStatus(string id = null)\n        {\n            var messageObj = new TwoWayDisplayBaseStateMessage\n            {\n                //PowerState = _display.PowerIsOnFeedback.BoolValue,\n                CurrentInput = _display.CurrentInputFeedback.StringValue\n            };\n\n            PostStatusMessage(messageObj, id);\n        }\n\n        protected override void RegisterActions()\n        {\n            base.RegisterActions();\n\n            AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n\n            AddAction(\"/displayStatus\", (id, content) => SendFullStatus(id));\n\n            _display.CurrentInputFeedback.OutputChange += CurrentInputFeedbackOnOutputChange;\n            _display.IsCoolingDownFeedback.OutputChange += IsCoolingFeedbackOnOutputChange;\n            _display.IsWarmingUpFeedback.OutputChange += IsWarmingFeedbackOnOutputChange;\n        }\n\n        private void CurrentInputFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                currentInput = feedbackEventArgs.StringValue\n            })\n            );\n        }\n\n        private void IsWarmingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                isWarming = feedbackEventArgs.BoolValue\n            })\n            );\n        }\n\n        private void IsCoolingFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs)\n        {\n            PostStatusMessage(JToken.FromObject(new\n            {\n                isCooling = feedbackEventArgs.BoolValue\n            })\n            );\n\n\n        }\n\n        #endregion\n    }\n\n    /// <summary>\n    /// Represents a TwoWayDisplayBaseStateMessage\n    /// </summary>\n    public class TwoWayDisplayBaseStateMessage : DeviceStateMessageBase\n    {\n        //[JsonProperty(\"powerState\", NullValueHandling = NullValueHandling.Ignore)]\n        //public bool? PowerState { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the CurrentInput\n        /// </summary>\n        [JsonProperty(\"currentInput\", NullValueHandling = NullValueHandling.Ignore)]\n        public string CurrentInput { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/Messengers/VideoCodecBaseMessenger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Crestron.SimplSharp;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Core;\nusing PepperDash.Core.Logging;\nusing PepperDash.Essentials.Core;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\nusing PepperDash.Essentials.Devices.Common.Cameras;\nusing PepperDash.Essentials.Devices.Common.Codec;\nusing PepperDash.Essentials.Devices.Common.VideoCodec;\nusing PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Provides a messaging bridge for a VideoCodecBase device\n    /// </summary>\n    public class VideoCodecBaseMessenger : MessengerBase\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        protected VideoCodecBase Codec { get; private set; }\n\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"key\"></param>\n        /// <param name=\"codec\"></param>\n        /// <param name=\"messagePath\"></param>\n        public VideoCodecBaseMessenger(string key, VideoCodecBase codec, string messagePath)\n            : base(key, messagePath, codec)\n        {\n            Codec = codec ?? throw new ArgumentNullException(\"codec\");\n            codec.CallStatusChange += Codec_CallStatusChange;\n            codec.IsReadyChange += Codec_IsReadyChange;\n\n            if (codec is IHasDirectory dirCodec)\n            {\n                dirCodec.DirectoryResultReturned += DirCodec_DirectoryResultReturned;\n            }\n\n            if (codec is IHasCallHistory recCodec)\n            {\n                recCodec.CallHistory.RecentCallsListHasChanged += CallHistory_RecentCallsListHasChanged;\n            }\n\n            if (codec is IPasswordPrompt pwPromptCodec)\n            {\n                pwPromptCodec.PasswordRequired += OnPasswordRequired;\n            }\n        }\n\n        private void OnPasswordRequired(object sender, PasswordPromptEventArgs args)\n        {\n            var eventMsg = new PasswordPromptEventMessage\n            {\n                Message = args.Message,\n                LastAttemptWasIncorrect = args.LastAttemptWasIncorrect,\n                LoginAttemptFailed = args.LoginAttemptFailed,\n                LoginAttemptCancelled = args.LoginAttemptCancelled,\n                EventType = \"passwordPrompt\"\n            };\n\n            PostEventMessage(eventMsg);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void CallHistory_RecentCallsListHasChanged(object sender, EventArgs e)\n        {\n            try\n            {\n                var state = new VideoCodecBaseStateMessage();\n\n                if (!(sender is CodecCallHistory codecCallHistory)) return;\n                var recents = codecCallHistory.RecentCalls;\n\n                if (recents != null)\n                {\n                    state.RecentCalls = recents;\n\n                    PostStatusMessage(state);\n                }\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error posting call history\");\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        protected virtual void DirCodec_DirectoryResultReturned(object sender, DirectoryEventArgs e)\n        {\n            if (Codec is IHasDirectory)\n                SendDirectory(e.Directory);\n        }\n\n        /// <summary>\n        /// Posts the current directory\n        /// </summary>\n        protected void SendDirectory(CodecDirectory directory)\n        {\n            try\n            {\n                var state = new VideoCodecBaseStateMessage();\n\n\n                if (Codec is IHasDirectory dirCodec)\n                {\n                    this.LogVerbose(\"Sending Directory.  Directory Item Count: {directoryItemCount}\", directory.CurrentDirectoryResults.Count);\n\n                    //state.CurrentDirectory = PrefixDirectoryFolderItems(directory);\n                    state.CurrentDirectory = directory;\n\n                    CrestronInvoke.BeginInvoke((o) => PostStatusMessage(state));\n                }\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error sending directory\");\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void Codec_IsReadyChange(object sender, EventArgs e)\n        {\n            try\n            {\n                var state = new VideoCodecBaseStateMessage\n                {\n                    IsReady = true\n                };\n\n                PostStatusMessage(state);\n\n                SendFullStatus();\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error sending codec ready status\");\n            }\n        }\n\n        /// <summary>\n        /// Called from base's RegisterWithAppServer method\n        /// </summary>\n        /// <param name=\"appServerController\"></param>\n        protected override void RegisterActions()\n        {\n            try\n            {\n                base.RegisterActions();\n\n                AddAction(\"/isReady\", (id, content) => SendIsReady());\n\n                AddAction(\"/fullStatus\", (id, content) => SendFullStatus(id));\n                AddAction(\"/codecStatus\", (id, content) => SendFullStatus(id));\n\n                AddAction(\"/dial\", (id, content) =>\n                {\n                    var value = content.ToObject<MobileControlSimpleContent<string>>();\n\n                    Codec.Dial(value.Value);\n                });\n\n                AddAction(\"/dialMeeting\", (id, content) => Codec.Dial(content.ToObject<Meeting>()));\n\n                AddAction(\"/endCallById\", (id, content) =>\n                {\n                    var s = content.ToObject<MobileControlSimpleContent<string>>();\n                    var call = GetCallWithId(s.Value);\n                    if (call != null)\n                        Codec.EndCall(call);\n                });\n\n                AddAction(\"/endAllCalls\", (id, content) => Codec.EndAllCalls());\n\n                AddAction(\"/dtmf\", (id, content) =>\n                {\n                    var s = content.ToObject<MobileControlSimpleContent<string>>();\n                    Codec.SendDtmf(s.Value);\n                });\n\n                AddAction(\"/rejectById\", (id, content) =>\n                {\n                    var s = content.ToObject<MobileControlSimpleContent<string>>();\n\n                    var call = GetCallWithId(s.Value);\n                    if (call != null)\n                        Codec.RejectCall(call);\n                });\n\n                AddAction(\"/acceptById\", (id, content) =>\n                {\n                    var s = content.ToObject<MobileControlSimpleContent<string>>();\n\n                    var call = GetCallWithId(s.Value);\n                    if (call != null)\n                        Codec.AcceptCall(call);\n                });\n\n                Codec.SharingContentIsOnFeedback.OutputChange += SharingContentIsOnFeedback_OutputChange;\n                Codec.SharingSourceFeedback.OutputChange += SharingSourceFeedback_OutputChange;\n\n                // Directory actions\n                if (Codec is IHasDirectory dirCodec)\n                {\n                    AddAction(\"/getDirectory\", (id, content) => GetDirectoryRoot());\n\n                    AddAction(\"/directoryById\", (id, content) =>\n                    {\n                        var msg = content.ToObject<MobileControlSimpleContent<string>>();\n                        GetDirectory(msg.Value);\n                    });\n\n                    AddAction(\"/directorySearch\", (id, content) =>\n                    {\n                        var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n                        GetDirectory(msg.Value);\n                    });\n\n                    AddAction(\"/directoryBack\", (id, content) => GetPreviousDirectory());\n\n                    dirCodec.PhonebookSyncState.InitialSyncCompleted += PhonebookSyncState_InitialSyncCompleted;\n                }\n\n                // History actions\n                if (Codec is IHasCallHistory recCodec)\n                {\n                    AddAction(\"/getCallHistory\", (id, content) => PostCallHistory());\n                }\n                if (Codec is IHasCodecCameras cameraCodec)\n                {\n                    this.LogVerbose(\"Adding IHasCodecCameras Actions\");\n\n                    cameraCodec.CameraSelected += CameraCodec_CameraSelected;\n\n                    AddAction(\"/cameraSelect\", (id, content) =>\n                    {\n                        var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n                        cameraCodec.SelectCamera(msg.Value);\n                    });\n\n\n                    MapCameraActions();\n\n                    if (Codec is IHasCodecRoomPresets presetsCodec)\n                    {\n                        this.LogVerbose(\"Adding IHasCodecRoomPresets Actions\");\n\n                        presetsCodec.CodecRoomPresetsListHasChanged += PresetsCodec_CameraPresetsListHasChanged;\n\n                        AddAction(\"/cameraPreset\", (id, content) =>\n                        {\n                            var msg = content.ToObject<MobileControlSimpleContent<int>>();\n\n                            presetsCodec.CodecRoomPresetSelect(msg.Value);\n                        });\n\n                        AddAction(\"/cameraPresetStore\", (id, content) =>\n                        {\n                            var msg = content.ToObject<CodecRoomPreset>();\n\n                            presetsCodec.CodecRoomPresetStore(msg.ID, msg.Description);\n                        });\n                    }\n\n                    if (Codec is IHasCameraAutoMode speakerTrackCodec)\n                    {\n                        this.LogVerbose(\"Adding IHasCameraAutoMode Actions\");\n\n                        speakerTrackCodec.CameraAutoModeIsOnFeedback.OutputChange += CameraAutoModeIsOnFeedback_OutputChange;\n\n                        AddAction(\"/cameraModeAuto\", (id, content) => speakerTrackCodec.CameraAutoModeOn());\n\n                        AddAction(\"/cameraModeManual\", (id, content) => speakerTrackCodec.CameraAutoModeOff());\n                    }\n\n                    if (Codec is IHasCameraOff cameraOffCodec)\n                    {\n                        this.LogVerbose(\"Adding IHasCameraOff Actions\");\n\n                        cameraOffCodec.CameraIsOffFeedback.OutputChange += (CameraIsOffFeedback_OutputChange);\n\n                        AddAction(\"/cameraModeOff\", (id, content) => cameraOffCodec.CameraOff());\n                    }\n                }\n\n\n\n                if (Codec is IHasCodecSelfView selfViewCodec)\n                {\n                    this.LogVerbose(\"Adding IHasCodecSelfView Actions\");\n\n                    AddAction(\"/cameraSelfView\", (id, content) => selfViewCodec.SelfViewModeToggle());\n\n                    selfViewCodec.SelfviewIsOnFeedback.OutputChange += new EventHandler<FeedbackEventArgs>(SelfviewIsOnFeedback_OutputChange);\n                }\n\n\n                if (Codec is IHasCodecLayouts layoutsCodec)\n                {\n                    this.LogVerbose(\"Adding IHasCodecLayouts Actions\");\n\n                    AddAction(\"/cameraRemoteView\", (id, content) => layoutsCodec.LocalLayoutToggle());\n\n                    AddAction(\"/cameraLayout\", (id, content) => layoutsCodec.LocalLayoutToggle());\n                }\n\n                if (Codec is IPasswordPrompt pwCodec)\n                {\n                    this.LogVerbose(\"Adding IPasswordPrompt Actions\");\n\n                    AddAction(\"/password\", (id, content) =>\n                    {\n                        var msg = content.ToObject<MobileControlSimpleContent<string>>();\n\n                        pwCodec.SubmitPassword(msg.Value);\n                    });\n                }\n\n\n                if (Codec is IHasFarEndContentStatus farEndContentStatus)\n                {\n                    farEndContentStatus.ReceivingContent.OutputChange +=\n                        (sender, args) => PostReceivingContent(args.BoolValue);\n                }\n\n                this.LogVerbose(\"Adding Privacy & Standby Actions\");\n\n                AddAction(\"/privacyModeOn\", (id, content) => Codec.PrivacyModeOn());\n                AddAction(\"/privacyModeOff\", (id, content) => Codec.PrivacyModeOff());\n                AddAction(\"/privacyModeToggle\", (id, content) => Codec.PrivacyModeToggle());\n                AddAction(\"/sharingStart\", (id, content) => Codec.StartSharing());\n                AddAction(\"/sharingStop\", (id, content) => Codec.StopSharing());\n                AddAction(\"/standbyOn\", (id, content) => Codec.StandbyActivate());\n                AddAction(\"/standbyOff\", (id, content) => Codec.StandbyDeactivate());\n            }\n            catch (Exception e)\n            {\n                this.LogException(e, \"Exception adding paths\");\n            }\n        }\n\n        private void SharingSourceFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            try\n            {\n                var state = new VideoCodecBaseStateMessage\n                {\n                    SharingSource = e.StringValue\n                };\n\n                PostStatusMessage(state);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error posting sharing source\");\n            }\n        }\n\n        private void SharingContentIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            try\n            {\n                var state = new VideoCodecBaseStateMessage\n                {\n                    SharingContentIsOn = e.BoolValue\n                };\n\n                PostStatusMessage(state);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error posting sharing content\");\n            }\n        }\n\n        private void PhonebookSyncState_InitialSyncCompleted(object sender, EventArgs e)\n        {\n            try\n            {\n                var state = new VideoCodecBaseStateMessage\n                {\n                    InitialPhonebookSyncComplete = true\n                };\n\n                PostStatusMessage(state);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error posting phonebook sync state\");\n            }\n        }\n\n        private void CameraIsOffFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            PostCameraMode();\n        }\n\n        private void SelfviewIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            PostCameraSelfView();\n        }\n\n        private void PresetsCodec_CameraPresetsListHasChanged(object sender, EventArgs e)\n        {\n            PostCameraPresets();\n        }\n\n        private void CameraAutoModeIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e)\n        {\n            PostCameraMode();\n        }\n\n\n        private void CameraCodec_CameraSelected(object sender, CameraSelectedEventArgs e)\n        {\n            try\n            {\n                MapCameraActions();\n                PostSelectedCamera();\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Exception handling camera selected event\");\n            }\n        }\n\n        /// <summary>\n        /// Maps the camera control actions to the current selected camera on the codec\n        /// </summary>\n        private void MapCameraActions()\n        {\n            if (Codec is IHasCameras cameraCodec && cameraCodec.SelectedCamera != null)\n            {\n                RemoveAction(\"/cameraUp\");\n                RemoveAction(\"/cameraDown\");\n                RemoveAction(\"/cameraLeft\");\n                RemoveAction(\"/cameraRight\");\n                RemoveAction(\"/cameraZoomIn\");\n                RemoveAction(\"/cameraZoomOut\");\n                RemoveAction(\"/cameraHome\");\n\n                if (cameraCodec.SelectedCamera is IHasCameraPtzControl camera)\n                {\n                    AddAction(\"/cameraUp\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                    {\n                        if (b)\n                        {\n                            camera.TiltUp();\n                            return;\n                        }\n\n                        camera.TiltStop();\n                    }));\n\n                    AddAction(\"/cameraDown\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                    {\n                        if (b)\n                        {\n                            camera.TiltDown();\n                            return;\n                        }\n\n                        camera.TiltStop();\n                    }));\n\n                    AddAction(\"/cameraLeft\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                    {\n                        if (b)\n                        {\n                            camera.PanLeft();\n                            return;\n                        }\n\n                        camera.PanStop();\n                    }));\n\n                    AddAction(\"/cameraRight\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                    {\n                        if (b)\n                        {\n                            camera.PanRight();\n                            return;\n                        }\n\n                        camera.PanStop();\n                    }));\n\n                    AddAction(\"/cameraZoomIn\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                    {\n                        if (b)\n                        {\n                            camera.ZoomIn();\n                            return;\n                        }\n\n                        camera.ZoomStop();\n                    }));\n\n                    AddAction(\"/cameraZoomOut\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                    {\n                        if (b)\n                        {\n                            camera.ZoomOut();\n                            return;\n                        }\n\n                        camera.ZoomStop();\n                    }));\n                    AddAction(\"/cameraHome\", (id, content) => camera.PositionHome());\n\n\n                    RemoveAction(\"/cameraAutoFocus\");\n                    RemoveAction(\"/cameraFocusNear\");\n                    RemoveAction(\"/cameraFocusFar\");\n\n                    if (cameraCodec is IHasCameraFocusControl focusCamera)\n                    {\n                        AddAction(\"/cameraAutoFocus\", (id, content) => focusCamera.TriggerAutoFocus());\n\n                        AddAction(\"/cameraFocusNear\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                        {\n                            if (b)\n                            {\n                                focusCamera.FocusNear();\n                                return;\n                            }\n\n                            focusCamera.FocusStop();\n                        }));\n\n                        AddAction(\"/cameraFocusFar\", (id, content) => HandleCameraPressAndHold(content, (b) =>\n                        {\n                            if (b)\n                            {\n                                focusCamera.FocusFar();\n                                return;\n                            }\n\n                            focusCamera.FocusStop();\n                        }));\n                    }\n                }\n            }\n        }\n\n        private void HandleCameraPressAndHold(JToken content, Action<bool> cameraAction)\n        {\n            var state = content.ToObject<MobileControlSimpleContent<string>>();\n\n            var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);\n            if (timerHandler == null)\n            {\n                return;\n            }\n\n            timerHandler(state.Value, cameraAction);\n\n            cameraAction(state.Value.Equals(\"true\", StringComparison.InvariantCultureIgnoreCase));\n        }\n\n        private string GetCameraMode()\n        {\n            string m = \"\";\n\n            if (Codec is IHasCameraAutoMode speakerTrackCodec)\n            {\n                m = speakerTrackCodec.CameraAutoModeIsOnFeedback.BoolValue\n                    ? eCameraControlMode.Auto.ToString().ToLower()\n                    : eCameraControlMode.Manual.ToString().ToLower();\n            }\n\n            if (Codec is IHasCameraOff cameraOffCodec)\n            {\n                if (cameraOffCodec.CameraIsOffFeedback.BoolValue)\n                    m = eCameraControlMode.Off.ToString().ToLower();\n            }\n\n            return m;\n        }\n\n        private void PostCallHistory()\n        {\n            try\n            {\n                var codec = (Codec as IHasCallHistory);\n\n                if (codec != null)\n                {\n                    var status = new VideoCodecBaseStateMessage();\n\n                    var recents = codec.CallHistory.RecentCalls;\n\n                    if (recents != null)\n                    {\n                        status.RecentCalls = codec.CallHistory.RecentCalls;\n\n                        PostStatusMessage(status);\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error posting call history\");\n            }\n        }\n\n        /// <summary>\n        /// Helper to grab a call with string ID\n        /// </summary>\n        /// <param name=\"id\"></param>\n        /// <returns></returns>\n        private CodecActiveCallItem GetCallWithId(string id)\n        {\n            return Codec.ActiveCalls.FirstOrDefault(c => c.Id == id);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"id\"></param>\n        private void GetDirectory(string id)\n        {\n            if (!(Codec is IHasDirectory dirCodec))\n            {\n                return;\n            }\n            dirCodec.GetDirectoryFolderContents(id);\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void GetDirectoryRoot()\n        {\n            try\n            {\n                if (!(Codec is IHasDirectory dirCodec))\n                {\n                    // do something else?\n                    return;\n                }\n                if (!dirCodec.PhonebookSyncState.InitialSyncComplete)\n                {\n                    var state = new VideoCodecBaseStateMessage\n                    {\n                        InitialPhonebookSyncComplete = false\n                    };\n\n                    PostStatusMessage(state);\n                    return;\n                }\n\n                dirCodec.SetCurrentDirectoryToRoot();\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error getting directory root\");\n            }\n        }\n\n        /// <summary>\n        /// Requests the parent folder contents\n        /// </summary>\n        private void GetPreviousDirectory()\n        {\n            if (!(Codec is IHasDirectory dirCodec))\n            {\n                return;\n            }\n\n            dirCodec.GetDirectoryParentFolderContents();\n        }\n\n        /// <summary>\n        /// Handler for codec changes\n        /// </summary>\n        private void Codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e)\n        {\n            SendFullStatus();\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void SendIsReady()\n        {\n            try\n            {\n                var status = new VideoCodecBaseStateMessage();\n\n                var codecType = Codec.GetType();\n\n                status.IsReady = Codec.IsReady;\n                status.IsZoomRoom = codecType.GetInterface(\"IHasZoomRoomLayouts\") != null;\n\n                PostStatusMessage(status);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error sending codec ready status\");\n            }\n        }\n\n        /// <summary>\n        /// Helper method to build call status for vtc\n        /// </summary>\n        /// <returns></returns>\n        protected VideoCodecBaseStateMessage GetStatus()\n        {\n            try\n            {\n                var status = new VideoCodecBaseStateMessage();\n\n                if (Codec is IHasCodecCameras camerasCodec)\n                {\n                    status.Cameras = new CameraStatus\n                    {\n                        CameraManualIsSupported = true,\n                        CameraAutoIsSupported = Codec.SupportsCameraAutoMode,\n                        CameraOffIsSupported = Codec.SupportsCameraOff,\n                        CameraMode = GetCameraMode(),\n                        Cameras = camerasCodec.Cameras,\n                        SelectedCamera = GetSelectedCamera(camerasCodec)\n                    };\n                }\n\n                if (Codec is IHasDirectory directoryCodec)\n                {\n                    status.HasDirectory = true;\n                    status.HasDirectorySearch = true;\n                    status.CurrentDirectory = directoryCodec.CurrentDirectoryResult;\n                }\n\n                var codecType = Codec.GetType();\n\n                status.CameraSelfViewIsOn = Codec is IHasCodecSelfView && (Codec as IHasCodecSelfView).SelfviewIsOnFeedback.BoolValue;\n                status.IsInCall = Codec.IsInCall;\n                status.PrivacyModeIsOn = Codec.PrivacyModeIsOnFeedback.BoolValue;\n                status.SharingContentIsOn = Codec.SharingContentIsOnFeedback.BoolValue;\n                status.SharingSource = Codec.SharingSourceFeedback.StringValue;\n                status.StandbyIsOn = Codec.StandbyIsOnFeedback.BoolValue;\n                status.Calls = Codec.ActiveCalls;\n                status.Info = Codec.CodecInfo;\n                status.ShowSelfViewByDefault = Codec.ShowSelfViewByDefault;\n                status.SupportsAdHocMeeting = Codec is IHasStartMeeting;\n                status.HasRecents = Codec is IHasCallHistory;\n                status.HasCameras = Codec is IHasCameras;\n                status.Presets = GetCurrentPresets();\n                status.IsZoomRoom = codecType.GetInterface(\"IHasZoomRoomLayouts\") != null;\n                status.ReceivingContent = Codec is IHasFarEndContentStatus && (Codec as IHasFarEndContentStatus).ReceivingContent.BoolValue;\n\n                if (Codec is IHasMeetingInfo meetingInfoCodec)\n                {\n                    status.MeetingInfo = meetingInfoCodec.MeetingInfo;\n                }\n\n                return status;\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error getting codec status\");\n                return null;\n            }\n        }\n\n        protected virtual void SendFullStatus(string id = null)\n        {\n            if (!Codec.IsReady)\n            {\n                return;\n            }\n\n            Task.Run(() => PostStatusMessage(GetStatus(), id));\n        }\n\n        private void PostReceivingContent(bool receivingContent)\n        {\n            try\n            {\n                var state = new VideoCodecBaseStateMessage\n                {\n                    ReceivingContent = receivingContent\n                };\n\n                PostStatusMessage(state);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error posting receiving content\");\n            }\n        }\n\n        private void PostCameraSelfView()\n        {\n            try\n            {\n                var status = new VideoCodecBaseStateMessage\n                {\n                    CameraSelfViewIsOn = Codec is IHasCodecSelfView\n                                         && (Codec as IHasCodecSelfView).SelfviewIsOnFeedback.BoolValue\n                };\n\n                PostStatusMessage(status);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error posting camera self view\");\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        private void PostCameraMode()\n        {\n            try\n            {\n                var status = new VideoCodecBaseStateMessage\n                {\n                    CameraMode = GetCameraMode()\n                };\n\n                PostStatusMessage(status);\n            }\n            catch (Exception ex)\n            {\n                this.LogError(ex, \"Error posting camera mode\");\n            }\n        }\n\n        private void PostSelectedCamera()\n        {\n            try\n            {\n                var camerasCodec = Codec as IHasCodecCameras;\n\n                var status = new VideoCodecBaseStateMessage\n                {\n                    Cameras = new CameraStatus() { SelectedCamera = GetSelectedCamera(camerasCodec) },\n                    Presets = GetCurrentPresets()\n                };\n\n                PostStatusMessage(status);\n            }\n            catch (Exception e)\n            {\n                this.LogError(e, \"Error posting selected camera\");\n            }\n        }\n\n        private void PostCameraPresets()\n        {\n            try\n            {\n                var status = new VideoCodecBaseStateMessage\n                {\n                    Presets = GetCurrentPresets()\n                };\n\n                PostStatusMessage(status);\n            }\n            catch (Exception e)\n            {\n                this.LogError(e, \"Error posting camera presets\");\n            }\n        }\n\n        private Camera GetSelectedCamera(IHasCodecCameras camerasCodec)\n        {\n            var camera = new Camera();\n\n            if (camerasCodec.SelectedCameraFeedback != null)\n                camera.Key = camerasCodec.SelectedCameraFeedback.StringValue;\n            if (camerasCodec.SelectedCamera != null)\n            {\n                camera.Name = camerasCodec.SelectedCamera.Name;\n\n                camera.Capabilities = new CameraCapabilities()\n                {\n                    CanPan = camerasCodec.SelectedCamera.CanPan,\n                    CanTilt = camerasCodec.SelectedCamera.CanTilt,\n                    CanZoom = camerasCodec.SelectedCamera.CanZoom,\n                    CanFocus = camerasCodec.SelectedCamera.CanFocus,\n                };\n            }\n\n            if (camerasCodec.ControllingFarEndCameraFeedback != null)\n                camera.IsFarEnd = camerasCodec.ControllingFarEndCameraFeedback.BoolValue;\n\n\n            return camera;\n        }\n\n        private List<CodecRoomPreset> GetCurrentPresets()\n        {\n            var presetsCodec = Codec as IHasCodecRoomPresets;\n\n            List<CodecRoomPreset> currentPresets = null;\n\n            if (presetsCodec != null && Codec is IHasFarEndCameraControl &&\n                (Codec as IHasFarEndCameraControl).ControllingFarEndCameraFeedback.BoolValue)\n                currentPresets = presetsCodec.FarEndRoomPresets;\n            else if (presetsCodec != null) currentPresets = presetsCodec.NearEndPresets;\n\n            return currentPresets;\n        }\n    }\n\n    /// <summary>\n    /// Represents a VideoCodecBaseStateMessage\n    /// </summary>\n    public class VideoCodecBaseStateMessage : DeviceStateMessageBase\n    {\n\n        [JsonProperty(\"calls\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<CodecActiveCallItem> Calls { get; set; }\n\n        [JsonProperty(\"cameraMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public string CameraMode { get; set; }\n\n        [JsonProperty(\"cameraSelfView\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CameraSelfViewIsOn { get; set; }\n\n        [JsonProperty(\"cameras\", NullValueHandling = NullValueHandling.Ignore)]\n        /// <summary>\n        /// Gets or sets the Cameras\n        /// </summary>\n        public CameraStatus Cameras { get; set; }\n\n        [JsonProperty(\"cameraSupportsAutoMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CameraSupportsAutoMode { get; set; }\n\n        [JsonProperty(\"cameraSupportsOffMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CameraSupportsOffMode { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the CurrentDialString\n        /// </summary>\n        [JsonProperty(\"currentDialString\", NullValueHandling = NullValueHandling.Ignore)]\n        public string CurrentDialString { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the CurrentDirectory\n        /// </summary>\n        [JsonProperty(\"currentDirectory\", NullValueHandling = NullValueHandling.Ignore)]\n        public CodecDirectory CurrentDirectory { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the DirectorySelectedFolderName\n        /// </summary>\n        [JsonProperty(\"directorySelectedFolderName\", NullValueHandling = NullValueHandling.Ignore)]\n        public string DirectorySelectedFolderName { get; set; }\n\n        [JsonProperty(\"hasCameras\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasCameras { get; set; }\n\n        [JsonProperty(\"hasDirectory\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasDirectory { get; set; }\n\n        [JsonProperty(\"hasDirectorySearch\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasDirectorySearch { get; set; }\n\n        [JsonProperty(\"hasPresets\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasPresets { get; set; }\n\n        [JsonProperty(\"hasRecents\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? HasRecents { get; set; }\n\n        [JsonProperty(\"initialPhonebookSyncComplete\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? InitialPhonebookSyncComplete { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Info\n        /// </summary>\n        [JsonProperty(\"info\", NullValueHandling = NullValueHandling.Ignore)]\n        public VideoCodecInfo Info { get; set; }\n\n        [JsonProperty(\"isInCall\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsInCall { get; set; }\n\n        [JsonProperty(\"isReady\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsReady { get; set; }\n\n        [JsonProperty(\"isZoomRoom\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsZoomRoom { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the MeetingInfo\n        /// </summary>\n        [JsonProperty(\"meetingInfo\", NullValueHandling = NullValueHandling.Ignore)]\n        public MeetingInfo MeetingInfo { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Presets\n        /// </summary>\n        [JsonProperty(\"presets\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<CodecRoomPreset> Presets { get; set; }\n\n        [JsonProperty(\"privacyModeIsOn\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? PrivacyModeIsOn { get; set; }\n\n        [JsonProperty(\"receivingContent\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? ReceivingContent { get; set; }\n\n        [JsonProperty(\"recentCalls\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<CodecCallHistory.CallHistoryEntry> RecentCalls { get; set; }\n\n        [JsonProperty(\"sharingContentIsOn\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? SharingContentIsOn { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the SharingSource\n        /// </summary>\n        [JsonProperty(\"sharingSource\", NullValueHandling = NullValueHandling.Ignore)]\n        public string SharingSource { get; set; }\n\n        [JsonProperty(\"showCamerasWhenNotInCall\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? ShowCamerasWhenNotInCall { get; set; }\n\n        [JsonProperty(\"showSelfViewByDefault\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? ShowSelfViewByDefault { get; set; }\n\n        [JsonProperty(\"standbyIsOn\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? StandbyIsOn { get; set; }\n\n        [JsonProperty(\"supportsAdHocMeeting\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? SupportsAdHocMeeting { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a CameraStatus\n    /// </summary>\n    public class CameraStatus\n    {\n        [JsonProperty(\"cameraManualSupported\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CameraManualIsSupported { get; set; }\n\n        [JsonProperty(\"cameraAutoSupported\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CameraAutoIsSupported { get; set; }\n\n        [JsonProperty(\"cameraOffSupported\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CameraOffIsSupported { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the CameraMode\n        /// </summary>\n        [JsonProperty(\"cameraMode\", NullValueHandling = NullValueHandling.Ignore)]\n        public string CameraMode { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Cameras\n        /// </summary>\n        [JsonProperty(\"cameraList\", NullValueHandling = NullValueHandling.Ignore)]\n        public List<CameraBase> Cameras { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the SelectedCamera\n        /// </summary>\n        [JsonProperty(\"selectedCamera\", NullValueHandling = NullValueHandling.Ignore)]\n        public Camera SelectedCamera { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a Camera\n    /// </summary>\n    public class Camera\n    {\n\n        /// <summary>\n        /// Gets or sets the Key\n        /// </summary>\n        [JsonProperty(\"key\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Key { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Name\n        /// </summary>\n        [JsonProperty(\"name\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Name { get; set; }\n\n        [JsonProperty(\"isFarEnd\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? IsFarEnd { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the Capabilities\n        /// </summary>\n        [JsonProperty(\"capabilities\", NullValueHandling = NullValueHandling.Ignore)]\n        public CameraCapabilities Capabilities { get; set; }\n    }\n\n    /// <summary>\n    /// Represents a CameraCapabilities\n    /// </summary>\n    public class CameraCapabilities\n    {\n        [JsonProperty(\"canPan\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CanPan { get; set; }\n\n        [JsonProperty(\"canTilt\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CanTilt { get; set; }\n\n        [JsonProperty(\"canZoom\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CanZoom { get; set; }\n\n        [JsonProperty(\"canFocus\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool? CanFocus { get; set; }\n\n    }\n\n    /// <summary>\n    /// Represents a VideoCodecBaseEventMessage\n    /// </summary>\n    public class VideoCodecBaseEventMessage : DeviceEventMessageBase\n    {\n\n    }\n\n    /// <summary>\n    /// Represents a PasswordPromptEventMessage\n    /// </summary>\n    public class PasswordPromptEventMessage : VideoCodecBaseEventMessage\n    {\n        /// <summary>\n        /// Gets or sets the Message\n        /// </summary>\n        [JsonProperty(\"message\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Message { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the LastAttemptWasIncorrect\n        /// </summary>\n        [JsonProperty(\"lastAttemptWasIncorrect\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool LastAttemptWasIncorrect { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the LoginAttemptFailed\n        /// </summary>\n        [JsonProperty(\"loginAttemptFailed\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool LoginAttemptFailed { get; set; }\n\n\n        /// <summary>\n        /// Gets or sets the LoginAttemptCancelled\n        /// </summary>\n        [JsonProperty(\"loginAttemptCancelled\", NullValueHandling = NullValueHandling.Ignore)]\n        public bool LoginAttemptCancelled { get; set; }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/MobileControlMessage.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing PepperDash.Essentials.Core.DeviceTypeInterfaces;\n\nnamespace PepperDash.Essentials.AppServer.Messengers\n{\n    /// <summary>\n    /// Represents a MobileControlMessage\n    /// </summary>\n    public class MobileControlMessage : IMobileControlMessage\n    {\n        [JsonProperty(\"type\")]\n        /// <summary>\n        /// Gets or sets the Type\n        /// </summary>\n        public string Type { get; set; }\n\n        [JsonProperty(\"clientId\")]\n        /// <summary>\n        /// Gets or sets the ClientId\n        /// </summary>\n        public string ClientId { get; set; }\n\n        [JsonProperty(\"content\")]\n        /// <summary>\n        /// Gets or sets the Content\n        /// </summary>\n        public JToken Content { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/MobileControlSimpleContent.cs",
    "content": "﻿using Newtonsoft.Json;\n\nnamespace PepperDash.Essentials.AppServer\n{\n    /// <summary>\n    /// Represents a MobileControlSimpleContent\n    /// </summary>\n    public class MobileControlSimpleContent<T>\n    {\n        [JsonProperty(\"value\", NullValueHandling = NullValueHandling.Ignore)]\n        /// <summary>\n        /// Gets or sets the Value\n        /// </summary>\n        public T Value { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/PepperDash.Essentials.MobileControl.Messengers.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <RootNamespace>PepperDash.Essentials.AppServer</RootNamespace>\n    <TargetFramework>net472</TargetFramework>\n    <AssemblyTitle>mobile-control-messengers</AssemblyTitle>\n    <AssemblyName>mobile-control-messengers</AssemblyName>\n    <Product>mobile-control-messengers</Product>\n    <Copyright>Copyright ©  2024</Copyright>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n    <InformationalVersion>$(Version)</InformationalVersion>\n    <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>\n    <Authors>PepperDash Technology</Authors>\n    <PackageId>PepperDash.Essentials.MobileControl.Messengers</PackageId>\n    <PackageTags>crestron 4series</PackageTags>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n    <DefineConstants>$(DefineConstants);SERIES4</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <DefineConstants>$(DefineConstants);SERIES4</DefineConstants>\n    <DocumentationFile>bin\\$(Configuration)\\mobile-control-messengers.xml</DocumentationFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Remove=\"Messengers\\SIMPLAtcMessenger.cs\" />\n    <Compile Remove=\"Messengers\\SIMPLCameraMessenger.cs\" />\n    <Compile Remove=\"Messengers\\SIMPLDirectRouteMessenger.cs\" />\n    <Compile Remove=\"Messengers\\SimplMessengerPropertiesConfig.cs\" />\n    <Compile Remove=\"Messengers\\SIMPLRouteMessenger.cs\" />\n    <Compile Remove=\"Messengers\\SIMPLVtcMessenger.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Crestron.SimplSharp.SDK.ProgramLibrary\" Version=\"2.21.90\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\PepperDash.Core\\PepperDash.Core.csproj\" />\n    <ProjectReference Include=\"..\\PepperDash.Essentials.Core\\PepperDash.Essentials.Core.csproj\">\n      <Private>false</Private>\n      <ExcludeAssets>runtime</ExcludeAssets>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\PepperDash.Essentials.Devices.Common\\PepperDash.Essentials.Devices.Common.csproj\">\n      <Private>false</Private>\n      <ExcludeAssets>runtime</ExcludeAssets>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Net.Http\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/SIMPLJoinMaps/MobileControlSIMPLRoomJoinMap.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\n\nnamespace PepperDash.Essentials.AppServer\n{\n    // ReSharper disable once InconsistentNaming\n    /// <summary>\n    /// Represents a MobileControlSIMPLRoomJoinMap\n    /// </summary>\n    public class MobileControlSIMPLRoomJoinMap : JoinMapBaseAdvanced\n    {\n        [JoinName(\"QrCodeUrl\")]\n        public JoinDataComplete QrCodeUrl =\n            new JoinDataComplete(new JoinData { JoinNumber = 403, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"QR Code URL\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"PortalSystemUrl\")]\n        public JoinDataComplete PortalSystemUrl =\n            new JoinDataComplete(new JoinData { JoinNumber = 404, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Portal System URL\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n        [JoinName(\"MasterVolume\")]\n        public JoinDataComplete MasterVolume =\n            new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Master Volume Mute Toggle/FB/Level/Label\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.DigitalAnalogSerial\n                });\n\n        [JoinName(\"VolumeJoinStart\")]\n        public JoinDataComplete VolumeJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 8 },\n                new JoinMetadata\n                {\n                    Description = \"Volume Mute Toggle/FB/Level/Label\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.DigitalAnalogSerial\n                });\n\n        [JoinName(\"PrivacyMute\")]\n        public JoinDataComplete PrivacyMute =\n            new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Privacy Mute Toggle/FB\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"PromptForCode\")]\n        public JoinDataComplete PromptForCode =\n            new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Prompt User for Code\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ClientJoined\")]\n        public JoinDataComplete ClientJoined =\n            new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Client Joined\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ActivityPhoneCallEnable\")]\n        public JoinDataComplete ActivityPhoneCallEnable =\n            new JoinDataComplete(new JoinData { JoinNumber = 48, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Enable Activity Phone Call\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"ActivityVideoCallEnable\")]\n        public JoinDataComplete ActivityVideoCallEnable =\n            new JoinDataComplete(new JoinData { JoinNumber = 49, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Enable Activity Video Call\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"ActivityShare\")]\n        public JoinDataComplete ActivityShare =\n            new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Activity Share\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ActivityPhoneCall\")]\n        public JoinDataComplete ActivityPhoneCall =\n            new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Activity Phone Call\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ActivityVideoCall\")]\n        public JoinDataComplete ActivityVideoCall =\n            new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Activity Video Call\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ShutdownPromptDuration\")]\n        public JoinDataComplete ShutdownPromptDuration =\n            new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Shutdown Cancel\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Analog\n                });\n\n        [JoinName(\"ShutdownCancel\")]\n        public JoinDataComplete ShutdownCancel =\n            new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Shutdown Cancel\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ShutdownEnd\")]\n        public JoinDataComplete ShutdownEnd =\n            new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Shutdown End\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ShutdownStart\")]\n        public JoinDataComplete ShutdownStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 63, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Shutdown Start\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"SourceHasChanged\")]\n        public JoinDataComplete SourceHasChanged =\n            new JoinDataComplete(new JoinData { JoinNumber = 71, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Source Changed\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CurrentSourceKey\")]\n        public JoinDataComplete CurrentSourceKey =\n            new JoinDataComplete(new JoinData { JoinNumber = 71, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Key of selected source\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n\n        [JoinName(\"ConfigIsLocal\")]\n        public JoinDataComplete ConfigIsLocal =\n            new JoinDataComplete(new JoinData { JoinNumber = 100, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Config is local to Essentials\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"NumberOfAuxFaders\")]\n        public JoinDataComplete NumberOfAuxFaders =\n            new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Number of Auxilliary Faders\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Analog\n                });\n\n        [JoinName(\"SpeedDialNameStartJoin\")]\n        public JoinDataComplete SpeedDialNameStartJoin =\n            new JoinDataComplete(new JoinData { JoinNumber = 241, JoinSpan = 10 },\n                new JoinMetadata\n                {\n                    Description = \"Speed Dial names\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"SpeedDialNumberStartJoin\")]\n        public JoinDataComplete SpeedDialNumberStartJoin =\n            new JoinDataComplete(new JoinData { JoinNumber = 251, JoinSpan = 10 },\n                new JoinMetadata\n                {\n                    Description = \"Speed Dial numbers\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"SpeedDialVisibleStartJoin\")]\n        public JoinDataComplete SpeedDialVisibleStartJoin =\n            new JoinDataComplete(new JoinData { JoinNumber = 261, JoinSpan = 10 },\n                new JoinMetadata\n                {\n                    Description = \"Speed Dial Visible\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"RoomIsOn\")]\n        public JoinDataComplete RoomIsOn =\n            new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Room Is On\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"UserCodeToSystem\")]\n        public JoinDataComplete UserCodeToSystem =\n            new JoinDataComplete(new JoinData { JoinNumber = 401, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"User Code\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"ServerUrl\")]\n        public JoinDataComplete ServerUrl =\n            new JoinDataComplete(new JoinData { JoinNumber = 402, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Server URL\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"ConfigRoomName\")]\n        public JoinDataComplete ConfigRoomName =\n            new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Room Name\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"ConfigHelpMessage\")]\n        public JoinDataComplete ConfigHelpMessage =\n            new JoinDataComplete(new JoinData { JoinNumber = 502, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Room help message\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"ConfigHelpNumber\")]\n        public JoinDataComplete ConfigHelpNumber =\n            new JoinDataComplete(new JoinData { JoinNumber = 503, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Room help number\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"ConfigRoomPhoneNumber\")]\n        public JoinDataComplete ConfigRoomPhoneNumber =\n            new JoinDataComplete(new JoinData { JoinNumber = 504, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Room phone number\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"ConfigRoomURI\")]\n        public JoinDataComplete ConfigRoomUri =\n            new JoinDataComplete(new JoinData { JoinNumber = 505, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Room URI\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"ApiOnlineAndAuthorized\")]\n        public JoinDataComplete ApiOnlineAndAuthorized =\n            new JoinDataComplete(new JoinData { JoinNumber = 500, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Config info from SIMPL is ready\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"ConfigIsReady\")]\n        public JoinDataComplete ConfigIsReady =\n            new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Config info from SIMPL is ready\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ReadyForConfig\")]\n        public JoinDataComplete ReadyForConfig =\n            new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Config info from SIMPL is ready\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"HideVideoConfRecents\")]\n        public JoinDataComplete HideVideoConfRecents =\n            new JoinDataComplete(new JoinData { JoinNumber = 502, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Hide Video Conference Recents\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"ShowCameraWhenNotInCall\")]\n        public JoinDataComplete ShowCameraWhenNotInCall =\n            new JoinDataComplete(new JoinData { JoinNumber = 503, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Show camera when not in call\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"UseSourceEnabled\")]\n        public JoinDataComplete UseSourceEnabled =\n            new JoinDataComplete(new JoinData { JoinNumber = 504, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Use Source Enabled Joins\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n\n        [JoinName(\"SourceShareDisableJoinStart\")]\n        public JoinDataComplete SourceShareDisableJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 601, JoinSpan = 20 },\n                new JoinMetadata\n                {\n                    Description = \"Source is not sharable\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"SourceIsEnabledJoinStart\")]\n        public JoinDataComplete SourceIsEnabledJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 621, JoinSpan = 20 },\n                new JoinMetadata\n                {\n                    Description = \"Source is enabled/visible\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"SourceIsControllableJoinStart\")]\n        public JoinDataComplete SourceIsControllableJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 641, JoinSpan = 20 },\n            new JoinMetadata\n            {\n                Description = \"Source is controllable\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"SourceIsAudioSourceJoinStart\")]\n        public JoinDataComplete SourceIsAudioSourceJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 661, JoinSpan = 20 },\n            new JoinMetadata\n            {\n                Description = \"Source is Audio Source\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\n        [JoinName(\"SourceNameJoinStart\")]\n        public JoinDataComplete SourceNameJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 601, JoinSpan = 20 },\n                new JoinMetadata\n                {\n                    Description = \"Source Names\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"SourceIconJoinStart\")]\n        public JoinDataComplete SourceIconJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 621, JoinSpan = 20 },\n                new JoinMetadata\n                {\n                    Description = \"Source Icons\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"SourceKeyJoinStart\")]\n        public JoinDataComplete SourceKeyJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 641, JoinSpan = 20 },\n                new JoinMetadata\n                {\n                    Description = \"Source Keys\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"SourceControlDeviceKeyJoinStart\")]\n        public JoinDataComplete SourceControlDeviceKeyJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 20 },\n            new JoinMetadata\n            {\n                Description = \"Source Control Device Keys\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n        [JoinName(\"SourceTypeJoinStart\")]\n        public JoinDataComplete SourceTypeJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 661, JoinSpan = 20 },\n                new JoinMetadata\n                {\n                    Description = \"Source Types\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"CameraNearNameStart\")]\n        public JoinDataComplete CameraNearNameStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 761, JoinSpan = 10 },\n                new JoinMetadata\n                {\n                    Description = \"Near End Camera Names\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"CameraFarName\")]\n        public JoinDataComplete CameraFarName =\n            new JoinDataComplete(new JoinData { JoinNumber = 771, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Far End Camera Name\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        #region Advanced Sharing\n        [JoinName(\"SupportsAdvancedSharing\")]\n        public JoinDataComplete SupportsAdvancedSharing =\n            new JoinDataComplete(new JoinData { JoinNumber = 505, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Supports Advanced Sharing\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"UseDestinationEnable\")]\n        public JoinDataComplete UseDestinationEnable =\n            new JoinDataComplete(new JoinData { JoinNumber = 506, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Use Destination Enable\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n\n        [JoinName(\"UserCanChangeShareMode\")]\n        public JoinDataComplete UserCanChangeShareMode =\n            new JoinDataComplete(new JoinData { JoinNumber = 507, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Share Mode Toggle Visible to User\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"DestinationNameJoinStart\")]\n        public JoinDataComplete DestinationNameJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 801, JoinSpan = 10 },\n            new JoinMetadata\n            {\n                Description = \"Destination Name\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n        [JoinName(\"DestinationDeviceKeyJoinStart\")]\n        public JoinDataComplete DestinationDeviceKeyJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 811, JoinSpan = 10 },\n            new JoinMetadata\n            {\n                Description = \"Destination Device Key\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n        [JoinName(\"DestinationTypeJoinStart\")]\n        public JoinDataComplete DestinationTypeJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 821, JoinSpan = 10 },\n            new JoinMetadata\n            {\n                Description = \"Destination type. Should be Audio, Video, AudioVideo\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n        [JoinName(\"DestinationIsEnabledJoinStart\")]\n        public JoinDataComplete DestinationIsEnabledJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 801, JoinSpan = 10 },\n            new JoinMetadata\n            {\n                Description = \"Show Destination on UI\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n        #endregion\n\n        public MobileControlSIMPLRoomJoinMap(uint joinStart)\n            : base(joinStart, typeof(MobileControlSIMPLRoomJoinMap))\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/SIMPLJoinMaps/MobileControlSIMPLRunDirectRouteActionJoinMap.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\nnamespace PepperDash.Essentials.AppServer\n{\n    /// <summary>\n    /// Represents a MobileControlSIMPLRunDirectRouteActionJoinMap\n    /// </summary>\n    public class MobileControlSIMPLRunDirectRouteActionJoinMap : JoinMapBaseAdvanced\n    {\n        [JoinName(\"AdvancedSharingModeFb\")]\n        public JoinDataComplete AdvancedSharingModeFb =\n            new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Use Advanced Sharing Mode\",\n                JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"AdvancedSharingModeOn\")]\n        public JoinDataComplete AdvancedSharingModeOn =\n            new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Use Advanced Sharing Mode\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"AdvancedSharingModeOff\")]\n        public JoinDataComplete AdvancedSharingModeOff =\n            new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Use Advanced Sharing Mode\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"AdvancedSharingModeToggle\")]\n        public JoinDataComplete AdvancedSharingModeToggle =\n            new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },\n            new JoinMetadata\n            {\n                Description = \"Use Advanced Sharing Mode\",\n                JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                JoinType = eJoinType.Digital\n            });\n\n        [JoinName(\"SourceForDestinationJoinStart\")]\n        public JoinDataComplete SourceForDestinationJoinStart =\n            new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 10 },\n            new JoinMetadata\n            {\n                Description = \"Source to Route to Destination & FB\",\n                JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                JoinType = eJoinType.Serial\n            });\n\n        [JoinName(\"SourceForDestinationAudio\")]\n        public JoinDataComplete SourceForDestinationAudio =\n            new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1 },\n                new JoinMetadata\n                {\n                    Description = \"Source to Route to Destination & FB\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        public MobileControlSIMPLRunDirectRouteActionJoinMap(uint joinStart)\n            : base(joinStart, typeof(MobileControlSIMPLRunDirectRouteActionJoinMap))\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/SIMPLJoinMaps/SIMPLAtcJoinMap.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\n\nnamespace PepperDash.Essentials.AppServer\n{\n    /// <summary>\n    /// Represents a SIMPLAtcJoinMap\n    /// </summary>\n    public class SIMPLAtcJoinMap : JoinMapBaseAdvanced\n    {\n        [JoinName(\"EndCall\")]\n        public JoinDataComplete EndCall =\n            new JoinDataComplete(new JoinData() { JoinNumber = 21, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Hang Up\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"IncomingAnswer\")]\n        public JoinDataComplete IncomingAnswer =\n            new JoinDataComplete(new JoinData() { JoinNumber = 51, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Answer Incoming Call\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"IncomingReject\")]\n        public JoinDataComplete IncomingReject =\n            new JoinDataComplete(new JoinData() { JoinNumber = 52, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Reject Incoming Call\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"SpeedDialStart\")]\n        public JoinDataComplete SpeedDialStart =\n            new JoinDataComplete(new JoinData() { JoinNumber = 41, JoinSpan = 4 },\n                new JoinMetadata()\n                {\n                    Description = \"Speed Dial\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CurrentDialString\")]\n        public JoinDataComplete CurrentDialString =\n            new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Dial String\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"CurrentCallNumber\")]\n        public JoinDataComplete CurrentCallNumber =\n            new JoinDataComplete(new JoinData() { JoinNumber = 11, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Call Number\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"CurrentCallName\")]\n        public JoinDataComplete CurrentCallName =\n            new JoinDataComplete(new JoinData() { JoinNumber = 12, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Call Name\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"HookState\")]\n        public JoinDataComplete HookState =\n            new JoinDataComplete(new JoinData() { JoinNumber = 21, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Hook State\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"CallDirection\")]\n        public JoinDataComplete CallDirection =\n            new JoinDataComplete(new JoinData() { JoinNumber = 22, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Call Direction\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"IncomingCallName\")]\n        public JoinDataComplete IncomingCallName =\n            new JoinDataComplete(new JoinData() { JoinNumber = 51, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Incoming Call Name\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"IncomingCallNumber\")]\n        public JoinDataComplete IncomingCallNumber =\n            new JoinDataComplete(new JoinData() { JoinNumber = 52, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Incoming Call Number\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"0\")]\n        public JoinDataComplete Dtmf0 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 10, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 0\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"1\")]\n        public JoinDataComplete Dtmf1 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 1\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"2\")]\n        public JoinDataComplete Dtmf2 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 2, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 2\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"3\")]\n        public JoinDataComplete Dtmf3 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 3, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 3\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"4\")]\n        public JoinDataComplete Dtmf4 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 4, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 4\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"5\")]\n        public JoinDataComplete Dtmf5 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 5, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 5\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"6\")]\n        public JoinDataComplete Dtmf6 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 6, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 6\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"7\")]\n        public JoinDataComplete Dtmf7 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 7, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 7\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"8\")]\n        public JoinDataComplete Dtmf8 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 8, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 8\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"9\")]\n        public JoinDataComplete Dtmf9 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 9, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 9\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"*\")]\n        public JoinDataComplete DtmfStar =\n            new JoinDataComplete(new JoinData() { JoinNumber = 11, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF *\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"#\")]\n        public JoinDataComplete DtmfPound =\n            new JoinDataComplete(new JoinData() { JoinNumber = 12, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF #\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        /// <summary>\n        /// Constructor that passes the joinStart to the base class\n        /// </summary>\n        /// <param name=\"joinStart\"></param>\n        public SIMPLAtcJoinMap(uint joinStart)\n            : base(joinStart, typeof(SIMPLAtcJoinMap))\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/PepperDash.Essentials.MobileControl.Messengers/SIMPLJoinMaps/SIMPLVtcJoinMap.cs",
    "content": "﻿using PepperDash.Essentials.Core;\n\n\nnamespace PepperDash.Essentials.AppServer\n{\n    /// <summary>\n    /// Represents a SIMPLVtcJoinMap\n    /// </summary>\n    public class SIMPLVtcJoinMap : JoinMapBaseAdvanced\n    {\n        [JoinName(\"EndCall\")]\n        public JoinDataComplete EndCall =\n            new JoinDataComplete(new JoinData() { JoinNumber = 24, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Hang Up\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"IncomingCall\")]\n        public JoinDataComplete IncomingCall =\n            new JoinDataComplete(new JoinData() { JoinNumber = 50, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Incoming Call\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"IncomingAnswer\")]\n        public JoinDataComplete IncomingAnswer =\n            new JoinDataComplete(new JoinData() { JoinNumber = 51, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Answer Incoming Call\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"IncomingReject\")]\n        public JoinDataComplete IncomingReject =\n            new JoinDataComplete(new JoinData() { JoinNumber = 52, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Reject Incoming Call\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"SpeedDialStart\")]\n        public JoinDataComplete SpeedDialStart =\n            new JoinDataComplete(new JoinData() { JoinNumber = 41, JoinSpan = 4 },\n                new JoinMetadata()\n                {\n                    Description = \"Speed Dial\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DirectorySearchBusy\")]\n        public JoinDataComplete DirectorySearchBusy =\n            new JoinDataComplete(new JoinData() { JoinNumber = 100, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory Search Busy FB\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DirectoryLineSelected\")]\n        public JoinDataComplete DirectoryLineSelected =\n            new JoinDataComplete(new JoinData() { JoinNumber = 101, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory Line Selected FB\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DirectoryEntryIsContact\")]\n        public JoinDataComplete DirectoryEntryIsContact =\n            new JoinDataComplete(new JoinData() { JoinNumber = 101, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory Selected Entry Is Contact FB\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DirectoryIsRoot\")]\n        public JoinDataComplete DirectoryIsRoot =\n            new JoinDataComplete(new JoinData() { JoinNumber = 102, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory is on Root FB\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DDirectoryHasChanged\")]\n        public JoinDataComplete DDirectoryHasChanged =\n            new JoinDataComplete(new JoinData() { JoinNumber = 103, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory has changed FB\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DirectoryRoot\")]\n        public JoinDataComplete DirectoryRoot =\n            new JoinDataComplete(new JoinData() { JoinNumber = 104, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Go to Directory Root\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DirectoryFolderBack\")]\n        public JoinDataComplete DirectoryFolderBack =\n            new JoinDataComplete(new JoinData() { JoinNumber = 105, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Go back one directory level\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DirectoryDialSelectedLine\")]\n        public JoinDataComplete DirectoryDialSelectedLine =\n            new JoinDataComplete(new JoinData() { JoinNumber = 106, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Dial selected directory line\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraTiltUp\")]\n        public JoinDataComplete CameraTiltUp =\n            new JoinDataComplete(new JoinData() { JoinNumber = 111, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Tilt Up\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraTiltDown\")]\n        public JoinDataComplete CameraTiltDown =\n            new JoinDataComplete(new JoinData() { JoinNumber = 112, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Tilt Down\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraPanLeft\")]\n        public JoinDataComplete CameraPanLeft =\n            new JoinDataComplete(new JoinData() { JoinNumber = 113, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Pan Left\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraPanRight\")]\n        public JoinDataComplete CameraPanRight =\n            new JoinDataComplete(new JoinData() { JoinNumber = 114, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Pan Right\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraZoomIn\")]\n        public JoinDataComplete CameraZoomIn =\n            new JoinDataComplete(new JoinData() { JoinNumber = 115, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Zoom In\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraZoomOut\")]\n        public JoinDataComplete CameraZoomOut =\n            new JoinDataComplete(new JoinData() { JoinNumber = 116, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Zoom Out\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraPresetStart\")]\n        public JoinDataComplete CameraPresetStart =\n            new JoinDataComplete(new JoinData() { JoinNumber = 121, JoinSpan = 5 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Presets\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraModeAuto\")]\n        public JoinDataComplete CameraModeAuto =\n            new JoinDataComplete(new JoinData() { JoinNumber = 131, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Mode Auto\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraModeManual\")]\n        public JoinDataComplete CameraModeManual =\n            new JoinDataComplete(new JoinData() { JoinNumber = 132, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Mode Manual\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraModeOff\")]\n        public JoinDataComplete CameraModeOff =\n            new JoinDataComplete(new JoinData() { JoinNumber = 133, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Mode Off\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraSelfView\")]\n        public JoinDataComplete CameraSelfView =\n            new JoinDataComplete(new JoinData() { JoinNumber = 141, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Self View Toggle/FB\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraLayout\")]\n        public JoinDataComplete CameraLayout =\n            new JoinDataComplete(new JoinData() { JoinNumber = 142, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Layout Toggle\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraSupportsAutoMode\")]\n        public JoinDataComplete CameraSupportsAutoMode =\n            new JoinDataComplete(new JoinData() { JoinNumber = 143, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Supports Auto Mode FB\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraSupportsOffMode\")]\n        public JoinDataComplete CameraSupportsOffMode =\n            new JoinDataComplete(new JoinData() { JoinNumber = 144, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Supports Off Mode FB\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"CameraNumberSelect\")]\n        public JoinDataComplete CameraNumberSelect =\n            new JoinDataComplete(new JoinData() { JoinNumber = 60, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Camera Number Select/FB\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"DirectorySelectRow\")]\n        public JoinDataComplete DirectorySelectRow =\n            new JoinDataComplete(new JoinData() { JoinNumber = 101, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory Select Row\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Analog\n                });\n\n        [JoinName(\"DirectoryRowCount\")]\n        public JoinDataComplete DirectoryRowCount =\n            new JoinDataComplete(new JoinData() { JoinNumber = 101, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory Row Count FB\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Analog\n                });\n\n        [JoinName(\"CurrentDialString\")]\n        public JoinDataComplete CurrentDialString =\n            new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Dial String\",\n                    JoinCapabilities = eJoinCapabilities.ToFromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"CurrentCallName\")]\n        public JoinDataComplete CurrentCallName =\n            new JoinDataComplete(new JoinData() { JoinNumber = 2, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Call Name\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"CurrentCallNumber\")]\n        public JoinDataComplete CurrentCallNumber =\n            new JoinDataComplete(new JoinData() { JoinNumber = 3, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Call Number\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"HookState\")]\n        public JoinDataComplete HookState =\n            new JoinDataComplete(new JoinData() { JoinNumber = 31, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Hook State\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"CallDirection\")]\n        public JoinDataComplete CallDirection =\n            new JoinDataComplete(new JoinData() { JoinNumber = 22, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Current Call Direction\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"IncomingCallName\")]\n        public JoinDataComplete IncomingCallName =\n            new JoinDataComplete(new JoinData() { JoinNumber = 51, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Incoming Call Name\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"IncomingCallNumber\")]\n        public JoinDataComplete IncomingCallNumber =\n            new JoinDataComplete(new JoinData() { JoinNumber = 52, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Incoming Call Number\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"DirectorySearchString\")]\n        public JoinDataComplete DirectorySearchString =\n            new JoinDataComplete(new JoinData() { JoinNumber = 100, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory Search String\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"DirectoryEntriesStart\")]\n        public JoinDataComplete DirectoryEntriesStart =\n            new JoinDataComplete(new JoinData() { JoinNumber = 101, JoinSpan = 255 },\n                new JoinMetadata()\n                {\n                    Description = \"Directory Entries\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"DirectoryEntrySelectedName\")]\n        public JoinDataComplete DirectoryEntrySelectedName =\n            new JoinDataComplete(new JoinData() { JoinNumber = 356, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Selected Directory Entry Name\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"DirectoryEntrySelectedNumber\")]\n        public JoinDataComplete DirectoryEntrySelectedNumber =\n            new JoinDataComplete(new JoinData() { JoinNumber = 357, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Selected Directory Entry Number\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"DirectorySelectedFolderName\")]\n        public JoinDataComplete DirectorySelectedFolderName =\n            new JoinDataComplete(new JoinData() { JoinNumber = 358, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"Selected Directory Folder Name\",\n                    JoinCapabilities = eJoinCapabilities.FromSIMPL,\n                    JoinType = eJoinType.Serial\n                });\n\n        [JoinName(\"1\")]\n        public JoinDataComplete Dtmf1 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 1, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 1\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"2\")]\n        public JoinDataComplete Dtmf2 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 2, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 2\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"3\")]\n        public JoinDataComplete Dtmf3 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 3, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 3\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"4\")]\n        public JoinDataComplete Dtmf4 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 4, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 4\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"5\")]\n        public JoinDataComplete Dtmf5 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 5, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 5\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"6\")]\n        public JoinDataComplete Dtmf6 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 6, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 6\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"7\")]\n        public JoinDataComplete Dtmf7 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 7, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 7\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"8\")]\n        public JoinDataComplete Dtmf8 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 8, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 8\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"9\")]\n        public JoinDataComplete Dtmf9 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 9, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 9\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"0\")]\n        public JoinDataComplete Dtmf0 =\n            new JoinDataComplete(new JoinData() { JoinNumber = 10, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF 0\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"*\")]\n        public JoinDataComplete DtmfStar =\n            new JoinDataComplete(new JoinData() { JoinNumber = 11, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF *\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        [JoinName(\"#\")]\n        public JoinDataComplete DtmfPound =\n            new JoinDataComplete(new JoinData() { JoinNumber = 12, JoinSpan = 1 },\n                new JoinMetadata()\n                {\n                    Description = \"DTMF #\",\n                    JoinCapabilities = eJoinCapabilities.ToSIMPL,\n                    JoinType = eJoinType.Digital\n                });\n\n        public SIMPLVtcJoinMap(uint joinStart)\n            : base(joinStart, typeof(SIMPLVtcJoinMap))\n        {\n        }\n    }\n}"
  }
]