diff --git a/.github/scripts/ZipBuildOutput.ps1 b/.github/scripts/ZipBuildOutput.ps1 index 5ca867ef..bf3c9d94 100644 --- a/.github/scripts/ZipBuildOutput.ps1 +++ b/.github/scripts/ZipBuildOutput.ps1 @@ -10,7 +10,7 @@ Get-ChildItem ($destination) $exclusions = @(git submodule foreach --quiet 'echo $name') # Trying to get any .json schema files (not currently working) # Gets any files with the listed extensions. -Get-ChildItem -recurse -Path "$($Env:GITHUB_WORKSPACE)" -include "*.clz", "*.cpz", "*.cplz", "*.dll" | ForEach-Object { +Get-ChildItem -recurse -Path "$($Env:GITHUB_WORKSPACE)" -include "*.clz", "*.cpz", "*.cplz", "*.dll", "*.nuspec" | ForEach-Object { $allowed = $true; # Exclude any files in submodules foreach ($exclude in $exclusions) { diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 315fbd67..0af60e3d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -48,6 +48,8 @@ jobs: run: | Write-Output ${{ env.VERSION }} ./.github/scripts/UpdateAssemblyVersion.ps1 ${{ env.VERSION }} + - name: restore Nuget Packages + run: nuget install .\packages.config -OutputDirectory .\packages -ExcludeVersion # Login to Docker - name: Login to Docker uses: azure/docker-login@v1 @@ -107,6 +109,49 @@ jobs: asset_content_type: application/zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + Push_Nuget_Package: + needs: Build_Project + runs-on: windows-latest + steps: + - name: Download Build Version Info + uses: actions/download-artifact@v1 + with: + name: Version + - name: Set Version Number + shell: powershell + run: | + Get-ChildItem "./Version" + $version = Get-Content -Path ./Version/version.txt + Write-Host "Version: $version" + Write-Output "::set-env name=VERSION::$version" + Remove-Item -Path ./Version/version.txt + Remove-Item -Path ./Version + - name: Download Build output + uses: actions/download-artifact@v1 + with: + name: Build + path: ./ + - name: Unzip Build file + run: | + Get-ChildItem .\*.zip | Expand-Archive -DestinationPath .\ + Remove-Item -Path .\*.zip + - name: Copy Files to root & delete output directory + run: | + Remove-Item -Path .\* -Include @("*.cpz","*.md","*.cplz","*.json","*.dll","*.clz") + Get-ChildItem -Path .\output\* | Copy-Item -Destination .\ + Remove-Item -Path .\output -Recurse + - name: Add nuget.exe + uses: nuget/setup-nuget@v1 + - name: Add Github Packages source + run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }} + - name: Add nuget.org API Key + run: nuget setApiKey ${{ secrets.NUGET_API_KEY }} + - name: Create nuget package + run: nuget pack "./PepperDash_Essentials_Core.nuspec" -version ${{ env.VERSION }} + - name: Publish nuget package to Github registry + run: nuget push **/*.nupkg -source github + - name: Publish nuget package to nuget.org + run: nuget push **/*.nupkg -Source https://api.nuget.org/v3/index.json # This step always runs and pushes the build to the internal build rep Internal_Push_Output: needs: Build_Project diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7bbbd646..1ee70fd3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,6 +44,8 @@ jobs: run: | Write-Output ${{ env.VERSION }} ./.github/scripts/UpdateAssemblyVersion.ps1 ${{ env.VERSION }} + - name: restore Nuget Packages + run: nuget install .\packages.config -OutputDirectory .\packages -ExcludeVersion # Login to Docker - name: Login to Docker uses: azure/docker-login@v1 @@ -85,6 +87,49 @@ jobs: asset_content_type: application/zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + Push_Nuget_Package: + needs: Build_Project + runs-on: windows-latest + steps: + - name: Download Build Version Info + uses: actions/download-artifact@v1 + with: + name: Version + - name: Set Version Number + shell: powershell + run: | + Get-ChildItem "./Version" + $version = Get-Content -Path ./Version/version.txt + Write-Host "Version: $version" + Write-Output "::set-env name=VERSION::$version" + Remove-Item -Path ./Version/version.txt + Remove-Item -Path ./Version + - name: Download Build output + uses: actions/download-artifact@v1 + with: + name: Build + path: ./ + - name: Unzip Build file + run: | + Get-ChildItem .\*.zip | Expand-Archive -DestinationPath .\ + Remove-Item -Path .\*.zip + - name: Copy Files to root & delete output directory + run: | + Remove-Item -Path .\* -Include @("*.cpz","*.md","*.cplz","*.json","*.dll","*.clz") + Get-ChildItem -Path .\output\* | Copy-Item -Destination .\ + Remove-Item -Path .\output -Recurse + - name: Add nuget.exe + uses: nuget/setup-nuget@v1 + - name: Add Github Packages source + run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }} + - name: Add nuget.org API Key + run: nuget setApiKey ${{ secrets.NUGET_API_KEY }} + - name: Create nuget package + run: nuget pack "./PepperDash_Essentials_Core.nuspec" -version ${{ env.VERSION }} + - name: Publish nuget package to Github registry + run: nuget push **/*.nupkg -source github + - name: Publish nuget package to nuget.org + run: nuget push **/*.nupkg -Source https://api.nuget.org/v3/index.json Internal_Push_Output: needs: Build_Project runs-on: windows-latest diff --git a/.gitignore b/.gitignore index 5ba628b2..501c2f26 100644 --- a/.gitignore +++ b/.gitignore @@ -23,5 +23,368 @@ SIMPLSharpLogs/ *.projectinfo essentials-framework/EssentialDMTestConfig/ output/ +packages/ PepperDashEssentials-0.0.0-buildType-test.zip +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 57cd454e..fb10a055 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -91,6 +91,9 @@ namespace PepperDash.Essentials }, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(DeviceManager.GetRoutingPorts, + "getroutingports", "Reports all routing ports, if any. Requires a device key", ConsoleAccessLevelEnum.AccessOperator); + if (!Debug.DoNotLoadOnNextBoot) { GoWithLoad(); diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 4b0ef2b2..4159212c 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -2,340 +2,340 @@ using System.Linq; using Crestron.SimplSharp; using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.Fusion; - - +using Crestron.SimplSharpPro.Fusion; + + using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Fusion; -namespace PepperDash.Essentials.Fusion -{ - public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase - { - BooleanSigData CodecIsInCall; - - public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) - : base(room, ipId) - { - - } - - /// - /// Called in base class constructor before RVI and GUID files are built - /// - protected override void ExecuteCustomSteps() - { - SetUpCodec(); - } - - /// - /// Creates a static asset for the codec and maps the joins to the main room symbol - /// - void SetUpCodec() - { - try - { - var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; - - if (codec == null) - { - Debug.Console(1, this, "Cannot link codec to Fusion because codec is null"); - return; - } - - codec.UsageTracker = new UsageTracking(codec); - codec.UsageTracker.UsageIsTracked = true; - codec.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; - - var codecPowerOnAction = new Action(b => { if (!b) codec.StandbyDeactivate(); }); - var codecPowerOffAction = new Action(b => { if (!b) codec.StandbyActivate(); }); - - // Map FusionRoom Attributes: - - // Codec volume - var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); - codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); - (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); - - // In Call Status - CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); - codec.CallStatusChange += new EventHandler(codec_CallStatusChange); - - // Online status - if (codec is ICommunicationMonitor) - { - var c = codec as ICommunicationMonitor; - var codecOnline = FusionRoom.CreateOffsetBoolSig(122, "Online - VC 1", eSigIoMask.InputSigOnly); - codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk; - c.CommunicationMonitor.StatusChange += (o, a) => - { - codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; - }; - Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, "Online - VC 1"); - } - - // Codec IP Address - bool codecHasIpInfo = false; - var codecComm = codec.Communication; - - string codecIpAddress = string.Empty; - int codecIpPort = 0; - - StringSigData codecIpAddressSig; - StringSigData codecIpPortSig; - - if(codecComm is GenericSshClient) - { - codecIpAddress = (codecComm as GenericSshClient).Hostname; - codecIpPort = (codecComm as GenericSshClient).Port; - codecHasIpInfo = true; - } - else if (codecComm is GenericTcpIpClient) - { - codecIpAddress = (codecComm as GenericTcpIpClient).Hostname; - codecIpPort = (codecComm as GenericTcpIpClient).Port; - codecHasIpInfo = true; - } - - if (codecHasIpInfo) - { - codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly); - codecIpAddressSig.InputSig.StringValue = codecIpAddress; - - codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly); - codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); - } - - var tempAsset = new FusionAsset(); - - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(c => c.Key.Equals(codec.Key)); - - if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) - { - tempAsset = FusionStaticAssets[deviceConfig.Uid]; - } - else - { - // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), codec.Name, "Codec", ""); - FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); - } - - var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); - codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction; - codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction; - codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig); - - // TODO: Map relevant attributes on asset symbol - - codecAsset.TrySetMakeModel(codec); - codecAsset.TryLinkAssetErrorToCommunication(codec); - } - catch (Exception e) - { - Debug.Console(1, this, "Error setting up codec in Fusion: {0}", e); - } - } - - void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e) - { - var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; - - CodecIsInCall.InputSig.BoolValue = codec.IsInCall; - } - - // These methods are overridden because they access the room class which is of a different type - - protected override void CreateSymbolAndBasicSigs(uint ipId) - { - Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); - - FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); - FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); - - FusionRoom.Register(); - - FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += FusionRoomSchedule_DeviceExtenderSigChange; - FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; - FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; - - CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); - - // Room to fusion room - Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); - - // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly); - // Don't think we need to get current status of this as nothing should be alive yet. - (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; - - - FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey)); - // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); - - - CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; - } - - protected override void SetUpSources() - { - // Sources - var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).SourceListKey); - if (dict != null) - { - // NEW PROCESS: - // Make these lists and insert the fusion attributes by iterating these - var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); - uint i = 1; - foreach (var kvp in setTopBoxes) - { - TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - break; - } - - var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); - i = 1; - foreach (var kvp in discPlayers) - { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - break; - } - - var laptops = dict.Where(d => d.Value.SourceDevice is Core.Devices.Laptop); - i = 1; - foreach (var kvp in laptops) - { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 10) // We only have ten spots??? - break; - } - - foreach (var kvp in dict) - { - var usageDevice = kvp.Value.SourceDevice as IUsageTracking; - - if (usageDevice != null) - { - usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); - usageDevice.UsageTracker.UsageIsTracked = true; - usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); - } - } - - } - else - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", - (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key); - } - } - - protected override void SetUpDisplay() - { - try - { - //Setup Display Usage Monitoring - - var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); - - // Consider updating this in multiple display systems - - foreach (DisplayBase display in displays) - { - display.UsageTracker = new UsageTracking(display); - display.UsageTracker.UsageIsTracked = true; - display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); - } - - var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase; - if (defaultDisplay == null) - { - Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); - return; - } - - var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); - var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); - - // Display to fusion room sigs - FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; - FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); - if (defaultDisplay is IDisplayUsage) - (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); - - - - MapDisplayToRoomJoins(1, 158, defaultDisplay); - - - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); - - //Check for existing asset in GUIDs collection - - var tempAsset = new FusionAsset(); - - if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) - { - tempAsset = FusionStaticAssets[deviceConfig.Uid]; - } - else - { - // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); - FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); - } - - var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); - dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; - dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); - // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); - // Use extension methods - dispAsset.TrySetMakeModel(defaultDisplay); - dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); - } - catch (Exception e) - { - Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); - } - - } - - protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) - { - string displayName = string.Format("Display {0} - ", displayIndex); - - - if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay) - { - // Power on - var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Power Off - var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Current Source - var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); - defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey); }); ; - } - } - } +namespace PepperDash.Essentials.Fusion +{ + public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase + { + BooleanSigData CodecIsInCall; + + public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) + : base(room, ipId) + { + + } + + /// + /// Called in base class constructor before RVI and GUID files are built + /// + protected override void ExecuteCustomSteps() + { + SetUpCodec(); + } + + /// + /// Creates a static asset for the codec and maps the joins to the main room symbol + /// + void SetUpCodec() + { + try + { + var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; + + if (codec == null) + { + Debug.Console(1, this, "Cannot link codec to Fusion because codec is null"); + return; + } + + codec.UsageTracker = new UsageTracking(codec); + codec.UsageTracker.UsageIsTracked = true; + codec.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; + + var codecPowerOnAction = new Action(b => { if (!b) codec.StandbyDeactivate(); }); + var codecPowerOffAction = new Action(b => { if (!b) codec.StandbyActivate(); }); + + // Map FusionRoom Attributes: + + // Codec volume + var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); + codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); + (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); + + // In Call Status + CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); + codec.CallStatusChange += new EventHandler(codec_CallStatusChange); + + // Online status + if (codec is ICommunicationMonitor) + { + var c = codec as ICommunicationMonitor; + var codecOnline = FusionRoom.CreateOffsetBoolSig(122, "Online - VC 1", eSigIoMask.InputSigOnly); + codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk; + c.CommunicationMonitor.StatusChange += (o, a) => + { + codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; + }; + Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, "Online - VC 1"); + } + + // Codec IP Address + bool codecHasIpInfo = false; + var codecComm = codec.Communication; + + string codecIpAddress = string.Empty; + int codecIpPort = 0; + + StringSigData codecIpAddressSig; + StringSigData codecIpPortSig; + + if(codecComm is GenericSshClient) + { + codecIpAddress = (codecComm as GenericSshClient).Hostname; + codecIpPort = (codecComm as GenericSshClient).Port; + codecHasIpInfo = true; + } + else if (codecComm is GenericTcpIpClient) + { + codecIpAddress = (codecComm as GenericTcpIpClient).Hostname; + codecIpPort = (codecComm as GenericTcpIpClient).Port; + codecHasIpInfo = true; + } + + if (codecHasIpInfo) + { + codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly); + codecIpAddressSig.InputSig.StringValue = codecIpAddress; + + codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly); + codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); + } + + var tempAsset = new FusionAsset(); + + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(c => c.Key.Equals(codec.Key)); + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), codec.Name, "Codec", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction; + codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction; + codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig); + + // TODO: Map relevant attributes on asset symbol + + codecAsset.TrySetMakeModel(codec); + codecAsset.TryLinkAssetErrorToCommunication(codec); + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up codec in Fusion: {0}", e); + } + } + + void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e) + { + var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; + + CodecIsInCall.InputSig.BoolValue = codec.IsInCall; + } + + // These methods are overridden because they access the room class which is of a different type + + protected override void CreateSymbolAndBasicSigs(uint ipId) + { + Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + + FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); + FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); + + FusionRoom.Register(); + + FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += FusionRoomSchedule_DeviceExtenderSigChange; + FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; + FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; + + CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); + + // Room to fusion room + Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); + + // Moved to + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly); + // Don't think we need to get current status of this as nothing should be alive yet. + (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; + + + FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); + FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey)); + // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); + + + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; + } + + protected override void SetUpSources() + { + // Sources + var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).SourceListKey); + if (dict != null) + { + // NEW PROCESS: + // Make these lists and insert the fusion attributes by iterating these + var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); + uint i = 1; + foreach (var kvp in setTopBoxes) + { + TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + break; + } + + var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); + i = 1; + foreach (var kvp in discPlayers) + { + TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + break; + } + + var laptops = dict.Where(d => d.Value.SourceDevice is Core.Devices.Laptop); + i = 1; + foreach (var kvp in laptops) + { + TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 10) // We only have ten spots??? + break; + } + + foreach (var kvp in dict) + { + var usageDevice = kvp.Value.SourceDevice as IUsageTracking; + + if (usageDevice != null) + { + usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); + usageDevice.UsageTracker.UsageIsTracked = true; + usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + } + } + + } + else + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", + (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key); + } + } + + protected override void SetUpDisplay() + { + try + { + //Setup Display Usage Monitoring + + var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); + + // Consider updating this in multiple display systems + + foreach (DisplayBase display in displays) + { + display.UsageTracker = new UsageTracking(display); + display.UsageTracker.UsageIsTracked = true; + display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + } + + var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase; + if (defaultDisplay == null) + { + Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); + return; + } + + var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); + var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); + + // Display to fusion room sigs + FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; + FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); + if (defaultDisplay is IDisplayUsage) + (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + + + + MapDisplayToRoomJoins(1, 158, defaultDisplay); + + + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); + + //Check for existing asset in GUIDs collection + + var tempAsset = new FusionAsset(); + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; + dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); + // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); + // Use extension methods + dispAsset.TrySetMakeModel(defaultDisplay); + dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); + } + + } + + protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) + { + string displayName = string.Format("Display {0} - ", displayIndex); + + + if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay) + { + // Power on + var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Power Off + var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Current Source + var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); + defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey); }); ; + } + } + } } \ No newline at end of file diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index c522b52e..d8fd20e7 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -48,32 +48,38 @@ False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + ..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + ..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll + ..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll + ..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll + ..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + ..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + True - + False - ..\essentials-framework\pepperdashcore-builds\PepperDash_Core.dll + ..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll False @@ -81,27 +87,33 @@ False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + ..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + ..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll + ..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll + True @@ -155,6 +167,7 @@ + diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index b34e923b..dfaa333d 100644 --- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -156,7 +156,10 @@ namespace PepperDash.Essentials.Room.Config public EssentialsEnvironmentPropertiesConfig Environment { get; set; } [JsonProperty("logo")] - public EssentialsLogoPropertiesConfig Logo { get; set; } + public EssentialsLogoPropertiesConfig LogoLight { get; set; } + + [JsonProperty("logoDark")] + public EssentialsLogoPropertiesConfig LogoDark { get; set; } [JsonProperty("microphonePrivacy")] public EssentialsRoomMicrophonePrivacyConfig MicrophonePrivacy { get; set; } @@ -181,6 +184,12 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")] public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; } + + public EssentialsRoomPropertiesConfig() + { + LogoLight = new EssentialsLogoPropertiesConfig(); + LogoDark = new EssentialsLogoPropertiesConfig(); + } } public class EssentialsAvRoomPropertiesConfig : EssentialsRoomPropertiesConfig @@ -280,7 +289,7 @@ namespace PepperDash.Essentials.Room.Config /// /// Gets either the custom URL, a local-to-processor URL, or null if it's a default logo /// - public string GetUrl() + public string GetLogoUrlLight() { if (Type == "url") return Url; @@ -289,6 +298,16 @@ namespace PepperDash.Essentials.Room.Config CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)); return null; } + + public string GetLogoUrlDark() + { + if (Type == "url") + return Url; + if (Type == "system") + return string.Format("http://{0}:8080/logo-dark.png", + CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)); + return null; + } } /// diff --git a/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs b/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs index 8113174a..34432269 100644 --- a/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs @@ -327,7 +327,8 @@ namespace PepperDash.Essentials this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); - this.LogoUrl = PropertiesConfig.Logo.GetUrl(); + this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); + this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); this.SourceListKey = PropertiesConfig.SourceListKey; this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs index 03f3afc7..f0604308 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs @@ -250,7 +250,8 @@ namespace PepperDash.Essentials this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); - this.LogoUrl = PropertiesConfig.Logo.GetUrl(); + this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); + this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); this.SourceListKey = PropertiesConfig.SourceListKey; this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index b7ce3030..ca137a27 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -50,6 +50,19 @@ namespace PepperDash.Essentials //************************ + public override string SourceListKey + { + get + { + return _SourceListKey; + } + set + { + _SourceListKey = value; + SetCodecExternalSources(); + + } + } protected override Func OnFeedbackFunc { @@ -206,9 +219,11 @@ namespace PepperDash.Essentials VideoCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.VideoCodecKey) as PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase; + + if (VideoCodec == null) throw new ArgumentNullException("codec cannot be null"); - + AudioCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.AudioCodecKey) as PepperDash.Essentials.Devices.Common.AudioCodec.AudioCodecBase; if (AudioCodec == null) @@ -298,6 +313,7 @@ namespace PepperDash.Essentials VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); + VideoCodec.IsReadyChange += (o, a) => this.SetCodecExternalSources(); if (AudioCodec != null) AudioCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); @@ -340,15 +356,16 @@ namespace PepperDash.Essentials IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); } - this.LogoUrl = PropertiesConfig.Logo.GetUrl(); + this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); + this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); + this.SourceListKey = PropertiesConfig.SourceListKey; this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); - + return base.CustomActivate(); } - /// /// /// @@ -670,6 +687,42 @@ namespace PepperDash.Essentials (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); } + + /// + /// Setup the external sources for the Cisco Touch 10 devices that support IHasExternalSourceSwitch + /// + private void SetCodecExternalSources() + { + var videoCodecWithExternalSwitching = VideoCodec as IHasExternalSourceSwitching; + + if (videoCodecWithExternalSwitching == null) + { + return; + } + else + { + string codecTieLine = ""; + codecTieLine = ConfigReader.ConfigObject.TieLines.SingleOrDefault(x => x.DestinationKey == VideoCodec.Key).DestinationPort; + videoCodecWithExternalSwitching.ClearExternalSources(); + videoCodecWithExternalSwitching.RunRouteAction = RunRouteAction; + var srcList = ConfigReader.ConfigObject.SourceLists.SingleOrDefault(x => x.Key == SourceListKey).Value.OrderBy(kv => kv.Value.Order); ; + + foreach (var kvp in srcList) + { + var srcConfig = kvp.Value; + + if (kvp.Key != DefaultCodecRouteString && kvp.Key != "roomOff") + { + + videoCodecWithExternalSwitching.AddExternalSource(codecTieLine, kvp.Key, srcConfig.PreferredName, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceType.desktop); + videoCodecWithExternalSwitching.SetExternalSourceState(kvp.Key, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceMode.Ready); + + + } + } + } + } + #region IPrivacy Members diff --git a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs index eade64bd..65039872 100644 --- a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs +++ b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs @@ -224,6 +224,8 @@ namespace PepperDash.Essentials var room = DeviceManager.GetDeviceForKey(props.DefaultRoomKey); if (room is EssentialsHuddleSpaceRoom) { + // Screen Saver Driver + mainDriver.ScreenSaverController = new ScreenSaverController(mainDriver, props); // Header Driver Debug.Console(0, panelController, "Adding header driver"); @@ -272,6 +274,9 @@ namespace PepperDash.Essentials { Debug.Console(0, panelController, "Adding huddle space VTC AV driver"); + // Screen Saver Driver + mainDriver.ScreenSaverController = new ScreenSaverController(mainDriver, props); + // Header Driver mainDriver.HeaderDriver = new EssentialsHeaderDriver(mainDriver, props); diff --git a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs index f5a16578..d307d088 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs @@ -1,490 +1,490 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.DeviceSupport; - -namespace PepperDash.Essentials -{ - /// - /// Where all UI element common joins are defined - /// - public class UIBoolJoin - { - /// - /// 901 - /// - public const uint VolumeUpPress = 901; - /// - /// 902 - /// - public const uint VolumeDownPress = 902; - - //**************************************************** - // Codec General - - /// - /// 1001 - /// - public const uint CallEndPress = 1001; - /// - /// 1002 - /// - public const uint CallEndAllConfirmPress = 1002; - /// - /// 1003 - For tapping the text field to reveal the keyboard - /// - public const uint CodecDirectorySearchTextPress = 1003; - /// - /// 1004 - /// - public const uint CallStopSharingPress = 1004; - /// - /// 1005 - /// - public const uint CallSharedSourceInfoVisible = 1005; - /// - /// 1006 - /// - public const uint CallEndAllConfirmVisible = 1006; - - - - // Audio Conference - /// - /// 1101 - /// - public const uint ACKeypadVisible = 1101; - /// - /// 1102 - /// - public const uint ACStagingPopoverVisible = 1102; - /// - /// 1111 - /// - public const uint ACSpeedDial1Press = 1111; - /// - /// 1112 - /// - public const uint ACSpeedDial2Press = 1112; - /// - /// 1113 - /// - public const uint ACSpeedDial3Press = 1113; - /// - /// 1114 - /// - public const uint ACSpeedDial4Press = 1114; - /// - /// 1121 - /// - public const uint ACSpeedDial1Visible = 1121; - /// - /// 1122 - /// - public const uint ACSpeedDial2Visible = 1122; - /// - /// 1123 - /// - public const uint ACSpeedDial3Visible = 1123; - /// - /// 1124 - /// - public const uint ACSpeedDial4Visible = 1124; - - //****************************************************** - // Video Conference - /// - /// 1201 - /// - public const uint VCKeypadWithFavoritesVisible = 1201; - /// - /// 1202 - /// - public const uint VCStagingInactivePopoverVisible = 1202; - /// - /// - /// - public const uint VCStagingActivePopoverVisible = 1203; - /// - /// - /// - public const uint VCKeypadVisible = 1204; - /// - /// 1205 - /// - public const uint VCDirectoryVisible = 1205; - /// - /// 1206 - /// - public const uint VCRecentsVisible = 1206; - /// - /// 1207 - /// - public const uint VCCameraVisible = 1207; - /// - /// 1208 - /// - public const uint VCSelfViewLayoutVisible = 1208; - /// - /// 1211 - 1215 - /// - public const uint VCFavoritePressStart = 1211; - // RANGE IN USE - public const uint VCFavoritePressEnd = 1215; - /// - /// 1221 - 1225 - /// - public const uint VCFavoriteVisibleStart = 1221; - // RANGE IN USE - public const uint VCFavoriteVisibleEnd = 1225; - /// - /// 1231 - /// - public const uint VCStagingRecentsPress = 1231; - /// - /// 1232 - /// - public const uint VCStagingDirectoryPress = 1232; - /// - /// 1233 - /// - public const uint VCStagingKeypadPress = 1233; - /// - /// 1234 - /// - public const uint VCStagingConnectPress = 1234; - /// - /// 1235 - /// - public const uint VCStagingCameraPress = 1235; - /// - /// 1236 - /// - public const uint VCStagingConnectEnable = 1236; - /// - /// 1237 - When the user touches the text field, should trigger keyboard - /// - public const uint VCKeypadTextPress = 1237; - /// - /// 1238 - /// - public const uint VCKeypadBackspacePress = 1238; - /// - /// 1239 - /// - public const uint VCKeypadBackspaceVisible = 1239; - /// - /// 1240 - /// - public const uint VCDirectoryBackPress = 1240; - /// - /// 1241 For touching the text area to bring up keyboard - /// - public const uint VCDirectorySearchTextPress = 1241; - /// - /// 1242 - /// - public const uint VCStagingSelfViewLayoutPress = 1242; - /// - /// 1243 - /// - public const uint VCDirectoryBackVisible = 1243; - /// - /// 1244 - /// - public const uint VCDirectoryBackspacePress = 1244; - /// - /// 1245 - /// - public const uint VCDirectoryBackspaceVisible = 1245; - /// - /// 1251 - /// - public const uint VCSelfViewTogglePress = 1251; - /// - /// 1252 - /// - public const uint VCLayoutTogglePress = 1252; - /// - /// 1253 - /// - public const uint VCSelfViewPipTogglePress = 1253; - /// - /// 1254 - /// - public const uint VCLayoutToggleEnable = 1254; - /// - /// 1255 - /// - public const uint VCMinMaxPress = 1255; - /// - /// 1256 - /// - public const uint VCMinMaxEnable = 1256; - - // Letter joins start at 2921; - - //****************************************************** - - // Environment Joins - - // Popup Container - - /// - /// 2001 - 2004 - /// - public const uint EnvironmentBackgroundSubpageVisibleBase = 2000; - - - // ColumnOne - - /// - /// 2011 - 2015 - /// - public const uint EnvironmentColumnOneLightingTypeVisibleBase = 2010; - - /// - /// 2016 - 2020 - /// - public const uint EnvironmentColumnOneShadingTypeVisibleBase = 2015; - - // ColumnTwo - - /// - /// 2021 - 2025 - /// - public const uint EnvironmentColumnTwoLightingTypeVisibleBase = 2020; - - /// - /// 2026 - 2030 - /// - public const uint EnvironmentColumnTwoShadingTypeVisibleBase = 2025; - - // ColumnThree - - /// - /// 2031 - 2035 - /// - public const uint EnvironmentColumnThreeLightingTypeVisibleBase = 2030; - - /// - /// 2036 - 2040 - /// - public const uint EnvironmentColumnThreeShadingTypeVisibleBase = 2035; - - // ColumnFour - - /// - /// 2041 - 2045 - /// - public const uint EnvironmentColumnFourLightingTypeVisibleBase = 2040; - - /// - /// 2046 - 2050 - /// - public const uint EnvironmentColumnFourShadingTypeVisibleBase = 2045; - - // Button press - - /// - /// 2051 - 2060 - /// - public const uint EnvironmentColumnOneButtonPressBase = 2050; - - /// - /// 2061 - 2070 - /// - public const uint EnvironmentColumnTwoButtonPressBase = 2060; - - /// - /// 2071 - 2080 - /// - public const uint EnvironmentColumnThreeButtonPressBase = 2070; - - /// - /// 2081 - 2090 - /// - public const uint EnvironmentColumnFourButtonPressBase = 2080; - - // Button visibility - - /// - /// 2151 - 2160 - /// - public const uint EnvironmentColumnOneButtonVisibleBase = 2150; - - /// - /// 2161 - 2170 - /// - public const uint EnvironmentColumnTwoButtonVisibleBase = 2160; - - /// - /// 2171 - 2180 - /// - public const uint EnvironmentColumnThreeButtonVisibleBase = 2170; - - /// - /// 2181 - 2190 - /// - public const uint EnvironmentColumnFourButtonVisibleBase = 2180; - - - //****************************************************** - - /// - /// 3101 - /// - public const uint TechExitButton = 3101; - /// - /// 3106 - /// - public const uint TechCommonItemsVisbible = 3106; - /// - /// 3107 - /// - public const uint TechSystemStatusVisible = 3107; - /// - /// 3108 - /// - public const uint TechDisplayControlsVisible = 3108; - /// - /// 3109 - /// - public const uint TechPanelSetupVisible = 3109; - /// - /// 3110 - /// - public const uint TechAdvancedVolumeVisible = 3110; - /// - /// 3111 - /// - public const uint TechAboutVisible = 3111; - /// - /// 3112 - /// - public const uint TechSchedulerVisible = 3112; - - //***************************************************** - /// - /// 3811 - /// - public const uint VolumeSingleMute1Visible = 3811; - /// - /// 3812 - /// - public const uint VolumeSlider1Press = 3812; - /// - /// 3813 - /// - public const uint Volume1ProgramMutePressAndFB = 3813; - /// - /// 3821 - /// - public const uint Volume2Visible = 3821; - /// - /// 3822 - /// - public const uint VolumeSlider2Press = 3822; - /// - /// 3823 - /// - public const uint Volume2MutePressAndFB = 3823; - /// - /// 3831 - /// - public const uint Volume3Visible = 3831; - /// - /// 3832 - /// - public const uint VolumeSlider3Press = 3832; - /// - /// 3833 - /// - public const uint Volume3MutePressAndFB = 3833; - /// - /// 3841 - /// - public const uint Volume4Visible = 3841; - /// - /// 3842 - /// - public const uint VolumeSlider4Press = 3842; - /// - /// 3843 - /// - public const uint Volume4MutePressAndFB = 3843; - /// - /// 3851 - /// - public const uint Volume5Visible = 3851; - /// - /// 3852 - /// - public const uint VolumeSlider5Press = 3852; - /// - /// 3853 - /// - public const uint Volume5MutePressAndFB = 3853; - /// - /// 3861 - /// - public const uint Volume6Visible = 3861; - /// - /// 3862 - /// - public const uint VolumeSlider6Press = 3862; - /// - /// 3863 - /// - public const uint Volume6MutePressAndFB = 3863; - - /// - /// 3869 - when the system is off and the gear is pressed - /// - public const uint VolumesPagePowerOffVisible = 3869; - /// - /// 3870 - /// - public const uint VolumesPageVisible = 3870; - /// - /// 3871 - /// - public const uint VolumeDualMute1Visible = 3871; - /// - /// 3874 - /// - public const uint Volume1SpeechMutePressAndFB = 3874; - /// - /// 3875 - /// - public const uint Volume1BackerVisibility = 3875; - /// - /// 3891 - /// - public const uint VolumeDefaultPress = 3891; - /// - /// 3951 - /// - /// 3952 - /// - public const uint HeaderIcon2Press = 3952; - /// - /// 3953 - /// - public const uint HeaderIcon3Press = 3953; - /// - /// 3954 - /// - public const uint HeaderIcon4Press = 3954; - /// - /// 3955 - /// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +namespace PepperDash.Essentials +{ + /// + /// Where all UI element common joins are defined + /// + public class UIBoolJoin + { + /// + /// 901 + /// + public const uint VolumeUpPress = 901; + /// + /// 902 + /// + public const uint VolumeDownPress = 902; + + //**************************************************** + // Codec General + + /// + /// 1001 + /// + public const uint CallEndPress = 1001; + /// + /// 1002 + /// + public const uint CallEndAllConfirmPress = 1002; + /// + /// 1003 - For tapping the text field to reveal the keyboard + /// + public const uint CodecDirectorySearchTextPress = 1003; + /// + /// 1004 + /// + public const uint CallStopSharingPress = 1004; + /// + /// 1005 + /// + public const uint CallSharedSourceInfoVisible = 1005; + /// + /// 1006 + /// + public const uint CallEndAllConfirmVisible = 1006; + + + + // Audio Conference + /// + /// 1101 + /// + public const uint ACKeypadVisible = 1101; + /// + /// 1102 + /// + public const uint ACStagingPopoverVisible = 1102; + /// + /// 1111 + /// + public const uint ACSpeedDial1Press = 1111; + /// + /// 1112 + /// + public const uint ACSpeedDial2Press = 1112; + /// + /// 1113 + /// + public const uint ACSpeedDial3Press = 1113; + /// + /// 1114 + /// + public const uint ACSpeedDial4Press = 1114; + /// + /// 1121 + /// + public const uint ACSpeedDial1Visible = 1121; + /// + /// 1122 + /// + public const uint ACSpeedDial2Visible = 1122; + /// + /// 1123 + /// + public const uint ACSpeedDial3Visible = 1123; + /// + /// 1124 + /// + public const uint ACSpeedDial4Visible = 1124; + + //****************************************************** + // Video Conference + /// + /// 1201 + /// + public const uint VCKeypadWithFavoritesVisible = 1201; + /// + /// 1202 + /// + public const uint VCStagingInactivePopoverVisible = 1202; + /// + /// + /// + public const uint VCStagingActivePopoverVisible = 1203; + /// + /// + /// + public const uint VCKeypadVisible = 1204; + /// + /// 1205 + /// + public const uint VCDirectoryVisible = 1205; + /// + /// 1206 + /// + public const uint VCRecentsVisible = 1206; + /// + /// 1207 + /// + public const uint VCCameraVisible = 1207; + /// + /// 1208 + /// + public const uint VCSelfViewLayoutVisible = 1208; + /// + /// 1211 - 1215 + /// + public const uint VCFavoritePressStart = 1211; + // RANGE IN USE + public const uint VCFavoritePressEnd = 1215; + /// + /// 1221 - 1225 + /// + public const uint VCFavoriteVisibleStart = 1221; + // RANGE IN USE + public const uint VCFavoriteVisibleEnd = 1225; + /// + /// 1231 + /// + public const uint VCStagingRecentsPress = 1231; + /// + /// 1232 + /// + public const uint VCStagingDirectoryPress = 1232; + /// + /// 1233 + /// + public const uint VCStagingKeypadPress = 1233; + /// + /// 1234 + /// + public const uint VCStagingConnectPress = 1234; + /// + /// 1235 + /// + public const uint VCStagingCameraPress = 1235; + /// + /// 1236 + /// + public const uint VCStagingConnectEnable = 1236; + /// + /// 1237 - When the user touches the text field, should trigger keyboard + /// + public const uint VCKeypadTextPress = 1237; + /// + /// 1238 + /// + public const uint VCKeypadBackspacePress = 1238; + /// + /// 1239 + /// + public const uint VCKeypadBackspaceVisible = 1239; + /// + /// 1240 + /// + public const uint VCDirectoryBackPress = 1240; + /// + /// 1241 For touching the text area to bring up keyboard + /// + public const uint VCDirectorySearchTextPress = 1241; + /// + /// 1242 + /// + public const uint VCStagingSelfViewLayoutPress = 1242; + /// + /// 1243 + /// + public const uint VCDirectoryBackVisible = 1243; + /// + /// 1244 + /// + public const uint VCDirectoryBackspacePress = 1244; + /// + /// 1245 + /// + public const uint VCDirectoryBackspaceVisible = 1245; + /// + /// 1251 + /// + public const uint VCSelfViewTogglePress = 1251; + /// + /// 1252 + /// + public const uint VCLayoutTogglePress = 1252; + /// + /// 1253 + /// + public const uint VCSelfViewPipTogglePress = 1253; + /// + /// 1254 + /// + public const uint VCLayoutToggleEnable = 1254; + /// + /// 1255 + /// + public const uint VCMinMaxPress = 1255; + /// + /// 1256 + /// + public const uint VCMinMaxEnable = 1256; + + // Letter joins start at 2921; + + //****************************************************** + + // Environment Joins + + // Popup Container + + /// + /// 2001 - 2004 + /// + public const uint EnvironmentBackgroundSubpageVisibleBase = 2000; + + + // ColumnOne + + /// + /// 2011 - 2015 + /// + public const uint EnvironmentColumnOneLightingTypeVisibleBase = 2010; + + /// + /// 2016 - 2020 + /// + public const uint EnvironmentColumnOneShadingTypeVisibleBase = 2015; + + // ColumnTwo + + /// + /// 2021 - 2025 + /// + public const uint EnvironmentColumnTwoLightingTypeVisibleBase = 2020; + + /// + /// 2026 - 2030 + /// + public const uint EnvironmentColumnTwoShadingTypeVisibleBase = 2025; + + // ColumnThree + + /// + /// 2031 - 2035 + /// + public const uint EnvironmentColumnThreeLightingTypeVisibleBase = 2030; + + /// + /// 2036 - 2040 + /// + public const uint EnvironmentColumnThreeShadingTypeVisibleBase = 2035; + + // ColumnFour + + /// + /// 2041 - 2045 + /// + public const uint EnvironmentColumnFourLightingTypeVisibleBase = 2040; + + /// + /// 2046 - 2050 + /// + public const uint EnvironmentColumnFourShadingTypeVisibleBase = 2045; + + // Button press + + /// + /// 2051 - 2060 + /// + public const uint EnvironmentColumnOneButtonPressBase = 2050; + + /// + /// 2061 - 2070 + /// + public const uint EnvironmentColumnTwoButtonPressBase = 2060; + + /// + /// 2071 - 2080 + /// + public const uint EnvironmentColumnThreeButtonPressBase = 2070; + + /// + /// 2081 - 2090 + /// + public const uint EnvironmentColumnFourButtonPressBase = 2080; + + // Button visibility + + /// + /// 2151 - 2160 + /// + public const uint EnvironmentColumnOneButtonVisibleBase = 2150; + + /// + /// 2161 - 2170 + /// + public const uint EnvironmentColumnTwoButtonVisibleBase = 2160; + + /// + /// 2171 - 2180 + /// + public const uint EnvironmentColumnThreeButtonVisibleBase = 2170; + + /// + /// 2181 - 2190 + /// + public const uint EnvironmentColumnFourButtonVisibleBase = 2180; + + + //****************************************************** + + /// + /// 3101 + /// + public const uint TechExitButton = 3101; + /// + /// 3106 + /// + public const uint TechCommonItemsVisbible = 3106; + /// + /// 3107 + /// + public const uint TechSystemStatusVisible = 3107; + /// + /// 3108 + /// + public const uint TechDisplayControlsVisible = 3108; + /// + /// 3109 + /// + public const uint TechPanelSetupVisible = 3109; + /// + /// 3110 + /// + public const uint TechAdvancedVolumeVisible = 3110; + /// + /// 3111 + /// + public const uint TechAboutVisible = 3111; + /// + /// 3112 + /// + public const uint TechSchedulerVisible = 3112; + + //***************************************************** + /// + /// 3811 + /// + public const uint VolumeSingleMute1Visible = 3811; + /// + /// 3812 + /// + public const uint VolumeSlider1Press = 3812; + /// + /// 3813 + /// + public const uint Volume1ProgramMutePressAndFB = 3813; + /// + /// 3821 + /// + public const uint Volume2Visible = 3821; + /// + /// 3822 + /// + public const uint VolumeSlider2Press = 3822; + /// + /// 3823 + /// + public const uint Volume2MutePressAndFB = 3823; + /// + /// 3831 + /// + public const uint Volume3Visible = 3831; + /// + /// 3832 + /// + public const uint VolumeSlider3Press = 3832; + /// + /// 3833 + /// + public const uint Volume3MutePressAndFB = 3833; + /// + /// 3841 + /// + public const uint Volume4Visible = 3841; + /// + /// 3842 + /// + public const uint VolumeSlider4Press = 3842; + /// + /// 3843 + /// + public const uint Volume4MutePressAndFB = 3843; + /// + /// 3851 + /// + public const uint Volume5Visible = 3851; + /// + /// 3852 + /// + public const uint VolumeSlider5Press = 3852; + /// + /// 3853 + /// + public const uint Volume5MutePressAndFB = 3853; + /// + /// 3861 + /// + public const uint Volume6Visible = 3861; + /// + /// 3862 + /// + public const uint VolumeSlider6Press = 3862; + /// + /// 3863 + /// + public const uint Volume6MutePressAndFB = 3863; + + /// + /// 3869 - when the system is off and the gear is pressed + /// + public const uint VolumesPagePowerOffVisible = 3869; + /// + /// 3870 + /// + public const uint VolumesPageVisible = 3870; + /// + /// 3871 + /// + public const uint VolumeDualMute1Visible = 3871; + /// + /// 3874 + /// + public const uint Volume1SpeechMutePressAndFB = 3874; + /// + /// 3875 + /// + public const uint Volume1BackerVisibility = 3875; + /// + /// 3891 + /// + public const uint VolumeDefaultPress = 3891; + /// + /// 3951 + /// + /// 3952 + /// + public const uint HeaderIcon2Press = 3952; + /// + /// 3953 + /// + public const uint HeaderIcon3Press = 3953; + /// + /// 3954 + /// + public const uint HeaderIcon4Press = 3954; + /// + /// 3955 + /// public const uint HeaderIcon5Press = 3955; /// 3960 @@ -509,329 +509,370 @@ namespace PepperDash.Essentials /// /// 3965 /// - public const uint HeaderCaret5Visible = 3965; - - /// - /// 3999 - /// - public const uint GenericModalVisible = 3999; - /// - /// 12345 - /// - public const uint AvNoControlsSubVisible = 12345; - - // 10000 - 14999 are general "source" pages - - /// - /// 15001 - /// - public const uint StartPageVisible = 15001; - /// - /// 15002 Shows the start page in the source controls area of the screen - /// - public const uint TapToBeginVisible = 15002; - /// - /// 15003 Message text when no source is showing - /// - public const uint SelectASourceVisible = 15003; - /// - /// 15004 - /// - public const uint RoomIsOn = 15004; - /// - /// 15005 Shows always-on volume control subpage with only audio mute - /// - public const uint VolumeControlsSingleMuteVisible = 15005; - /// - /// 15006 Shows always-on volume control subpage with mic and audio mutes - /// - public const uint VolumeControlsDualMuteVisible = 15006; - /// - /// 15010 - /// - public const uint ShowPanelSetupPress = 15010; - /// - /// 15011 - Top bar with room name and button that pops up dialog with room data - /// - public const uint TopBarHabaneroVisible = 15011; - /// - /// 15012 - /// - public const uint SourceStagingBarVisible = 15012; - /// - /// 15013 - /// - public const uint PowerOffStep1Visible = 15013; - /// - /// 15014 - /// - public const uint PowerOffStep2Visible = 15014; - /// - /// 15015 - /// - public const uint ShowPowerOffPress = 15015; - /// - /// 15016 - /// - public const uint PowerOffMorePress = 15016; - /// - /// 15017 - /// - public const uint StagingPageAdditionalArrowsVisible = 15017; - /// - /// 15018 The Header with dynamic buttons - /// - public const uint TopBarHabaneroDynamicVisible = 15018; - /// - /// 15019 Shown when system is starting and not ready for use - /// - public const uint SystemInitializingVisible = 15019; - /// - /// 15020 - /// - public const uint PanelSetupVisible = 15020; - /// - /// 15021 - /// - public const uint SourceWaitOverlayVisible = 15021; - /// - /// 15022 - /// - public const uint ActivityFooterVisible = 15022; - /// - /// 15024 - /// - public const uint HeaderCallStatusLeftPositionVisible = 15024; - /// - /// 15025 - /// - public const uint HeaderCallStatusRightPositionVisible = 15025; - /// - /// 15027 - /// - public const uint HeaderCallStatusLabelPress = 15027; - /// - /// 15028 The gear button in header - /// - public const uint FIXFIX_HeaderGearButtonPress_FIXFIX = 15028; - /// - /// 15029 the room button in header - /// - public const uint HeaderRoomButtonPress = 15029; - /// - /// 15030 Visibility for room data popup - /// - public const uint RoomHeaderPageVisible = 15030; - /// - /// 15031 - /// - public const uint AllRoomsOffPress = 15031; - /// - /// 15032 - /// - public const uint DisplayPowerTogglePress = 15032; - /// - /// 15033 - /// - public const uint PowerOffCancelPress = 15033; - /// - /// 15034 - /// - public const uint PowerOffConfirmPress = 15034; - /// - /// 15035 - /// - public const uint VolumeButtonPopupPress = 15035; - /// - /// 15035 - /// - public const uint VolumeButtonPopupVisible = 15035; - /// - /// 15036 - /// - public const uint VolumeGaugePopupVisible = 15036; - /// - /// 15037 - /// - public const uint GearButtonVisible = 15037; - /// - /// 15038 - /// - public const uint CalendarHeaderButtonVisible = 15038; - /// - /// 15039 - /// - public const uint CalendarHeaderButtonPress = 15039; - /// - /// 15040 - /// - public const uint CallStatusPageVisible = 15040; - /// - /// 15041 - /// - public const uint LightsPageVisible = 15041; - /// - /// 15042 Closes whichever interlocked modal is open - /// - public const uint InterlockedModalClosePress = 15042; - /// - /// 15043 Vis for modal backer for full-screen source - /// - public const uint SourceBackgroundOverlayVisible = 15043; - /// - /// 15044 Close button for source modal overlay - /// - public const uint SourceBackgroundOverlayClosePress = 15044; - /// - /// 15045 - Visibility for the bar containing call navigation button list - /// - public const uint CallStagingBarVisible = 15045; - /// - /// 15046 - /// - public const uint MeetingsOrContacMethodsListVisible = 15046; - /// - /// 15047 The "Join" button on the next meeting ribbon - /// - public const uint NextMeetingJoinPress = 15047; - /// - /// 15048 Dismisses the ribbon - /// - public const uint NextMeetingModalClosePress = 15048; - /// - /// 15049 - /// - public const uint NextMeetingModalVisible = 15049; - /// - /// 15051 - /// - public const uint Display1SelectPressAndFb = 15051; - /// - /// 15052 - /// - public const uint Display1ControlButtonEnable = 15052; - /// - /// 15053 - /// - public const uint Display1ControlButtonPress = 15053; - /// - /// 15054 - /// - public const uint Display1AudioButtonEnable = 15054; - /// - /// 15055 - /// - public const uint Display1AudioButtonPressAndFb = 15055; - /// - /// 15056 - /// - public const uint Display2SelectPressAndFb = 15056; - /// - /// 15057 - /// - public const uint Display2ControlButtonEnable = 15057; - /// - /// 15058 - /// - public const uint Display2ControlButtonPress = 15058; - /// - /// 15059 - /// - public const uint Display2AudioButtonEnable = 15059; - /// - /// 15060 - /// - public const uint Display2AudioButtonPressAndFb = 15060; - /// - /// 15061 Reveals the dual-display subpage - /// - public const uint DualDisplayPageVisible = 15061; - /// - /// 15062 Reveals the toggle switch for the sharing mode - /// - public const uint ToggleSharingModeVisible = 15062; - /// - /// 15063 Press for the toggle mode switch - /// - public const uint ToggleSharingModePress = 15063; - /// - /// 15064 - /// - public const uint LogoDefaultVisible = 15064; - /// - /// 15065 - /// - public const uint LogoUrlVisible = 15065; - /// - /// 15066 - Reveals the active calls header item - /// - public const uint HeaderActiveCallsListVisible = 15066; - /// - /// 15067 - /// - public const uint NotificationRibbonVisible = 15067; - /// - /// 15083 - Press for Call help desk on AC/VC - /// - public const uint HelpPageShowCallButtonPress = 15083; - /// - /// 15084 - Show the "call help desk" button on help page - /// - public const uint HelpPageShowCallButtonVisible = 15084; - /// - /// 15085 Visibility join for help subpage - /// - public const uint HelpPageVisible = 15085; - /// - /// 15086 Press for help header button - /// - public const uint HelpPress = 15086; - /// - /// 15088 - /// - public const uint DateOnlyVisible = 15088; - /// - /// 15089 - /// - public const uint TimeOnlyVisible = 15089; - /// - /// 15090 - /// - public const uint DateAndTimeVisible = 15090; - /// - /// 15091 - /// - public const uint SetupFullDistrib = 15091; - - // PIN dialogs ************************************ - - /// - /// 15201 - /// - public const uint PinDialog4DigitVisible = 15201; - /// - /// 15206 - /// - public const uint PinDialogCancelPress = 15206; - /// - /// 15207 - /// - public const uint PinDialogErrorVisible = 15207; - /// - /// 15211 - /// - public const uint PinDialogDot1 = 15211; - /// - /// 15212 - /// - public const uint PinDialogDot2 = 15212; - /// - /// 15213 - /// - public const uint PinDialogDot3 = 15213; - /// - /// 15214 - /// - public const uint PinDialogDot4 = 15214; - } + public const uint HeaderCaret5Visible = 3965; + + /// + /// 3999 + /// + public const uint GenericModalVisible = 3999; + /// + /// 12345 + /// + public const uint AvNoControlsSubVisible = 12345; + + // 10000 - 14999 are general "source" pages + + /// + /// 15001 + /// + public const uint StartPageVisible = 15001; + /// + /// 15002 Shows the start page in the source controls area of the screen + /// + public const uint TapToBeginVisible = 15002; + /// + /// 15003 Message text when no source is showing + /// + public const uint SelectASourceVisible = 15003; + /// + /// 15004 + /// + public const uint RoomIsOn = 15004; + /// + /// 15005 Shows always-on volume control subpage with only audio mute + /// + public const uint VolumeControlsSingleMuteVisible = 15005; + /// + /// 15006 Shows always-on volume control subpage with mic and audio mutes + /// + public const uint VolumeControlsDualMuteVisible = 15006; + /// + /// 15010 + /// + public const uint ShowPanelSetupPress = 15010; + /// + /// 15011 - Top bar with room name and button that pops up dialog with room data + /// + public const uint TopBarHabaneroVisible = 15011; + /// + /// 15012 + /// + public const uint SourceStagingBarVisible = 15012; + /// + /// 15013 + /// + public const uint PowerOffStep1Visible = 15013; + /// + /// 15014 + /// + public const uint PowerOffStep2Visible = 15014; + /// + /// 15015 + /// + public const uint ShowPowerOffPress = 15015; + /// + /// 15016 + /// + public const uint PowerOffMorePress = 15016; + /// + /// 15017 + /// + public const uint StagingPageAdditionalArrowsVisible = 15017; + /// + /// 15018 The Header with dynamic buttons + /// + public const uint TopBarHabaneroDynamicVisible = 15018; + /// + /// 15019 Shown when system is starting and not ready for use + /// + public const uint SystemInitializingVisible = 15019; + /// + /// 15020 + /// + public const uint PanelSetupVisible = 15020; + /// + /// 15021 + /// + public const uint SourceWaitOverlayVisible = 15021; + /// + /// 15022 + /// + public const uint ActivityFooterVisible = 15022; + /// + /// 15024 + /// + public const uint HeaderCallStatusLeftPositionVisible = 15024; + /// + /// 15025 + /// + public const uint HeaderCallStatusRightPositionVisible = 15025; + /// + /// 15027 + /// + public const uint HeaderCallStatusLabelPress = 15027; + /// + /// 15028 The gear button in header + /// + public const uint FIXFIX_HeaderGearButtonPress_FIXFIX = 15028; + /// + /// 15029 the room button in header + /// + public const uint HeaderRoomButtonPress = 15029; + /// + /// 15030 Visibility for room data popup + /// + public const uint RoomHeaderInfoPageVisible = 15030; + /// + /// 15031 + /// + public const uint AllRoomsOffPress = 15031; + /// + /// 15032 + /// + public const uint DisplayPowerTogglePress = 15032; + /// + /// 15033 + /// + public const uint PowerOffCancelPress = 15033; + /// + /// 15034 + /// + public const uint PowerOffConfirmPress = 15034; + /// + /// 15035 + /// + public const uint VolumeButtonPopupPress = 15035; + /// + /// 15035 + /// + public const uint VolumeButtonPopupVisible = 15035; + /// + /// 15036 + /// + public const uint VolumeGaugePopupVisible = 15036; + /// + /// 15037 + /// + public const uint GearButtonVisible = 15037; + /// + /// 15038 + /// + public const uint CalendarHeaderButtonVisible = 15038; + /// + /// 15039 + /// + public const uint CalendarHeaderButtonPress = 15039; + /// + /// 15040 + /// + public const uint CallStatusPageVisible = 15040; + /// + /// 15041 + /// + public const uint LightsPageVisible = 15041; + /// + /// 15042 Closes whichever interlocked modal is open + /// + public const uint InterlockedModalClosePress = 15042; + /// + /// 15043 Vis for modal backer for full-screen source + /// + public const uint SourceBackgroundOverlayVisible = 15043; + /// + /// 15044 Close button for source modal overlay + /// + public const uint SourceBackgroundOverlayClosePress = 15044; + /// + /// 15045 - Visibility for the bar containing call navigation button list + /// + public const uint CallStagingBarVisible = 15045; + /// + /// 15046 + /// + public const uint MeetingsOrContacMethodsListVisible = 15046; + /// + /// 15047 The "Join" button on the next meeting ribbon + /// + public const uint NextMeetingJoinPress = 15047; + /// + /// 15048 Dismisses the ribbon + /// + public const uint NextMeetingModalClosePress = 15048; + /// + /// 15049 + /// + public const uint NextMeetingModalVisible = 15049; + /// + /// 15051 + /// + public const uint Display1SelectPressAndFb = 15051; + /// + /// 15052 + /// + public const uint Display1ControlButtonEnable = 15052; + /// + /// 15053 + /// + public const uint Display1ControlButtonPress = 15053; + /// + /// 15054 + /// + public const uint Display1AudioButtonEnable = 15054; + /// + /// 15055 + /// + public const uint Display1AudioButtonPressAndFb = 15055; + /// + /// 15056 + /// + public const uint Display2SelectPressAndFb = 15056; + /// + /// 15057 + /// + public const uint Display2ControlButtonEnable = 15057; + /// + /// 15058 + /// + public const uint Display2ControlButtonPress = 15058; + /// + /// 15059 + /// + public const uint Display2AudioButtonEnable = 15059; + /// + /// 15060 + /// + public const uint Display2AudioButtonPressAndFb = 15060; + /// + /// 15061 Reveals the dual-display subpage + /// + public const uint DualDisplayPageVisible = 15061; + /// + /// 15062 Reveals the toggle switch for the sharing mode + /// + public const uint ToggleSharingModeVisible = 15062; + /// + /// 15063 Press for the toggle mode switch + /// + public const uint ToggleSharingModePress = 15063; + /// + /// 15064 + /// + public const uint LogoDefaultVisible = 15064; + /// + /// 15065 + /// + public const uint LogoUrlVisible = 15065; + /// + /// 15066 - Reveals the active calls header item + /// + public const uint HeaderActiveCallsListVisible = 15066; + /// + /// 15067 + /// + public const uint NotificationRibbonVisible = 15067; + /// + /// 15083 - Press for Call help desk on AC/VC + /// + public const uint HelpPageShowCallButtonPress = 15083; + /// + /// 15084 - Show the "call help desk" button on help page + /// + public const uint HelpPageShowCallButtonVisible = 15084; + /// + /// 15085 Visibility join for help subpage + /// + public const uint HelpPageVisible = 15085; + /// + /// 15086 Press for help header button + /// + public const uint HelpPress = 15086; + /// + /// 15088 + /// + public const uint DateOnlyVisible = 15088; + /// + /// 15089 + /// + public const uint TimeOnlyVisible = 15089; + /// + /// 15090 + /// + public const uint DateAndTimeVisible = 15090; + /// + /// 15091 + /// + public const uint SetupFullDistrib = 15091; + + /// + /// 15092 + /// + public const uint StartMCPageVisible = 15092; + + + /// + /// 15093 + /// + public const uint RoomHeaderInfoMCPageVisible = 15093; + + /// + /// 15094 + /// + public const uint MCScreenSaverVisible = 15094; + + /// + /// 15095 + /// + public const uint MCScreenSaverPosition1Visible = 15095; + + /// + /// 15096 + /// + public const uint MCScreenSaverPosition2Visible = 15096; + + /// + /// 15097 + /// + public const uint MCScreenSaverPosition3Visible = 15097; + + /// + /// 15098 + /// + public const uint MCScreenSaverPosition4Visible = 15098; + + /// + /// 15099 + /// + public const uint MCScreenSaverClosePress = 15099; + + // PIN dialogs ************************************ + + /// + /// 15201 + /// + public const uint PinDialog4DigitVisible = 15201; + /// + /// 15206 + /// + public const uint PinDialogCancelPress = 15206; + /// + /// 15207 + /// + public const uint PinDialogErrorVisible = 15207; + /// + /// 15211 + /// + public const uint PinDialogDot1 = 15211; + /// + /// 15212 + /// + public const uint PinDialogDot2 = 15212; + /// + /// 15213 + /// + public const uint PinDialogDot3 = 15213; + /// + /// 15214 + /// + public const uint PinDialogDot4 = 15214; + } } \ No newline at end of file diff --git a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs index b5f5a86b..ea60526b 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs @@ -146,6 +146,10 @@ namespace PepperDash.Essentials /// public const uint RoomPhoneText = 3904; /// + /// 3905 - Video address/number for room header + /// + public const uint RoomVideoAddressText = 3905; + /// /// 3906 - The separator for verbose-header text on addresses /// public const uint RoomAddressPipeText = 3906; @@ -154,6 +158,14 @@ namespace PepperDash.Essentials /// public const uint RoomUserCode = 3907; /// + /// 3908 - The url for the mobile control server + /// + public const uint RoomMcUrl = 3908; + /// + /// 3909 - The url for the mobile control QR Code image + /// + public const uint RoomMcQrCodeUrl = 3909; + /// /// 3911 /// public const uint PowerOffMessage = 3911; @@ -189,12 +201,19 @@ namespace PepperDash.Essentials /// /// 3923 /// - public const uint LogoUrl = 3923; + public const uint LogoUrlLightBkgnd = 3923; + + /// /// 3924 - the text on the "call help desk" button /// public const uint HelpPageCallButtonText = 3924; + /// + /// 3925 + /// + public const uint LogoUrlDarkBkgnd = 3925; + /// /// 3951 /// diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs index e32015fe..ab4ff3aa 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs @@ -1,4 +1,5 @@ using System; +using Crestron.SimplSharp; using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.UI; using PepperDash.Essentials.Core; @@ -11,6 +12,8 @@ namespace PepperDash.Essentials /// public class EssentialsPanelMainInterfaceDriver : PanelDriverBase { + CTimer InactivityTimer; + /// /// Assign the appropriate A/V driver. /// Want to keep the AvDriver alive, because it may hold states @@ -23,6 +26,10 @@ namespace PepperDash.Essentials public PanelDriverBase CurrentChildDriver { get; private set; } + public ScreenSaverController ScreenSaverController { get; set; } + + private readonly long _timeoutMs; + CrestronTouchpanelPropertiesConfig Config; /// @@ -35,18 +42,72 @@ namespace PepperDash.Essentials : base(trilist) { Config = config; + + _timeoutMs = Config.ScreenSaverTimeoutMin * 60 * 1000; + + var tsx52or60 = trilist as Tswx52ButtonVoiceControl; + + if (tsx52or60 != null) + { + tsx52or60.ExtenderTouchDetectionReservedSigs.Use(); + tsx52or60.ExtenderTouchDetectionReservedSigs.DeviceExtenderSigChange += ExtenderTouchDetectionReservedSigs_DeviceExtenderSigChange; + tsx52or60.ExtenderTouchDetectionReservedSigs.Time.UShortValue = 1; + ManageInactivityTimer(); + + } + else + { + var tswx70 = trilist as TswX70Base; + if (tswx70 != null) + { + tswx70.ExtenderTouchDetectionReservedSigs.Use(); + tswx70.ExtenderTouchDetectionReservedSigs.DeviceExtenderSigChange += ExtenderTouchDetectionReservedSigs_DeviceExtenderSigChange; + tswx70.ExtenderTouchDetectionReservedSigs.Time.UShortValue = 1; + ManageInactivityTimer(); + } + } } + void ExtenderTouchDetectionReservedSigs_DeviceExtenderSigChange(Crestron.SimplSharpPro.DeviceExtender currentDeviceExtender, Crestron.SimplSharpPro.SigEventArgs args) + { + + if (args.Sig.BoolValue) + { + ManageInactivityTimer(); + } + } + + private void ManageInactivityTimer() + { + if (InactivityTimer != null) + { + InactivityTimer.Reset(_timeoutMs); + } + else + { + InactivityTimer = new CTimer((o) => InactivityTimerExpired(), _timeoutMs); + } + } + + void InactivityTimerExpired() + { + InactivityTimer.Stop(); + InactivityTimer.Dispose(); + InactivityTimer = null; + + ScreenSaverController.Show(); + } + public override void Show() { CurrentChildDriver = null; ShowSubDriver(AvDriver as PanelDriverBase); - base.Show(); + base.Show(); } public override void Hide() { - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[AvDriver.StartPageVisibleJoin].BoolValue = false; base.Hide(); } diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs index edda29e0..4a8cb53c 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs @@ -267,7 +267,7 @@ // HideAndClearCurrentDisplayModeSigsInUse(); // tl[UIBoolJoin.TopBarHabaneroVisible].BoolValue = false; // tl[UIBoolJoin.ActivityFooterVisible].BoolValue = false; -// tl[UIBoolJoin.StartPageVisible].BoolValue = false; +// tl[StartPageVisibleJoin].BoolValue = false; // tl[UIBoolJoin.TapToBeginVisible].BoolValue = false; // tl[UIBoolJoin.ToggleSharingModeVisible].BoolValue = false; // tl[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; @@ -335,7 +335,7 @@ // } // else // { -// TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; +// TriList.BooleanInput[StartPageVisibleJoin].BoolValue = true; // TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = true; // TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; // } @@ -525,7 +525,7 @@ // if (!_CurrentRoom.OnFeedback.BoolValue) // { // ShareButtonSig.BoolValue = true; -// TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; +// TriList.BooleanInput[StartPageVisibleJoin].BoolValue = false; // ShowCurrentSharingMode(); // } // } @@ -953,13 +953,13 @@ // if (value) // { // SetupActivityFooterWhenRoomOn(); -// TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; +// TriList.BooleanInput[StartPageVisibleJoin].BoolValue = false; // } // else // { // HideCurrentSharingMode(); // SetupActivityFooterWhenRoomOff(); -// TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; +// TriList.BooleanInput[StartPageVisibleJoin].BoolValue = true; // if (LastSelectedSourceSig != null) // { // LastSelectedSourceSig.BoolValue = false; diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs index 232a99da..42b2cf5c 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs @@ -24,6 +24,9 @@ namespace PepperDash.Essentials PresentationMode, AudioSetup } + public uint StartPageVisibleJoin { get; private set; } + + /// /// Whether volume ramping from this panel will show the volume /// gauge popup. @@ -209,6 +212,7 @@ namespace PepperDash.Essentials "Tap Share to begin"; } + /// /// /// @@ -225,20 +229,34 @@ namespace PepperDash.Essentials if (Config.HeaderStyle.ToLower() == CrestronTouchpanelPropertiesConfig.Habanero) { TriList.SetSigFalseAction(UIBoolJoin.HeaderRoomButtonPress, () => - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderPageVisible)); + { + if (CurrentRoom.IsMobileControlEnabled) + { + Debug.Console(1, "Showing Mobile Control Header Info"); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderInfoMCPageVisible); + } + else + { + Debug.Console(1, "Showing Non Mobile Control Header Info"); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderInfoPageVisible); + } + }); } else if (Config.HeaderStyle.ToLower() == CrestronTouchpanelPropertiesConfig.Verbose) { - // room name on join 1, concat phone and sip on join 2, no button method - //var addr = roomConf.Addresses; - //if (addr == null) // protect from missing values by using default empties - // addr = new EssentialsRoomAddressPropertiesConfig(); - //// empty string when either missing, pipe when both showing - //TriList.SetString(UIStringJoin.RoomAddressPipeText, - // (string.IsNullOrEmpty(addr.PhoneNumber.Trim()) - // || string.IsNullOrEmpty(addr.SipAddress.Trim())) ? "" : " | "); - //TriList.SetString(UIStringJoin.RoomPhoneText, addr.PhoneNumber); - //TriList.SetString(UIStringJoin.RoomSipText, addr.SipAddress); + TriList.SetSigFalseAction(UIBoolJoin.HeaderRoomButtonPress, () => + { + if (CurrentRoom.IsMobileControlEnabled) + { + Debug.Console(1, "Showing Mobile Control Header Info"); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderInfoMCPageVisible); + } + else + { + Debug.Console(1, "Showing Non Mobile Control Header Info"); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderInfoPageVisible); + } + }); } TriList.SetBool(UIBoolJoin.DateAndTimeVisible, Config.ShowDate && Config.ShowTime); @@ -256,7 +274,7 @@ namespace PepperDash.Essentials } else { - TriList.SetBool(UIBoolJoin.StartPageVisible, true); + TriList.SetBool(StartPageVisibleJoin, true); TriList.SetBool(UIBoolJoin.TapToBeginVisible, true); SetupActivityFooterWhenRoomOff(); } @@ -321,7 +339,7 @@ namespace PepperDash.Essentials /// void ShowLogo() { - if (CurrentRoom.LogoUrl == null) + if (CurrentRoom.LogoUrlLightBkgnd == null) { TriList.SetBool(UIBoolJoin.LogoDefaultVisible, true); TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); @@ -330,7 +348,8 @@ namespace PepperDash.Essentials { TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); TriList.SetBool(UIBoolJoin.LogoUrlVisible, true); - TriList.SetString(UIStringJoin.LogoUrl, _CurrentRoom.LogoUrl); + TriList.SetString(UIStringJoin.LogoUrlLightBkgnd, _CurrentRoom.LogoUrlLightBkgnd); + TriList.SetString(UIStringJoin.LogoUrlDarkBkgnd, _CurrentRoom.LogoUrlDarkBkgnd); } } @@ -351,7 +370,7 @@ namespace PepperDash.Essentials HideAndClearCurrentDisplayModeSigsInUse(); TriList.BooleanInput[UIBoolJoin.TopBarHabaneroDynamicVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[StartPageVisibleJoin].BoolValue = false; TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; //TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = false; @@ -416,7 +435,7 @@ namespace PepperDash.Essentials } else { - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; + TriList.BooleanInput[StartPageVisibleJoin].BoolValue = true; TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = true; TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; } @@ -474,7 +493,7 @@ namespace PepperDash.Essentials void ShareButtonPressed() { ShareButtonSig.BoolValue = true; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[StartPageVisibleJoin].BoolValue = false; TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true; // Run default source when room is off and share is pressed @@ -778,7 +797,7 @@ namespace PepperDash.Essentials } // Name and logo TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = _CurrentRoom.Name; - if (_CurrentRoom.LogoUrl == null) + if (_CurrentRoom.LogoUrlLightBkgnd == null) { TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = true; TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = false; @@ -787,7 +806,9 @@ namespace PepperDash.Essentials { TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = true; - TriList.StringInput[UIStringJoin.LogoUrl].StringValue = _CurrentRoom.LogoUrl; + TriList.StringInput[UIStringJoin.LogoUrlLightBkgnd].StringValue = _CurrentRoom.LogoUrlLightBkgnd; + TriList.StringInput[UIStringJoin.LogoUrlLightBkgnd].StringValue = _CurrentRoom.LogoUrlDarkBkgnd; + } // Shutdown timer @@ -820,12 +841,43 @@ namespace PepperDash.Essentials if (_CurrentRoom == room) return; // Disconnect current (probably never called) + if (_CurrentRoom != null) + _CurrentRoom.ConfigChanged -= room_ConfigChanged; + room.ConfigChanged -= room_ConfigChanged; room.ConfigChanged += room_ConfigChanged; + if (room.IsMobileControlEnabled) + { + StartPageVisibleJoin = UIBoolJoin.StartMCPageVisible; + UpdateMCJoins(room); + + if (_CurrentRoom != null) + _CurrentRoom.MobileControlRoomBridge.UserCodeChanged -= MobileControlRoomBridge_UserCodeChanged; + + room.MobileControlRoomBridge.UserCodeChanged -= MobileControlRoomBridge_UserCodeChanged; + room.MobileControlRoomBridge.UserCodeChanged += MobileControlRoomBridge_UserCodeChanged; + } + else + { + StartPageVisibleJoin = UIBoolJoin.StartPageVisible; + } + RefreshCurrentRoom(room); } + void MobileControlRoomBridge_UserCodeChanged(object sender, EventArgs e) + { + UpdateMCJoins(_CurrentRoom); + } + + void UpdateMCJoins(EssentialsHuddleSpaceRoom room) + { + TriList.SetString(UIStringJoin.RoomMcUrl, room.MobileControlRoomBridge.McServerUrl); + TriList.SetString(UIStringJoin.RoomMcQrCodeUrl, room.MobileControlRoomBridge.QrCodeUrl); + TriList.SetString(UIStringJoin.RoomUserCode, room.MobileControlRoomBridge.UserCode); + } + /// /// Fires when room config of current room has changed. Meant to refresh room values to propegate any updates to UI /// @@ -855,7 +907,7 @@ namespace PepperDash.Essentials SetupActivityFooterWhenRoomOn(); TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[StartPageVisibleJoin].BoolValue = false; TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = true; } @@ -863,7 +915,7 @@ namespace PepperDash.Essentials { SetupActivityFooterWhenRoomOff(); ShowLogo(); - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; + TriList.BooleanInput[StartPageVisibleJoin].BoolValue = true; TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; } diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 9248e50a..5a99f6e4 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -28,6 +28,8 @@ namespace PepperDash.Essentials Presentation, AudioSetup, Call, Start } + public uint StartPageVisibleJoin { get; private set; } + /// /// Whether volume ramping from this panel will show the volume /// gauge popup. @@ -240,20 +242,34 @@ namespace PepperDash.Essentials if (Config.HeaderStyle.ToLower() == CrestronTouchpanelPropertiesConfig.Habanero) { TriList.SetSigFalseAction(UIBoolJoin.HeaderRoomButtonPress, () => - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderPageVisible)); + { + if (CurrentRoom.IsMobileControlEnabled) + { + Debug.Console(1, "Showing Mobile Control Header Info"); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderInfoMCPageVisible); + } + else + { + Debug.Console(1, "Showing Non Mobile Control Header Info"); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderInfoPageVisible); + } + }); } else if (Config.HeaderStyle.ToLower() == CrestronTouchpanelPropertiesConfig.Verbose) { - // room name on join 1, concat phone and sip on join 2, no button method - //var addr = roomConf.Addresses; - //if (addr == null) // protect from missing values by using default empties - // addr = new EssentialsRoomAddressPropertiesConfig(); - //// empty string when either missing, pipe when both showing - //TriList.SetString(UIStringJoin.RoomAddressPipeText, - // (string.IsNullOrEmpty(addr.PhoneNumber.Trim()) - // || string.IsNullOrEmpty(addr.SipAddress.Trim())) ? "" : " | "); - //TriList.SetString(UIStringJoin.RoomPhoneText, addr.PhoneNumber); - //TriList.SetString(UIStringJoin.RoomSipText, addr.SipAddress); + TriList.SetSigFalseAction(UIBoolJoin.HeaderRoomButtonPress, () => + { + if (CurrentRoom.IsMobileControlEnabled) + { + Debug.Console(1, "Showing Mobile Control Header Info"); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderInfoMCPageVisible); + } + else + { + Debug.Console(1, "Showing Non Mobile Control Header Info"); + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderInfoPageVisible); + } + }); } TriList.SetBool(UIBoolJoin.DateAndTimeVisible, Config.ShowDate && Config.ShowTime); @@ -276,7 +292,7 @@ namespace PepperDash.Essentials } else { - TriList.SetBool(UIBoolJoin.StartPageVisible, true); + TriList.SetBool(StartPageVisibleJoin, true); TriList.SetBool(UIBoolJoin.TapToBeginVisible, true); SetupActivityFooterWhenRoomOff(); } @@ -332,7 +348,7 @@ namespace PepperDash.Essentials /// void ShowLogo() { - if (CurrentRoom.LogoUrl == null) + if (CurrentRoom.LogoUrlLightBkgnd == null) { TriList.SetBool(UIBoolJoin.LogoDefaultVisible, true); TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); @@ -341,7 +357,8 @@ namespace PepperDash.Essentials { TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); TriList.SetBool(UIBoolJoin.LogoUrlVisible, true); - TriList.SetString(UIStringJoin.LogoUrl, _CurrentRoom.LogoUrl); + TriList.SetString(UIStringJoin.LogoUrlLightBkgnd, _CurrentRoom.LogoUrlLightBkgnd); + TriList.SetString(UIStringJoin.LogoUrlDarkBkgnd, _CurrentRoom.LogoUrlDarkBkgnd); } } @@ -362,7 +379,7 @@ namespace PepperDash.Essentials HideAndClearCurrentDisplayModeSigsInUse(); TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, false); TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[StartPageVisibleJoin].BoolValue = false; TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; if (NextMeetingTimer != null) @@ -606,7 +623,7 @@ namespace PepperDash.Essentials return; HideLogo(); HideNextMeetingPopup(); - TriList.SetBool(UIBoolJoin.StartPageVisible, false); + TriList.SetBool(StartPageVisibleJoin, false); TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false); TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); if (CurrentSourcePageManager != null) @@ -626,7 +643,7 @@ namespace PepperDash.Essentials if (VCDriver.IsVisible) VCDriver.Hide(); HideNextMeetingPopup(); - TriList.SetBool(UIBoolJoin.StartPageVisible, false); + TriList.SetBool(StartPageVisibleJoin, false); TriList.SetBool(UIBoolJoin.CallStagingBarVisible, false); TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, true); // Run default source when room is off and share is pressed @@ -957,12 +974,43 @@ namespace PepperDash.Essentials if (_CurrentRoom == room) return; // Disconnect current (probably never called) + if(_CurrentRoom != null) + _CurrentRoom.ConfigChanged -= room_ConfigChanged; + room.ConfigChanged -= room_ConfigChanged; room.ConfigChanged += room_ConfigChanged; + if (room.IsMobileControlEnabled) + { + StartPageVisibleJoin = UIBoolJoin.StartMCPageVisible; + UpdateMCJoins(room); + + if (_CurrentRoom != null) + _CurrentRoom.MobileControlRoomBridge.UserCodeChanged -= MobileControlRoomBridge_UserCodeChanged; + + room.MobileControlRoomBridge.UserCodeChanged -= MobileControlRoomBridge_UserCodeChanged; + room.MobileControlRoomBridge.UserCodeChanged += MobileControlRoomBridge_UserCodeChanged; + } + else + { + StartPageVisibleJoin = UIBoolJoin.StartPageVisible; + } + RefreshCurrentRoom(room); } + void MobileControlRoomBridge_UserCodeChanged(object sender, EventArgs e) + { + UpdateMCJoins(_CurrentRoom); + } + + void UpdateMCJoins(EssentialsHuddleVtc1Room room) + { + TriList.SetString(UIStringJoin.RoomMcUrl, room.MobileControlRoomBridge.McServerUrl); + TriList.SetString(UIStringJoin.RoomMcQrCodeUrl, room.MobileControlRoomBridge.QrCodeUrl); + TriList.SetString(UIStringJoin.RoomUserCode, room.MobileControlRoomBridge.UserCode); + } + /// /// Fires when room config of current room has changed. Meant to refresh room values to propegate any updates to UI /// @@ -999,12 +1047,15 @@ namespace PepperDash.Essentials /// void SetupSourceList() { - + var inCall = CurrentRoom.InCallFeedback.BoolValue; var config = ConfigReader.ConfigObject.SourceLists; + + if (config.ContainsKey(_CurrentRoom.SourceListKey)) { var srcList = config[_CurrentRoom.SourceListKey].OrderBy(kv => kv.Value.Order); + // Setup sources list SourceStagingSrl.Clear(); @@ -1028,6 +1079,8 @@ namespace PepperDash.Essentials b => { if (!b) UiSelectSource(routeKey); }); SourceStagingSrl.AddItem(item); // add to the SRL item.RegisterForSourceChange(_CurrentRoom); + Debug.Console(1, "**** KEY {0}", kvp.Key); + } SourceStagingSrl.Count = (ushort)(i - 1); } @@ -1149,7 +1202,7 @@ namespace PepperDash.Essentials var value = _CurrentRoom.OnFeedback.BoolValue; TriList.BooleanInput[UIBoolJoin.RoomIsOn].BoolValue = value; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = !value; + TriList.BooleanInput[StartPageVisibleJoin].BoolValue = !value; if (value) //ON { @@ -1381,6 +1434,7 @@ namespace PepperDash.Essentials void ShowNotificationRibbon(string message, int timeout); void HideNotificationRibbon(); void ShowTech(); + uint StartPageVisibleJoin { get; } } /// diff --git a/PepperDashEssentials/UIDrivers/ScreenSaverController.cs b/PepperDashEssentials/UIDrivers/ScreenSaverController.cs new file mode 100644 index 00000000..1e3f6d22 --- /dev/null +++ b/PepperDashEssentials/UIDrivers/ScreenSaverController.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials +{ + /// + /// Driver responsible for controlling the screenshaver showing the client logo, MC connection information and QR Code. Moves the elements around to prevent screen burn in + /// + public class ScreenSaverController : PanelDriverBase + { + + /// + /// The parent driver for this + /// + EssentialsPanelMainInterfaceDriver Parent; + + + CTimer PositionTimer; + + uint PositionTimeoutMs; + + List PositionJoins; + + int CurrentPositionIndex = 0; + + public ScreenSaverController(EssentialsPanelMainInterfaceDriver parent, CrestronTouchpanelPropertiesConfig config) + : base(parent.TriList) + { + Parent = parent; + + PositionTimeoutMs = config.ScreenSaverMovePositionIntervalMs; + + TriList.SetSigFalseAction(UIBoolJoin.MCScreenSaverClosePress, () => this.Hide()); + + } + + public override void Show() + { + TriList.SetBool(UIBoolJoin.MCScreenSaverVisible, true); + Parent.AvDriver.PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.MCScreenSaverVisible); + //TriList.SetBool(UIBoolJoin.MCScreenSaverVisible, true); + + CurrentPositionIndex = 0; + SetCurrentPosition(); + + ClearAllPositions(); + + TriList.SetBool(UIBoolJoin.MCScreenSaverVisible, false); + Parent.AvDriver.PopupInterlock.HideAndClear(); + //TriList.SetBool(UIBoolJoin.MCScreenSaverVisible, false); + + base.Hide(); + } + + + + void StartPositionTimer() + { + if (PositionTimer == null) + { + PositionTimer = new CTimer((o) => PositionTimerExpired(), PositionTimeoutMs); + } + else + { + PositionTimer.Reset(PositionTimeoutMs); + } + + } + + void PositionTimerExpired() + { + IncrementPositionIndex(); + + SetCurrentPosition(); + + StartPositionTimer(); + } + + void IncrementPositionIndex() + { + if (CurrentPositionIndex < PositionJoins.Count - 1) + { + CurrentPositionIndex++; + } + else + { + CurrentPositionIndex = 0; + } + + Debug.Console(1, "ScreenSaver Position Timer Expired: Setting new position: {0}", CurrentPositionIndex); + } + + // + void SetCurrentPosition() + { + ClearAllPositions(); + + // Set based on current index + TriList.SetBool(PositionJoins[CurrentPositionIndex], true); + } + + void ClearAllPositions() + { + foreach (var join in PositionJoins) + { + TriList.SetBool(join, false); + } + } + } + +} \ No newline at end of file diff --git a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index f966c4cb..55811d44 100644 --- a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs +++ b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -192,22 +192,76 @@ namespace PepperDash.Essentials.UIDrivers.VC /// void Codec_IsReady() { - string roomNumberSipUri = ""; - - if (!string.IsNullOrEmpty(Codec.CodecInfo.SipUri)) // If both values are present, format the string with a pipe divider - roomNumberSipUri = string.Format("{0} | {1}", GetFormattedPhoneNumber(Codec.CodecInfo.SipPhoneNumber), Codec.CodecInfo.SipUri); - else // If only one value present, just show the phone number - roomNumberSipUri = Codec.CodecInfo.SipPhoneNumber; - - if(string.IsNullOrEmpty(roomNumberSipUri)) - roomNumberSipUri = string.Format("{0} | {1}", Codec.CodecInfo.E164Alias, Codec.CodecInfo.H323Id); - - TriList.SetString(UIStringJoin.RoomPhoneText, roomNumberSipUri); + SetupAddresses(); if(HeaderDriver.HeaderButtonsAreSetUp) HeaderDriver.ComputeHeaderCallStatus(Codec); } + void SetupAddresses() + { + string roomContactNumbers = ""; + string roomPhoneNumber = ""; + string roomVideoAddress = ""; + + + Debug.Console(1, + @" + Codec.CodecInfo.IpAddress: {0} + Codec.CodecInfo.SipUri: {1} + Codec.CodecInfo.SipPhoneNumber: {2} + Codec.CodecInfo.E164Alias: {3} + Codec.CodecInfo.H323Id: {4} + ", Codec.CodecInfo.IpAddress, Codec.CodecInfo.SipUri, Codec.CodecInfo.SipPhoneNumber, Codec.CodecInfo.E164Alias, Codec.CodecInfo.H323Id); + + // Populate phone number + if (!string.IsNullOrEmpty(Codec.CodecInfo.SipUri)) // If both values are present, format the string with a pipe divider + { + roomPhoneNumber = Codec.CodecInfo.SipUri; + } + else if (!string.IsNullOrEmpty(Codec.CodecInfo.SipPhoneNumber)) // If only one value present, just show the phone number + { + roomPhoneNumber = GetFormattedPhoneNumber(Codec.CodecInfo.SipPhoneNumber); + } + + // Populate video number + if (!string.IsNullOrEmpty(Codec.CodecInfo.IpAddress)) + { + roomVideoAddress = Codec.CodecInfo.IpAddress; + } + else if (!string.IsNullOrEmpty(Codec.CodecInfo.E164Alias)) + { + roomVideoAddress = Codec.CodecInfo.E164Alias; + } + else if (!string.IsNullOrEmpty(Codec.CodecInfo.H323Id)) + { + roomVideoAddress = Codec.CodecInfo.H323Id; + } + + Debug.Console(1, + @" Room Contact Numbers: + Phone Number: {0} + Video Number: {1} + ", roomPhoneNumber, roomVideoAddress); + + if (!string.IsNullOrEmpty(roomPhoneNumber) && !string.IsNullOrEmpty(roomVideoAddress)) + { + roomContactNumbers = string.Format("{0} | {1}", roomPhoneNumber, roomVideoAddress); + } + else if (!string.IsNullOrEmpty(roomPhoneNumber)) + { + roomContactNumbers = roomPhoneNumber; + } + else if (!string.IsNullOrEmpty(roomVideoAddress)) + { + roomContactNumbers = roomVideoAddress; + } + + TriList.SetString(UIStringJoin.RoomAddressPipeText, roomContactNumbers); + TriList.SetString(UIStringJoin.RoomPhoneText, roomPhoneNumber); + TriList.SetString(UIStringJoin.RoomVideoAddressText, roomVideoAddress); + } + /// /// Handles status changes for calls /// @@ -337,6 +391,15 @@ namespace PepperDash.Essentials.UIDrivers.VC icon = "Camera"; msg = string.Format("Incoming video call from: {0}", call.Name); } + + if (Parent.PopupInterlock.IsShown) + { + if (Parent.PopupInterlock.CurrentJoin == UIBoolJoin.MCScreenSaverVisible) + { + Parent.PopupInterlock.HideAndClear(); + } + } + IncomingCallModal.PresentModalDialog(2, "Incoming Call", icon, msg, "Ignore", "Accept", false, false, b => { diff --git a/README.md b/README.md index e411cabf..efb706c0 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,39 @@ - # PepperDash Essentials Framework (c) 2020 ## [Latest Release](https://github.com/PepperDash/Essentials/releases/latest) ## License + Provided under MIT license ## Overview + PepperDash 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. Essentials 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. ## Minimum Requirements + - Essentials Framework runs on any Crestron 3-series processor, **4-series** processor or Crestron's VC-4 platform. - To edit and compile the source, Microsoft Visual Studio 2008 Professional with SP1 is required. - Crestron's Simpl# Plugin is also required (must be obtained from Crestron). ## Dependencies -The [PepperDash.Core](https://github.com/PepperDash/PepperDashCore) SIMPL# library is required. It is referenced as a submodule and will be automatically checked out when cloning this repo if set to recurse submodules. This allows different builds of the PepperDash.Core library to be referenced by checking out the desired submodule commit. +The [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). + +### Installing Dependencies + +To install dependencies once nuget.exe is installed, run the following command: +`nuget install .\packages.config -OutputDirectory .\packages -excludeVersion`. +To verify that the packages installed correctly, open Essentials and make sure that all references are found, then try and build it. + +### Installing Different versions of PepperDash Core + +If 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. ## Utilization + Essentials 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. Utilization of Essentials Framework falls into the following categories: @@ -34,13 +47,13 @@ Utilization of Essentials Framework falls into the following categories: - 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. 3. Hybrid Application that may contain elements of both standalone control and SIMPL partner application integration. -- 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. - ## Documentation - For detailed documentation, see the [Wiki](https://github.com/PepperDash/EssentialsFramework/wiki). +- 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. + +## Documentation + +For detailed documentation, see the [Wiki](https://github.com/PepperDash/EssentialsFramework/wiki). ## How-To (Getting Started) See [Getting Started](https://github.com/PepperDash/Essentials/wiki/Get-started#how-to-get-started) - - diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs index 03f73ba4..616d61b1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs @@ -1,11 +1,33 @@ -using PepperDash.Core; +using System; +using PepperDash.Core; namespace PepperDash.Essentials.Core.DeviceTypeInterfaces { - public interface IMobileControl:IKeyed + /// + /// Describes a MobileControlSystemController + /// + public interface IMobileControl : IKeyed { void CreateMobileControlRoomBridge(EssentialsRoomBase room); void LinkSystemMonitorToAppServer(); + + + } + + /// + /// Describes a MobileControl Room Bridge + /// + public interface IMobileControlRoomBridge : IKeyed + { + event EventHandler UserCodeChanged; + + string UserCode { get; } + + string QrCodeUrl { get; } + + string McServerUrl { get; } + + string RoomName { get; } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs index dfc63911..d50de428 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs @@ -337,7 +337,36 @@ namespace PepperDash.Essentials.Core return; } com.SimulateReceive(match.Groups[2].Value); - } + } + + /// + /// Prints a list of routing inputs and outputs by device key. + /// + /// Device key from which to report data + public static void GetRoutingPorts(string s) + { + var device = GetDeviceForKey(s); + + if (device == null) return; + var inputPorts = (device as IRoutingInputsOutputs).InputPorts; + var outputPorts = (device as IRoutingInputsOutputs).OutputPorts; + if (inputPorts != null) + { + Debug.Console(0, "Device {0} has {1} Input Ports:", s, inputPorts.Count); + foreach (var routingInputPort in inputPorts) + { + Debug.Console(0, "{0}", routingInputPort.Key); + } + } + if (outputPorts != null) + { + Debug.Console(0, "Device {0} has {1} Output Ports:", s, outputPorts.Count); + foreach (var routingOutputPort in outputPorts) + { + Debug.Console(0, "{0}", routingOutputPort.Key); + } + } + } /// /// Attempts to set the debug level of a device diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs index ecfca4f8..83241e79 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs @@ -1,45 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro; +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; using Newtonsoft.Json.Linq; -using PepperDash.Essentials.Core.Config; - - -using PepperDash.Core; - -namespace PepperDash.Essentials.Core -{ - - /// - /// IR port wrapper. May act standalone - /// - public class IrOutputPortController : Device - { - uint IrPortUid; - IROutputPort IrPort; - - public ushort StandardIrPulseTime { get; set; } - public string DriverFilepath { get; private set; } - public bool DriverIsLoaded { get; private set; } - - /// - /// Constructor for IrDevice base class. If a null port is provided, this class will - /// still function without trying to talk to a port. - /// - public IrOutputPortController(string key, IROutputPort port, string irDriverFilepath) - : base(key) - { - //if (port == null) throw new ArgumentNullException("port"); - IrPort = port; - if (port == null) - { - Debug.Console(0, this, "WARNING No valid IR Port assigned to controller. IR will not function"); - return; - } - LoadDriver(irDriverFilepath); +using PepperDash.Essentials.Core.Config; + + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + + /// + /// IR port wrapper. May act standalone + /// + public class IrOutputPortController : Device + { + uint IrPortUid; + IROutputPort IrPort; + + public ushort StandardIrPulseTime { get; set; } + public string DriverFilepath { get; private set; } + public bool DriverIsLoaded { get; private set; } + + /// + /// Constructor for IrDevice base class. If a null port is provided, this class will + /// still function without trying to talk to a port. + /// + public IrOutputPortController(string key, IROutputPort port, string irDriverFilepath) + : base(key) + { + //if (port == null) throw new ArgumentNullException("port"); + IrPort = port; + if (port == null) + { + Debug.Console(0, this, "WARNING No valid IR Port assigned to controller. IR will not function"); + return; + } + LoadDriver(irDriverFilepath); } public IrOutputPortController(string key, Func postActivationFunc, @@ -54,85 +54,85 @@ namespace PepperDash.Essentials.Core - /// - /// Loads the IR driver at path - /// - /// - public void LoadDriver(string path) - { - if (string.IsNullOrEmpty(path)) path = DriverFilepath; - try - { - IrPortUid = IrPort.LoadIRDriver(path); - DriverFilepath = path; - StandardIrPulseTime = 200; - DriverIsLoaded = true; - } - catch - { - DriverIsLoaded = false; - var message = string.Format("WARNING IR Driver '{0}' failed to load", path); - Debug.Console(0, this, message); - ErrorLog.Error(message); - } - } - - - /// - /// Starts and stops IR command on driver. Safe for missing commands - /// - public virtual void PressRelease(string command, bool state) - { - Debug.Console(2, this, "IR:'{0}'={1}", command, state); - if (IrPort == null) - { - Debug.Console(2, this, "WARNING No IR Port assigned to controller"); - return; - } - if (!DriverIsLoaded) - { - Debug.Console(2, this, "WARNING IR driver is not loaded"); - return; - } - if (state) - { - if (IrPort.IsIRCommandAvailable(IrPortUid, command)) - IrPort.Press(IrPortUid, command); - else - NoIrCommandError(command); - } - else - IrPort.Release(); - } - - /// - /// Pulses a command on driver. Safe for missing commands - /// - public virtual void Pulse(string command, ushort time) - { - if (IrPort == null) - { - Debug.Console(2, this, "WARNING No IR Port assigned to controller"); - return; - } - if (!DriverIsLoaded) - { - Debug.Console(2, this, "WARNING IR driver is not loaded"); - return; - } - if (IrPort.IsIRCommandAvailable(IrPortUid, command)) - IrPort.PressAndRelease(IrPortUid, command, time); - else - NoIrCommandError(command); - } - - /// - /// Notifies the console when a bad command is used. - /// - protected void NoIrCommandError(string command) - { - Debug.Console(2, this, "Device {0}: IR Driver {1} does not contain command {2}", - Key, IrPort.IRDriverFileNameByIRDriverId(IrPortUid), command); + /// + /// Loads the IR driver at path + /// + /// + public void LoadDriver(string path) + { + if (string.IsNullOrEmpty(path)) path = DriverFilepath; + try + { + IrPortUid = IrPort.LoadIRDriver(path); + DriverFilepath = path; + StandardIrPulseTime = 200; + DriverIsLoaded = true; + } + catch + { + DriverIsLoaded = false; + var message = string.Format("WARNING IR Driver '{0}' failed to load", path); + Debug.Console(0, this, message); + ErrorLog.Error(message); + } } - } + + + /// + /// Starts and stops IR command on driver. Safe for missing commands + /// + public virtual void PressRelease(string command, bool state) + { + Debug.Console(2, this, "IR:'{0}'={1}", command, state); + if (IrPort == null) + { + Debug.Console(2, this, "WARNING No IR Port assigned to controller"); + return; + } + if (!DriverIsLoaded) + { + Debug.Console(2, this, "WARNING IR driver is not loaded"); + return; + } + if (state) + { + if (IrPort.IsIRCommandAvailable(IrPortUid, command)) + IrPort.Press(IrPortUid, command); + else + NoIrCommandError(command); + } + else + IrPort.Release(); + } + + /// + /// Pulses a command on driver. Safe for missing commands + /// + public virtual void Pulse(string command, ushort time) + { + if (IrPort == null) + { + Debug.Console(2, this, "WARNING No IR Port assigned to controller"); + return; + } + if (!DriverIsLoaded) + { + Debug.Console(2, this, "WARNING IR driver is not loaded"); + return; + } + if (IrPort.IsIRCommandAvailable(IrPortUid, command)) + IrPort.PressAndRelease(IrPortUid, command, time); + else + NoIrCommandError(command); + } + + /// + /// Notifies the console when a bad command is used. + /// + protected void NoIrCommandError(string command) + { + Debug.Console(2, this, "Device {0}: IR Driver {1} does not contain command {2}", + Key, IrPort.IRDriverFileNameByIRDriverId(IrPortUid), command); + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs index b523d2c8..eadaab12 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs @@ -1,5 +1,6 @@ using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Linq; using Crestron.SimplSharp; using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.Diagnostics; @@ -100,25 +101,28 @@ namespace PepperDash.Essentials.Core.Monitoring UptimeFeedback.FireUpdate(); LastStartFeedback.FireUpdate(); - } - - private void ParseUptime(string response) - { - var splitString = response.Trim().Split('\r', '\n'); - - var lastStartRaw = splitString[2]; - var lastStartIndex = lastStartRaw.IndexOf(':'); - - _lastStart = lastStartRaw.Substring(lastStartIndex + 1).Trim(); - - var uptimeRaw = splitString[0]; - - var forIndex = uptimeRaw.IndexOf("for", StringComparison.Ordinal); - - //4 => "for " to get what's on the right - _uptime = uptimeRaw.Substring(forIndex + 4); - } - + } + + private void ParseUptime(string response) + { + var splitString = response.Trim().Split('\r', '\n'); + + var lastStartRaw = splitString.FirstOrDefault(o => o.Contains("started")); + var uptimeRaw = splitString.FirstOrDefault(o => o.Contains("running")); + + if (!String.IsNullOrEmpty(lastStartRaw)) + { + var lastStartIndex = lastStartRaw.IndexOf(':'); + _lastStart = lastStartRaw.Substring(lastStartIndex + 1).Trim(); + } + + if (String.IsNullOrEmpty(uptimeRaw)) return; + var forIndex = uptimeRaw.IndexOf("for", StringComparison.Ordinal); + + //4 => "for " to get what's on the right + _uptime = uptimeRaw.Substring(forIndex + 4); + } + private void CrestronEnvironmentOnEthernetEventHandler(EthernetEventArgs ethernetEventArgs) { if (ethernetEventArgs.EthernetEventType != eEthernetEventType.LinkUp) return; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index e57b6a62..c31a46b8 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -48,71 +48,83 @@ False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll + True False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.ThreeSeriesCards.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.ThreeSeriesCards.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + True - + False - ..\..\pepperdashcore-builds\PepperDash_Core.dll + ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll + ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll + True diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec new file mode 100644 index 00000000..d5bb4a57 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec @@ -0,0 +1,24 @@ + + + + PepperDashEssentials + 1.5.6 + PepperDash Essentials + PepperDash Technologies + pepperdash + false + MIT + https://github.com/PepperDash/PepperDashCore + Copyright 2020 + PepperDash 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. Essentials 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. + crestron 3series 4series + + + + + + + + + + \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs index bb57941e..4cf36470 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs @@ -9,6 +9,7 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Devices; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; namespace PepperDash.Essentials.Core { @@ -37,10 +38,32 @@ namespace PepperDash.Essentials.Core protected abstract Func IsWarmingFeedbackFunc { get; } protected abstract Func IsCoolingFeedbackFunc { get; } + /// + /// Indicates if this room is Mobile Control Enabled + /// + public bool IsMobileControlEnabled { get; private set; } + + /// + /// The bridge for this room if Mobile Control is enabled + /// + public IMobileControlRoomBridge MobileControlRoomBridge { get; private set; } + /// /// The config name of the source list /// - public string SourceListKey { get; set; } + /// + protected string _SourceListKey; + public virtual string SourceListKey { + get + { + return _SourceListKey; + } + set + { + _SourceListKey = value; + + } + } /// /// Timer used for informing the UIs of a shutdown @@ -58,7 +81,9 @@ namespace PepperDash.Essentials.Core public Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; set; } - public string LogoUrl { get; set; } + public string LogoUrlLightBkgnd { get; set; } + + public string LogoUrlDarkBkgnd { get; set; } protected SecondsCountdownTimer RoomVacancyShutdownTimer { get; private set; } @@ -128,6 +153,34 @@ namespace PepperDash.Essentials.Core }); } + public override bool CustomActivate() + { + SetUpMobileControl(); + + return base.CustomActivate(); + } + + /// + /// If mobile control is enabled, sets the appropriate properties + /// + void SetUpMobileControl() + { + var mcBridgeKey = string.Format("mobileControlBridge-{0}", Key); + var mcBridge = DeviceManager.GetDeviceForKey(mcBridgeKey); + if (mcBridge == null) + { + Debug.Console(1, this, "*********************Mobile Control Bridge Not found for this room."); + IsMobileControlEnabled = false; + return; + } + else + { + MobileControlRoomBridge = mcBridge as IMobileControlRoomBridge; + Debug.Console(1, this, "*********************Mobile Control Bridge found and enabled for this room"); + IsMobileControlEnabled = true; + } + } + void RoomVacancyShutdownPromptTimer_HasFinished(object sender, EventArgs e) { switch (VacancyMode) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/CrestronTouchpanelPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/CrestronTouchpanelPropertiesConfig.cs index 42882a51..73b66bce 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/CrestronTouchpanelPropertiesConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/CrestronTouchpanelPropertiesConfig.cs @@ -14,6 +14,8 @@ public UiSetupPropertiesConfig Setup { get; set; } public string HeaderStyle { get; set; } public bool IncludeInFusionRoomHealth { get; set; } + public uint ScreenSaverTimeoutMin { get; set; } + public uint ScreenSaverMovePositionIntervalMs { get; set; } /// @@ -26,6 +28,10 @@ { SourcesOverflowCount = 5; HeaderStyle = CrestronTouchpanelPropertiesConfig.Habanero; + + // Default values + ScreenSaverTimeoutMin = 5; + ScreenSaverMovePositionIntervalMs = 15000; } /// diff --git a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj index 76aae649..a8843496 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -48,43 +48,48 @@ False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + True - + False - ..\..\pepperdashcore-builds\PepperDash_Core.dll + ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False False - ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + True diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs new file mode 100644 index 00000000..d86f628e --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasExternalSourceSwitching.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public interface IHasExternalSourceSwitching + { + bool ExternalSourceListEnabled { get; } + void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type); + void SetExternalSourceState(string key, eExternalSourceMode mode); + void ClearExternalSources(); + Action RunRouteAction { set;} + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index e33e54fd..7c978bd8 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -48,24 +48,28 @@ False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Lighting.dll + ..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Lighting.dll + True - + False - ..\..\pepperdashcore-builds\PepperDash_Core.dll + ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll False @@ -73,26 +77,28 @@ False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + True False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False False - ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + True @@ -112,6 +118,7 @@ + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index 802471b3..9759cfdd 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -15,16 +15,17 @@ using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Routing; using PepperDash.Essentials.Devices.Common.Cameras; using PepperDash.Essentials.Devices.Common.Codec; -using PepperDash.Essentials.Core; using PepperDash.Essentials.Devices.Common.VideoCodec; namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; + public enum eExternalSourceType {camera, desktop, document_camera, mediaplayer, PC, whiteboard, other} + public enum eExternalSourceMode {Ready, NotReady, Hidden, Error} public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView, - ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets + ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IHasExternalSourceSwitching { public event EventHandler DirectoryResultReturned; @@ -348,6 +349,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CallHistory = new CodecCallHistory(); + if (props.Favorites != null) { CallFavorites = new CodecCallFavorites(); @@ -398,6 +400,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco SetUpCameras(); CreateOsdSource(); + + if (props.ExternalSourceListEnabled != null) + { + ExternalSourceListEnabled = props.ExternalSourceListEnabled; + } } /// @@ -472,7 +479,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco prefix + "/Bookings" + Delimiter + prefix + "/Event/CallDisconnect" + Delimiter + prefix + "/Event/Bookings" + Delimiter + - prefix + "/Event/CameraPresetListUpdated" + Delimiter; + prefix + "/Event/CameraPresetListUpdated" + Delimiter + + prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter; return base.CustomActivate(); } @@ -629,6 +637,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } } } + } @@ -853,6 +862,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { GetBookings(null); } + + else if (response.IndexOf("\"UserInterface\":{") > -1 || response.IndexOf("\"UserInterface\": {") > -1) // External Source Trigger + { + CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); + JsonConvert.PopulateObject(response, eventReceived); + Debug.Console(2, this, "*** Got an External Source Selection {0} {1}", eventReceived, eventReceived.Event.UserInterface, eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value); + + if (RunRouteAction != null) + { + RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null); + } + } } else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1) { @@ -1699,7 +1720,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { get { - if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias.E164 != null) + if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias != null + && CodecConfiguration.Configuration.H323.H323Alias.ID != null) { return CodecConfiguration.Configuration.H323.H323Alias.ID.Value; } @@ -1801,9 +1823,76 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco SendText(string.Format("xCommand Call FarEndControl RoomPreset Activate CallId: {0} PresetId: {1}", GetCallId(), preset)); } - } - /// + #region IHasExternalSourceSwitching Members + + /// + /// Weather the Cisco supports External Source Lists or not + /// + public bool ExternalSourceListEnabled + { + get; + private set; + } + + /// + /// Adds an external source to the Cisco + /// + /// + /// + /// + public void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type) + { + int id = 2; + if (connectorId.ToLower() == "hdmiin3") + { + id = 3; + } + SendText(string.Format("xCommand UserInterface Presentation ExternalSource Add ConnectorId: {0} SourceIdentifier: \"{1}\" Name: \"{2}\" Type: {3}", id, key, name, type.ToString())); + // SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: Ready", key)); + Debug.Console(2, this, "Adding ExternalSource {0} {1}", connectorId, name); + + } + + + /// + /// Sets the state of the External Source + /// + /// + /// + public void SetExternalSourceState(string key, eExternalSourceMode mode) + { + SendText(string.Format("xCommand UserInterface Presentation ExternalSource State Set SourceIdentifier: \"{0}\" State: {1}", key, mode.ToString())); + } + /// + /// Clears all external sources on the codec + /// + public void ClearExternalSources() + { + SendText("xCommand UserInterface Presentation ExternalSource RemoveAll"); + + } + + /// + /// Action that will run when the External Source is selected. + /// + public Action RunRouteAction { private get; set; } + + + + + + + #endregion + #region ExternalDevices + + + + #endregion + } + + + /// /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes /// public class CodecCommandWithLabel diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs index 521b9e5c..2f335780 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs @@ -1,47 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; - -using PepperDash.Core; -using PepperDash.Essentials.Core; - -using Newtonsoft.Json; - -namespace PepperDash.Essentials.Devices.Common.Codec -{ - public class CiscoSparkCodecPropertiesConfig - { - [JsonProperty("communicationMonitorProperties")] - public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } - - [JsonProperty("favorites")] - public List Favorites { get; set; } - - /// - /// Valid values: "Local" or "Corporate" - /// - [JsonProperty("phonebookMode")] - public string PhonebookMode { get; set; } - - [JsonProperty("showSelfViewByDefault")] - public bool ShowSelfViewByDefault { get; set; } - - [JsonProperty("sharing")] - public SharingProperties Sharing { get; set; } - - /// - /// Optionsal property to set the limit of any phonebook queries for directory or searching - /// - [JsonProperty("phonebookResultsLimit")] - public uint PhonebookResultsLimit { get; set; } - - } - - public class SharingProperties - { - [JsonProperty("autoShareContentWhileInCall")] - public bool AutoShareContentWhileInCall { get; set; } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public class CiscoSparkCodecPropertiesConfig + { + [JsonProperty("communicationMonitorProperties")] + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + [JsonProperty("favorites")] + public List Favorites { get; set; } + + /// + /// Valid values: "Local" or "Corporate" + /// + [JsonProperty("phonebookMode")] + public string PhonebookMode { get; set; } + + [JsonProperty("showSelfViewByDefault")] + public bool ShowSelfViewByDefault { get; set; } + + [JsonProperty("sharing")] + public SharingProperties Sharing { get; set; } + + [JsonProperty("externalSourceListEnabled")] + public bool ExternalSourceListEnabled { get; set; } + + /// + /// Optionsal property to set the limit of any phonebook queries for directory or searching + /// + [JsonProperty("phonebookResultsLimit")] + public uint PhonebookResultsLimit { get; set; } + + } + + public class SharingProperties + { + [JsonProperty("autoShareContentWhileInCall")] + public bool AutoShareContentWhileInCall { get; set; } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs index 342a8848..23594ce2 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs @@ -126,11 +126,36 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Encryption Encryption { get; set; } public RequestedURI RequestedURI { get; set; } public PeopleCountAverage PeopleCountAverage { get; set; } - } - + } + public class UserInterface + { + public string id { get; set; } + public Presentation Presentation { get; set; } + } + public class Presentation + { + public string id { get; set; } + public ExternalSource ExternalSource { get; set; } + } + public class ExternalSource + { + public string id { get; set; } + public Selected Selected { get; set; } + } + public class Selected + { + public string id { get; set; } + public SourceIdentifier SourceIdentifier { get; set; } + } + public class SourceIdentifier + { + public string id { get; set; } + public string Value { get; set; } + } public class Event { - public CallDisconnect CallDisconnect { get; set; } + public CallDisconnect CallDisconnect { get; set; } + public UserInterface UserInterface { get; set; } } public class RootObject diff --git a/packages.config b/packages.config new file mode 100644 index 00000000..11087174 --- /dev/null +++ b/packages.config @@ -0,0 +1,3 @@ + + + \ No newline at end of file