diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 94c45937..e654fa68 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -22,7 +22,7 @@ env: RELEASE_BRANCH: main jobs: Build_Project: - runs-on: windows-latest + runs-on: windows-2019 steps: # First we checkout the source repo - name: Checkout repo @@ -98,7 +98,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} Push_Nuget_Package: needs: Build_Project - runs-on: windows-latest + runs-on: windows-2019 steps: - name: Download Build Version Info uses: actions/download-artifact@v1 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dc219ce3..abcc8419 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ env: RELEASE_BRANCH: main jobs: Build_Project: - runs-on: windows-latest + runs-on: windows-2019 steps: # First we checkout the source repo - name: Checkout repo @@ -82,7 +82,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} Push_Nuget_Package: needs: Build_Project - runs-on: windows-latest + runs-on: windows-2019 steps: - name: Download Build Version Info uses: actions/download-artifact@v1 diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 84018a15..be78aaf9 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -28,6 +28,7 @@ namespace PepperDash.Essentials HttpLogoServer LogoServer; private CTimer _startTimer; + private CEvent _initializeEvent; private const long StartupTime = 500; public ControlSystem() @@ -46,6 +47,24 @@ namespace PepperDash.Essentials public override void InitializeSystem() { _startTimer = new CTimer(StartSystem,StartupTime); + + + // If the control system is a DMPS type, we need to wait to exit this method until all devices have had time to activate + // to allow any HD-BaseT DM endpoints to register first. + if (Global.ControlSystemIsDmpsType) + { + Debug.Console(2, "******************* InitializeSystem() Entering **********************"); + + _initializeEvent = new CEvent(); + + DeviceManager.AllDevicesActivated += (o, a) => + { + _initializeEvent.Set(); + Debug.Console(2, "******************* InitializeSystem() Exiting **********************"); + }; + + _initializeEvent.Wait(30000); + } } private void StartSystem(object obj) @@ -361,9 +380,7 @@ namespace PepperDash.Essentials if(propertiesConfig == null) propertiesConfig = new DM.Config.DmpsRoutingPropertiesConfig(); - var dmpsRoutingController = DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", this.ControllerPrompt, propertiesConfig); - - DeviceManager.AddDevice(dmpsRoutingController); + DeviceManager.AddDevice(DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", this.ControllerPrompt, propertiesConfig)); } else if (this.ControllerPrompt.IndexOf("mpc3", StringComparison.OrdinalIgnoreCase) > -1) { @@ -450,14 +467,13 @@ namespace PepperDash.Essentials return; } + uint fusionIpId = 0xf1; + foreach (var roomConfig in ConfigReader.ConfigObject.Rooms) { var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as IEssentialsRoom; if (room != null) { - // default IPID - uint fusionIpId = 0xf1; - // default to no join map key string fusionJoinMapKey = string.Empty; @@ -478,7 +494,7 @@ namespace PepperDash.Essentials { DeviceManager.AddDevice(room); - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion"); + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId); DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase(room, fusionIpId, fusionJoinMapKey)); @@ -490,8 +506,11 @@ namespace PepperDash.Essentials { DeviceManager.AddDevice(room); - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((IEssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey)); + if (!(room is EssentialsCombinedHuddleVtc1Room)) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId); + DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((IEssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey)); + } Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge..."); @@ -502,7 +521,7 @@ namespace PepperDash.Essentials DeviceManager.AddDevice(room); Debug.Console(0, Debug.ErrorLogLevel.Notice, - "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion"); + "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId); DeviceManager.AddDevice(new EssentialsTechRoomFusionSystemController((EssentialsTechRoom)room, fusionIpId, fusionJoinMapKey)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge"); @@ -515,9 +534,13 @@ namespace PepperDash.Essentials DeviceManager.AddDevice(room); } + fusionIpId += 1; } else + { Debug.Console(0, Debug.ErrorLogLevel.Notice, "Notice: Cannot create room from config, key '{0}' - Is this intentional? This may be a valid configuration.", roomConfig.Key); + + } } Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded."); diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 54d936f0..c3877706 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -150,7 +150,7 @@ namespace PepperDash.Essentials.Fusion protected override void CreateSymbolAndBasicSigs(uint ipId) { - Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + Debug.Console(0, this, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId); FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index 14bd450a..e4a0cff6 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -144,12 +144,14 @@ + - + + diff --git a/PepperDashEssentials/Properties/ControlSystem.cfg b/PepperDashEssentials/Properties/ControlSystem.cfg index d2c3b2c7..384bf949 100644 --- a/PepperDashEssentials/Properties/ControlSystem.cfg +++ b/PepperDashEssentials/Properties/ControlSystem.cfg @@ -1,7 +1,7 @@  - 192.168.10.1 -
auto 192.168.10.1
+ Test RMC3 +
auto 192.168.1.40;username crestron
Program01 Internal Flash
\ No newline at end of file diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index 9824ec67..5a2bd34f 100644 --- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -39,6 +39,10 @@ namespace PepperDash.Essentials.Room.Config { return new EssentialsDualDisplayRoom(roomConfig); } + if (typeName == "combinedhuddlevtc1") + { + return new EssentialsCombinedHuddleVtc1Room(roomConfig); + } return typeName != "techroom" ? null : new EssentialsTechRoom(roomConfig); } @@ -201,6 +205,12 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")] public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; } + /// + /// Indicates if this room represents a combination of other rooms + /// + [JsonProperty("isRoomCombinationScenario")] + public bool IsRoomCombinationScenario { get; set; } + public EssentialsRoomPropertiesConfig() { LogoLight = new EssentialsLogoPropertiesConfig(); diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs index 9ff3a2d2..2944b854 100644 --- a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -56,6 +56,9 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("mirroredTuners")] public Dictionary MirroredTuners { get; set; } + [JsonProperty("helpMessage")] + public string HelpMessage { get; set; } + /// /// Indicates the room /// diff --git a/PepperDashEssentials/Room/Types/EssentialsCombinedHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsCombinedHuddleVtc1Room.cs new file mode 100644 index 00000000..50186d0b --- /dev/null +++ b/PepperDashEssentials/Room/Types/EssentialsCombinedHuddleVtc1Room.cs @@ -0,0 +1,821 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Room.Config; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.AudioCodec; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; + +namespace PepperDash.Essentials +{ + public class EssentialsCombinedHuddleVtc1Room : EssentialsRoomBase, IEssentialsHuddleVtc1Room + { + private bool _codecExternalSourceChange; + public event EventHandler CurrentVolumeDeviceChange; + public event SourceInfoChangeHandler CurrentSourceChange; + + + //************************ + // Call-related stuff + + public BoolFeedback InCallFeedback { get; private set; } + + ///// + ///// Make this more specific + ///// + //public List ActiveCalls { get; private set; } + + /// + /// States: 0 for on hook, 1 for video, 2 for audio, 3 for telekenesis + /// + public IntFeedback CallTypeFeedback { get; private set; } + + /// + /// + /// + public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } + + /// + /// When something in the room is sharing with the far end or through other means + /// + public BoolFeedback IsSharingFeedback { get; private set; } + + //************************ + + protected override Func OnFeedbackFunc + { + get + { + return () => + { + + var displays = Displays.OfType().ToList(); + + var val = CurrentSourceInfo != null + && CurrentSourceInfo.Type == eSourceListItemType.Route + && displays.Count > 0; + //&& disp.PowerIsOnFeedback.BoolValue; + return val; + }; + } + } + /// + /// + /// + protected override Func IsWarmingFeedbackFunc + { + get + { + return () => Displays.OfType().Any((d) => d.IsWarmingUpFeedback.BoolValue); + } + } + /// + /// + /// + protected override Func IsCoolingFeedbackFunc + { + get + { + return () => Displays.OfType().Any((d) => d.IsCoolingDownFeedback.BoolValue); + } + } + + public EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; private set; } + + private List Displays; + + public IRoutingSinkWithSwitching DefaultDisplay { get; private set; } + + public IBasicVolumeControls DefaultAudioDevice { get; private set; } + public IBasicVolumeControls DefaultVolumeControls { get; private set; } + + public VideoCodecBase VideoCodec { get; private set; } + + public AudioCodecBase AudioCodec { get; private set; } + + public bool ExcludeFromGlobalFunctions { get; set; } + + public string DefaultSourceItem { get; set; } + + public ushort DefaultVolume { get; set; } + + /// + /// If room is off, enables power on to last source. Default true + /// + public bool EnablePowerOnToLastSource { get; set; } + string LastSourceKey; + + /// + /// Sets the volume control device, and attaches/removes InUseTrackers with "audio" + /// tag to device. + /// + public IBasicVolumeControls CurrentVolumeControls + { + get { return _CurrentAudioDevice; } + set + { + if (value == _CurrentAudioDevice) return; + + var oldDev = _CurrentAudioDevice; + // derigister this room from the device, if it can + if (oldDev is IInUseTracking) + (oldDev as IInUseTracking).InUseTracker.RemoveUser(this, "audio"); + var handler = CurrentVolumeDeviceChange; + if (handler != null) + CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.WillChange)); + _CurrentAudioDevice = value; + if (handler != null) + CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(oldDev, value, ChangeType.DidChange)); + // register this room with new device, if it can + if (_CurrentAudioDevice is IInUseTracking) + (_CurrentAudioDevice as IInUseTracking).InUseTracker.AddUser(this, "audio"); + } + } + IBasicVolumeControls _CurrentAudioDevice; + + /// + /// The SourceListItem last run - containing names and icons + /// + public SourceListItem CurrentSourceInfo + { + get { return _CurrentSourceInfo; } + set + { + if (value == _CurrentSourceInfo) return; + + var handler = CurrentSourceChange; + // remove from in-use tracker, if so equipped + if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) + (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control"); + + if (handler != null) + handler(_CurrentSourceInfo, ChangeType.WillChange); + + _CurrentSourceInfo = value; + + // add to in-use tracking + if (_CurrentSourceInfo != null && _CurrentSourceInfo.SourceDevice is IInUseTracking) + (_CurrentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control"); + if (handler != null) + handler(_CurrentSourceInfo, ChangeType.DidChange); + + var vc = VideoCodec as IHasExternalSourceSwitching; + if (vc != null && !_codecExternalSourceChange) + { + vc.SetSelectedSource(CurrentSourceInfoKey); + } + + _codecExternalSourceChange = false; + } + } + SourceListItem _CurrentSourceInfo; + + public string CurrentSourceInfoKey { get; set; } + + /// + /// "codecOsd" + /// + public string DefaultCodecRouteString { get { return "codecOsd"; } } + + /// + /// Temporary implementation. Returns the schedule-ready object or null if none. Fow now, + /// always returns the VideoCodec if it is capable + /// + public IHasScheduleAwareness ScheduleSource { get { return VideoCodec as IHasScheduleAwareness; } } + + CCriticalSection SourceSelectLock = new CCriticalSection(); + + public EssentialsCombinedHuddleVtc1Room(DeviceConfig config) + : base(config) + { + try + { + PropertiesConfig = JsonConvert.DeserializeObject + (config.Properties.ToString()); + + 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) + Debug.Console(0, this, "No Audio Codec Found"); + + DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IBasicVolumeControls; + + Displays = new List(); + + Initialize(); + } + catch (Exception e) + { + Debug.Console(1, this, "Error building room: \n{0}", e); + } + } + + void Initialize() + { + try + { + if (DefaultAudioDevice is IBasicVolumeControls) + DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls; + else if (DefaultAudioDevice is IHasVolumeDevice) + DefaultVolumeControls = (DefaultAudioDevice as IHasVolumeDevice).VolumeDevice; + CurrentVolumeControls = DefaultVolumeControls; + + + // Combines call feedback from both codecs if available + InCallFeedback = new BoolFeedback(() => + { + bool inAudioCall = false; + bool inVideoCall = false; + + if (AudioCodec != null) + inAudioCall = AudioCodec.IsInCall; + + if (VideoCodec != null) + inVideoCall = VideoCodec.IsInCall; + + if (inAudioCall || inVideoCall) + return true; + else + return false; + }); + + SetupDisplays(); + + // Get Microphone Privacy object, if any MUST HAPPEN AFTER setting InCallFeedback + this.MicrophonePrivacy = EssentialsRoomConfigHelper.GetMicrophonePrivacy(PropertiesConfig, this); + + Debug.Console(2, this, "Microphone Privacy Config evaluated."); + + // Get emergency object, if any + this.Emergency = EssentialsRoomConfigHelper.GetEmergency(PropertiesConfig, this); + + Debug.Console(2, this, "Emergency Config evaluated."); + + + VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); + VideoCodec.IsReadyChange += (o, a) => { this.SetCodecExternalSources(); SetCodecBranding(); }; + + if (AudioCodec != null) + AudioCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate(); + + IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue); + VideoCodec.SharingContentIsOnFeedback.OutputChange += (o, a) => this.IsSharingFeedback.FireUpdate(); + + // link privacy to VC (for now?) + PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue); + VideoCodec.PrivacyModeIsOnFeedback.OutputChange += (o, a) => this.PrivacyModeIsOnFeedback.FireUpdate(); + + CallTypeFeedback = new IntFeedback(() => 0); + + SetSourceListKey(); + + EnablePowerOnToLastSource = true; + } + catch (Exception e) + { + Debug.Console(0, this, "Error Initializing Room: {0}", e); + } + } + + private void SetupDisplays() + { + //DefaultDisplay = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultDisplayKey) as IRoutingSinkWithSwitching; + + var destinationList = ConfigReader.ConfigObject.DestinationLists[PropertiesConfig.DestinationListKey]; + + foreach (var destination in destinationList) + { + var dest = destination.Value.SinkDevice as IRoutingSinkWithSwitching; + + if (dest != null) + { + Displays.Add(dest); + } + + var display = dest as DisplayBase; + if (display != null) + { + // Link power, warming, cooling to display + var dispTwoWay = display as IHasPowerControlWithFeedback; + if (dispTwoWay != null) + { + dispTwoWay.PowerIsOnFeedback.OutputChange += (o, a) => + { + if (dispTwoWay.PowerIsOnFeedback.BoolValue != OnFeedback.BoolValue) + { + //if (!dispTwoWay.PowerIsOnFeedback.BoolValue) + // CurrentSourceInfo = null; + OnFeedback.FireUpdate(); + } + if (dispTwoWay.PowerIsOnFeedback.BoolValue) + { + SetDefaultLevels(); + } + }; + } + + display.IsWarmingUpFeedback.OutputChange += (o, a) => + { + IsWarmingUpFeedback.FireUpdate(); + if (!IsWarmingUpFeedback.BoolValue) + (CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(DefaultVolume); + }; + display.IsCoolingDownFeedback.OutputChange += (o, a) => + { + IsCoolingDownFeedback.FireUpdate(); + }; + + } + } + } + + private void SetSourceListKey() + { + if (!string.IsNullOrEmpty(PropertiesConfig.SourceListKey)) + { + SetSourceListKey(PropertiesConfig.SourceListKey); + } + else + { + SetSourceListKey(Key); + } + + SetCodecExternalSources(); + } + + protected override void CustomSetConfig(DeviceConfig config) + { + var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString()); + + if (newPropertiesConfig != null) + PropertiesConfig = newPropertiesConfig; + + ConfigWriter.UpdateRoomConfig(config); + } + + public override bool CustomActivate() + { + // Add Occupancy object from config + if (PropertiesConfig.Occupancy != null) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Setting Occupancy Provider for room"); + this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as + IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); + } + + this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); + this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); + + this.DefaultSourceItem = PropertiesConfig.DefaultSourceItem; + this.DefaultVolume = (ushort)(PropertiesConfig.Volumes.Master.Level * 65535 / 100); + + return base.CustomActivate(); + } + + /// + /// + /// + protected override void EndShutdown() + { + VideoCodec.EndAllCalls(); + + SetDefaultLevels(); + + RunDefaultPresentRoute(); + + CrestronEnvironment.Sleep(1000); + + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Shutting down room"); + + RunRouteAction("roomOff"); + VideoCodec.StopSharing(); + VideoCodec.StandbyActivate(); + } + + /// + /// Routes the default source item, if any. Returns true when default route exists + /// + public override bool RunDefaultPresentRoute() + { + if (DefaultSourceItem != null) + RunRouteAction(DefaultSourceItem); + + return DefaultSourceItem != null; + } + + /// + /// Sets up the room when started into call mode without presenting a source + /// + /// + public bool RunDefaultCallRoute() + { + RunRouteAction(DefaultCodecRouteString); + return true; + } + + public void RunRouteActionCodec(string routeKey, string sourceListKey) + { + _codecExternalSourceChange = true; + RunRouteAction(routeKey, sourceListKey); + } + + /// + /// + /// + /// + public void RunRouteAction(string routeKey) + { + RunRouteAction(routeKey, new Action(() => { })); + } + + /// + /// + /// + /// + /// + /// + public void RunRouteAction(string routeKey, string sourceListKey) + { + if (string.IsNullOrEmpty(sourceListKey)) + { + Debug.Console(1, this, "No sourceListKey present. RunRouteAction assumes default source list."); + RunRouteAction(routeKey, new Action(() => { })); + } + else + { + Debug.Console(1, this, "sourceListKey present but not yet implemented"); + throw new NotImplementedException(); + } + } + + /// + /// + /// + /// + /// + /// + public void RunRouteAction(string routeKey, string sourceListKey, Action successCallback) + { + if (string.IsNullOrEmpty(sourceListKey)) + { + RunRouteAction(routeKey, successCallback); + } + else + throw new NotImplementedException(); + } + + + /// + /// Gets a source from config list SourceListKey and dynamically build and executes the + /// route or commands + /// + /// + public void RunRouteAction(string routeKey, Action successCallback) + { + // Run this on a separate thread + new CTimer(o => + { + // try to prevent multiple simultaneous selections + SourceSelectLock.TryEnter(); + + try + { + + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Run route action '{0}'", routeKey); + var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey); + if (dict == null) + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey); + return; + } + + // Try to get the list item by it's string key + if (!dict.ContainsKey(routeKey)) + { + Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'", + routeKey, SourceListKey); + return; + } + + // End usage timer on last source + if (!string.IsNullOrEmpty(LastSourceKey)) + { + var usageLastSource = dict[LastSourceKey].SourceDevice as IUsageTracking; + if (usageLastSource != null && usageLastSource.UsageTracker != null) + { + try + { + // There MAY have been failures in here. Protect + usageLastSource.UsageTracker.EndDeviceUsage(); + } + catch (Exception e) + { + Debug.Console(1, this, "*#* EXCEPTION in end usage tracking:\r{0}", e); + } + } + } + + // Let's run it + var item = dict[routeKey]; + if (routeKey.ToLower() != "roomoff") + { + + LastSourceKey = routeKey; + } + else + CurrentSourceInfoKey = null; + + // hand off the individual routes to this helper + foreach (var route in item.RouteList) + DoRouteItem(route); + + // Start usage timer on routed source + var usageNewSource = item.SourceDevice as IUsageTracking; + if (usageNewSource != null && usageNewSource.UsageTracker != null) // Have to make sure there is a usage tracker! + { + (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); + } + + // See if this can be moved into common, base-class method ------------- + + + // Set volume control, using default if non provided + IBasicVolumeControls volDev = null; + // Handle special cases for volume control + if (string.IsNullOrEmpty(item.VolumeControlKey) + || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase)) + volDev = DefaultVolumeControls; + //else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + // volDev = DefaultDisplay as IBasicVolumeControls; + // Or a specific device, probably rarely used. + else + { + var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey); + if (dev is IBasicVolumeControls) + volDev = dev as IBasicVolumeControls; + else if (dev is IHasVolumeDevice) + volDev = (dev as IHasVolumeDevice).VolumeDevice; + } + + if (volDev != CurrentVolumeControls) + { + // zero the volume on the device we are leaving. + // Set the volume to default on device we are entering + if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback) + { + var vd = CurrentVolumeControls as IBasicVolumeWithFeedback; + SavedVolumeLevels[vd] = (uint)vd.VolumeLevelFeedback.IntValue; + vd.SetVolume(0); + } + + CurrentVolumeControls = volDev; + if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback) + { + var vd = CurrentVolumeControls as IBasicVolumeWithFeedback; + ushort vol = (SavedVolumeLevels.ContainsKey(vd) ? (ushort)SavedVolumeLevels[vd] : DefaultVolume); + vd.SetVolume(vol); + } + } + // ----------------------------------------------------------------------- + + + + // store the name and UI info for routes + if (item.SourceKey == "$off") + { + CurrentSourceInfoKey = routeKey; + CurrentSourceInfo = null; + } + else if (item.SourceKey != null) + { + CurrentSourceInfoKey = routeKey; + CurrentSourceInfo = item; + } + + OnFeedback.FireUpdate(); + + if (OnFeedback.BoolValue) + { + if (VideoCodec.UsageTracker.InUseTracker.InUseFeedback.BoolValue) + { + Debug.Console(1, this, "Video Codec in use, deactivating standby on codec"); + VideoCodec.StandbyDeactivate(); + } + + if (VideoCodec.StandbyIsOnFeedback.BoolValue) + { + VideoCodec.StandbyDeactivate(); + } + else + { + Debug.Console(1, this, "Video codec not in standby. No need to wake."); + } + } + else + { + Debug.Console(1, this, "Room OnFeedback state: {0}", OnFeedback.BoolValue); + } + + // report back when done + if (successCallback != null) + successCallback(); + } + catch (Exception e) + { + Debug.Console(1, this, "ERROR in routing: {0}", e); + } + + SourceSelectLock.Leave(); + }, 0); // end of CTimer + } + + /// + /// + /// + /// + void DoRouteItem(SourceRouteListItem route) + { + // if there is a $defaultAll on route, run two separate + if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase)) + { + foreach (var display in Displays) + { + var tempVideo = new SourceRouteListItem + { + DestinationKey = display.Key, + SourceKey = route.SourceKey, + Type = eRoutingSignalType.Video + }; + DoRoute(tempVideo); + } + } + else + DoRoute(route); + } + + /// + /// + /// + /// + /// + bool DoRoute(SourceRouteListItem route) + { + IRoutingSink dest = null; + + if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) + dest = DefaultAudioDevice as IRoutingSink; + //else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + // dest = DefaultDisplay; + else + dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSink; + + if (dest == null) + { + Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey); + return false; + } + + if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase)) + { + dest.ReleaseRoute(); + if (dest is IHasPowerControl) + (dest as IHasPowerControl).PowerOff(); + + } + else + { + var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs; + if (source == null) + { + Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey); + return false; + } + dest.ReleaseAndMakeRoute(source, route.Type); + } + return true; + } + + public override void RoomVacatedForTimeoutPeriod(object o) + { + //Implement this + } + + /// + /// Does what it says + /// + public override void SetDefaultLevels() + { + Debug.Console(1, this, "Restoring default levels"); + var vc = CurrentVolumeControls as IBasicVolumeWithFeedback; + if (vc != null) + vc.SetVolume(DefaultVolume); + } + /// + /// Will power the room on with the last-used source + /// + public override void PowerOnToDefaultOrLastSource() + { + if (!EnablePowerOnToLastSource || LastSourceKey == null) + return; + RunRouteAction(LastSourceKey); + } + + /// + /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions + /// + public static void AllRoomsOff() + { + var allRooms = DeviceManager.AllDevices.Where(d => + d is IEssentialsRoom && !(d as IEssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); + foreach (var room in allRooms) + (room as IEssentialsHuddleSpaceRoom).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 || !videoCodecWithExternalSwitching.ExternalSourceListEnabled) + { + return; + } + + try + { + // Get the tie line that the external switcher is connected to + string codecInputConnectorName = ConfigReader.ConfigObject.TieLines.SingleOrDefault( + x => x.DestinationKey == VideoCodec.Key && x.DestinationPort == videoCodecWithExternalSwitching.ExternalSourceInputPort).DestinationPort; + + videoCodecWithExternalSwitching.ClearExternalSources(); + videoCodecWithExternalSwitching.RunRouteAction = RunRouteActionCodec; + 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(codecInputConnectorName, kvp.Key, srcConfig.PreferredName, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceType.desktop); + videoCodecWithExternalSwitching.SetExternalSourceState(kvp.Key, PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eExternalSourceMode.Ready); + } + } + } + catch (Exception e) + { + Debug.Console(2, this, "Error setting codec external sources: {0}", e); + } + } + + private void SetCodecBranding() + { + var vcWithBranding = VideoCodec as IHasBranding; + + if (vcWithBranding == null) return; + + Debug.Console(1, this, "Setting Codec Branding"); + vcWithBranding.InitializeBranding(Key); + } + + #region IPrivacy Members + + + public void PrivacyModeOff() + { + VideoCodec.PrivacyModeOff(); + } + + public void PrivacyModeOn() + { + VideoCodec.PrivacyModeOn(); + } + + public void PrivacyModeToggle() + { + VideoCodec.PrivacyModeToggle(); + } + + #endregion + + } +} \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index a6563491..ba4567dc 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -210,7 +210,7 @@ namespace PepperDash.Essentials { PropertiesConfig = JsonConvert.DeserializeObject (config.Properties.ToString()); - DefaultDisplay = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultDisplayKey) as IRoutingSinkWithSwitching; + DefaultDisplay = DeviceManager.GetDeviceForKey((PropertiesConfig as EssentialsHuddleVtc1PropertiesConfig).DefaultDisplayKey) as IRoutingSinkWithSwitching; VideoCodec = DeviceManager.GetDeviceForKey(PropertiesConfig.VideoCodecKey) as PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase; diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index c2b08f89..b97ef9c4 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -19,7 +19,7 @@ namespace PepperDash.Essentials { public class EssentialsTechRoom : EssentialsRoomBase, ITvPresetsProvider, IBridgeAdvanced, IRunDirectRouteAction { - private readonly EssentialsTechRoomConfig _config; + public EssentialsTechRoomConfig PropertiesConfig { get; private set; } private readonly Dictionary _displays; private readonly DevicePresetsModel _tunerPresets; @@ -57,16 +57,16 @@ namespace PepperDash.Essentials public EssentialsTechRoom(DeviceConfig config) : base(config) { - _config = config.Properties.ToObject(); + PropertiesConfig = config.Properties.ToObject(); - _tunerPresets = new DevicePresetsModel(String.Format("{0}-presets", config.Key), _config.PresetsFileName); + _tunerPresets = new DevicePresetsModel(String.Format("{0}-presets", config.Key), PropertiesConfig.PresetsFileName); - _tunerPresets.SetFileName(_config.PresetsFileName); + _tunerPresets.SetFileName(PropertiesConfig.PresetsFileName); _tunerPresets.PresetRecalled += TunerPresetsOnPresetRecalled; - _tuners = GetDevices(_config.Tuners); - _displays = GetDevices(_config.Displays); + _tuners = GetDevices(PropertiesConfig.Tuners); + _displays = GetDevices(PropertiesConfig.Displays); RoomPowerIsOnFeedback = new BoolFeedback(() => RoomPowerIsOn); @@ -153,7 +153,7 @@ namespace PepperDash.Essentials private void CreateOrUpdateScheduledEvents() { - var eventsConfig = _config.ScheduledEvents; + var eventsConfig = PropertiesConfig.ScheduledEvents; GetOrCreateScheduleGroup(); @@ -207,21 +207,21 @@ namespace PepperDash.Essentials { //update config based on key of scheduleEvent GetOrCreateScheduleGroup(); - var existingEventIndex = _config.ScheduledEvents.FindIndex((e) => e.Key == scheduledEvent.Key); + var existingEventIndex = PropertiesConfig.ScheduledEvents.FindIndex((e) => e.Key == scheduledEvent.Key); if (existingEventIndex < 0) { - _config.ScheduledEvents.Add(scheduledEvent); + PropertiesConfig.ScheduledEvents.Add(scheduledEvent); } else { - _config.ScheduledEvents[existingEventIndex] = scheduledEvent; + PropertiesConfig.ScheduledEvents[existingEventIndex] = scheduledEvent; } //create or update event based on config CreateOrUpdateSingleEvent(scheduledEvent); //save config - Config.Properties = JToken.FromObject(_config); + Config.Properties = JToken.FromObject(PropertiesConfig); CustomSetConfig(Config); //Fire Event @@ -230,7 +230,7 @@ namespace PepperDash.Essentials public List GetScheduledEvents() { - return _config.ScheduledEvents ?? new List(); + return PropertiesConfig.ScheduledEvents ?? new List(); } private void OnScheduledEventUpdate() @@ -242,14 +242,14 @@ namespace PepperDash.Essentials return; } - handler(this, new ScheduledEventEventArgs {ScheduledEvents = _config.ScheduledEvents}); + handler(this, new ScheduledEventEventArgs {ScheduledEvents = PropertiesConfig.ScheduledEvents}); } public event EventHandler ScheduledEventsChanged; private void HandleScheduledEvent(ScheduledEvent schevent, ScheduledEventCommon.eCallbackReason type) { - var eventConfig = _config.ScheduledEvents.FirstOrDefault(e => e.Key == schevent.Name); + var eventConfig = PropertiesConfig.ScheduledEvents.FirstOrDefault(e => e.Key == schevent.Name); if (eventConfig == null) { @@ -286,11 +286,11 @@ Params: {2}" { Debug.Console(2, this, "Room Powering On"); - var dummySource = DeviceManager.GetDeviceForKey(_config.DummySourceKey) as IRoutingOutputs; + var dummySource = DeviceManager.GetDeviceForKey(PropertiesConfig.DummySourceKey) as IRoutingOutputs; if (dummySource == null) { - Debug.Console(1, this, "Unable to get source with key: {0}", _config.DummySourceKey); + Debug.Console(1, this, "Unable to get source with key: {0}", PropertiesConfig.DummySourceKey); return; } @@ -376,12 +376,12 @@ Params: {2}" bridge.AddJoinMap(Key, joinMap); } - if (_config.IsPrimary) + if (PropertiesConfig.IsPrimary) { Debug.Console(1, this, "Linking Primary system Tuner Preset Mirroring"); - if (_config.MirroredTuners != null && _config.MirroredTuners.Count > 0) + if (PropertiesConfig.MirroredTuners != null && PropertiesConfig.MirroredTuners.Count > 0) { - foreach (var tuner in _config.MirroredTuners) + foreach (var tuner in PropertiesConfig.MirroredTuners) { var f = CurrentPresetsFeedbacks[tuner.Value]; @@ -423,9 +423,9 @@ Params: {2}" { Debug.Console(1, this, "Linking Secondary system Tuner Preset Mirroring"); - if (_config.MirroredTuners != null && _config.MirroredTuners.Count > 0) + if (PropertiesConfig.MirroredTuners != null && PropertiesConfig.MirroredTuners.Count > 0) { - foreach (var tuner in _config.MirroredTuners) + foreach (var tuner in PropertiesConfig.MirroredTuners) { var t = _tuners[tuner.Value]; diff --git a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs new file mode 100644 index 00000000..c7f68e8b --- /dev/null +++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs @@ -0,0 +1,24 @@ +using System; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Config; + + + +namespace PepperDash.Essentials +{ + public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasDefaultDisplay + { + bool ExcludeFromGlobalFunctions { get; } + + void RunRouteAction(string routeKey); + + EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; } + + IBasicVolumeControls CurrentVolumeControls { get; } + + event EventHandler CurrentVolumeDeviceChange; + } + + +} \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs new file mode 100644 index 00000000..03f7340b --- /dev/null +++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs @@ -0,0 +1,27 @@ + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Config; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.AudioCodec; + +namespace PepperDash.Essentials +{ + public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange, + IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback + { + EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; } + + bool ExcludeFromGlobalFunctions { get; } + + void RunRouteAction(string routeKey); + + IHasScheduleAwareness ScheduleSource { get; } + + new BoolFeedback InCallFeedback { get; } + + new BoolFeedback PrivacyModeIsOnFeedback { get; } + + string DefaultCodecRouteString { get; } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs index 72e22b9c..441381bd 100644 --- a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs +++ b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs @@ -16,6 +16,8 @@ namespace PepperDash.Essentials { public class EssentialsTouchpanelController : EssentialsDevice, IHasBasicTriListWithSmartObject { + private CrestronTouchpanelPropertiesConfig _propertiesConfig; + public BasicTriListWithSmartObject Panel { get; private set; } public PanelDriverBase PanelDriver { get; private set; } @@ -27,7 +29,14 @@ namespace PepperDash.Essentials : base(key, name) { Panel = tsw; - tsw.LoadSmartObjects(sgdPath); + + if (!string.IsNullOrEmpty(sgdPath)) + Panel.LoadSmartObjects(sgdPath); + else + Debug.Console(1, this, "No SGD file path defined"); + + + tsw.SigChange += Panel_SigChange; } @@ -37,7 +46,7 @@ namespace PepperDash.Essentials Panel = dge; if (!string.IsNullOrEmpty(sgdPath)) - dge.LoadSmartObjects(sgdPath); + Panel.LoadSmartObjects(sgdPath); else Debug.Console(1, this, "No SGD file path defined"); @@ -50,6 +59,7 @@ namespace PepperDash.Essentials public EssentialsTouchpanelController(string key, string name, string type, CrestronTouchpanelPropertiesConfig props, uint id) : base(key, name) { + _propertiesConfig = props; Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating touchpanel hardware..."); type = type.ToLower(); @@ -61,6 +71,8 @@ namespace PepperDash.Essentials app.ParameterProjectName.Value = props.ProjectName; Panel = app; } + else if (type == "xpanel") + Panel = new XpanelForSmartGraphics(id, Global.ControlSystem); else if (type == "tsw550") Panel = new Tsw550(id, Global.ControlSystem); else if (type == "tsw552") @@ -134,10 +146,194 @@ namespace PepperDash.Essentials Panel.LoadSmartObjects(sgdName); Panel.SigChange += Panel_SigChange; + AddPostActivationAction(() => + { + // Check for IEssentialsRoomCombiner in DeviceManager and if found, subscribe to its event + var roomCombiner = DeviceManager.AllDevices.FirstOrDefault((d) => d is IEssentialsRoomCombiner) as IEssentialsRoomCombiner; + + if (roomCombiner != null) + { + // Subscribe to the even + roomCombiner.RoomCombinationScenarioChanged += new EventHandler(roomCombiner_RoomCombinationScenarioChanged); + + // Connect to the initial roomKey + if (roomCombiner.CurrentScenario != null) + { + // Use the current scenario + DetermineRoomKeyFromScenario(roomCombiner.CurrentScenario); + } + else + { + // Current Scenario not yet set. Use default + SetupPanelDrivers(_propertiesConfig.DefaultRoomKey); + } + } + else + { + // No room combiner, use the default key + SetupPanelDrivers(_propertiesConfig.DefaultRoomKey); + } + }); } + void roomCombiner_RoomCombinationScenarioChanged(object sender, EventArgs e) + { + var roomCombiner = sender as IEssentialsRoomCombiner; + + DetermineRoomKeyFromScenario(roomCombiner.CurrentScenario); + } + + /// + /// Determines the room key to use based on the scenario + /// + /// + void DetermineRoomKeyFromScenario(IRoomCombinationScenario scenario) + { + string newRoomKey = null; + + if (scenario.UiMap.ContainsKey(Key)) + { + newRoomKey = scenario.UiMap[Key]; + } + else if (scenario.UiMap.ContainsKey(_propertiesConfig.DefaultRoomKey)) + { + newRoomKey = scenario.UiMap[_propertiesConfig.DefaultRoomKey]; + } + + SetupPanelDrivers(newRoomKey); + } + + + /// + /// Sets up drivers and links them to the room specified + /// + /// key of room to link the drivers to + void SetupPanelDrivers(string roomKey) + { + // Clear out any existing actions + Panel.ClearAllSigActions(); + + Debug.Console(0, this, "Linking TP '{0}' to Room '{1}'", Key, roomKey); + + var mainDriver = new EssentialsPanelMainInterfaceDriver(Panel, _propertiesConfig); + // Then the sub drivers + + // spin up different room drivers depending on room type + var room = DeviceManager.GetDeviceForKey(roomKey); + if (room is IEssentialsHuddleSpaceRoom) + { + // Screen Saver Driver + + mainDriver.ScreenSaverController = new ScreenSaverController(mainDriver, _propertiesConfig); + + // Header Driver + Debug.Console(0, this, "Adding header driver"); + mainDriver.HeaderDriver = new EssentialsHeaderDriver(mainDriver, _propertiesConfig); + + // AV Driver + Debug.Console(0, this, "Adding huddle space AV driver"); + var avDriver = new EssentialsHuddlePanelAvFunctionsDriver(mainDriver, _propertiesConfig); + avDriver.DefaultRoomKey = roomKey; + mainDriver.AvDriver = avDriver; + avDriver.CurrentRoom = room as IEssentialsHuddleSpaceRoom; + + // Environment Driver + if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0) + { + Debug.Console(0, this, "Adding environment driver"); + mainDriver.EnvironmentDriver = new EssentialsEnvironmentDriver(mainDriver, _propertiesConfig); + + mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.PropertiesConfig.Environment); + } + + mainDriver.HeaderDriver.SetupHeaderButtons(avDriver, avDriver.CurrentRoom); + + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + // Wire up hard keys + tsw.Power.UserObject = new Action(b => { if (!b) avDriver.PowerButtonPressed(); }); + if (mainDriver.EnvironmentDriver != null) + tsw.Lights.UserObject = new Action(b => + { + if (!b) + { + mainDriver.EnvironmentDriver.Toggle(); + } + }); + tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); + tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); + } + } + else if (room is IEssentialsHuddleVtc1Room) + { + Debug.Console(0, this, "Adding huddle space VTC AV driver"); + + // Screen Saver Driver + mainDriver.ScreenSaverController = new ScreenSaverController(mainDriver, _propertiesConfig); + + // Header Driver + mainDriver.HeaderDriver = new EssentialsHeaderDriver(mainDriver, _propertiesConfig); + + // AV Driver + var avDriver = new EssentialsHuddleVtc1PanelAvFunctionsDriver(mainDriver, _propertiesConfig); + + var codecDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver(Panel, avDriver, + (room as IEssentialsHuddleVtc1Room).VideoCodec, mainDriver.HeaderDriver); + avDriver.SetVideoCodecDriver(codecDriver); + avDriver.DefaultRoomKey = roomKey; + mainDriver.AvDriver = avDriver; + avDriver.CurrentRoom = room as IEssentialsHuddleVtc1Room; + + // Environment Driver + if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0) + { + Debug.Console(0, this, "Adding environment driver"); + mainDriver.EnvironmentDriver = new EssentialsEnvironmentDriver(mainDriver, _propertiesConfig); + + mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.PropertiesConfig.Environment); + } + + mainDriver.HeaderDriver.SetupHeaderButtons(avDriver, avDriver.CurrentRoom); + + + if (Panel is TswFt5ButtonSystem) + { + var tsw = Panel as TswFt5ButtonSystem; + // Wire up hard keys + tsw.Power.UserObject = new Action(b => { if (!b) avDriver.EndMeetingPress(); }); + if (mainDriver.EnvironmentDriver != null) + tsw.Lights.UserObject = new Action(b => + { + if (!b) + { + mainDriver.EnvironmentDriver.Toggle(); + } + }); + tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); + tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); + } + + LoadAndShowDriver(mainDriver); + } + else + { + Debug.Console(0, this, "ERROR: Cannot load AvFunctionsDriver for room '{0}'", roomKey); + } + + } + public void LoadAndShowDriver(PanelDriverBase driver) { + if (PanelDriver != null) + { + var mainDriver = PanelDriver as EssentialsPanelMainInterfaceDriver; + if (mainDriver != null) + { + mainDriver.Dispose(); + } + } + PanelDriver = driver; driver.Show(); } @@ -148,7 +344,6 @@ namespace PepperDash.Essentials PanelDriver.BackButtonPressed(); } - void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) { // If the sig is transitioning on, mark it in case it was home button that transitioned it @@ -225,119 +420,6 @@ namespace PepperDash.Essentials var panelController = new EssentialsTouchpanelController(dc.Key, dc.Name, dc.Type, props, comm.IpIdInt); - panelController.AddPostActivationAction(() => - { - var mainDriver = new EssentialsPanelMainInterfaceDriver(panelController.Panel, props); - // Then the sub drivers - - // spin up different room drivers depending on room type - var room = DeviceManager.GetDeviceForKey(props.DefaultRoomKey); - if (room is IEssentialsHuddleSpaceRoom) - { - // Screen Saver Driver - mainDriver.ScreenSaverController = new ScreenSaverController(mainDriver, props); - - // Header Driver - Debug.Console(0, panelController, "Adding header driver"); - mainDriver.HeaderDriver = new EssentialsHeaderDriver(mainDriver, props); - - // AV Driver - Debug.Console(0, panelController, "Adding huddle space AV driver"); - var avDriver = new EssentialsHuddlePanelAvFunctionsDriver(mainDriver, props); - avDriver.DefaultRoomKey = props.DefaultRoomKey; - mainDriver.AvDriver = avDriver; - avDriver.CurrentRoom = room as IEssentialsHuddleSpaceRoom; - - // Environment Driver - if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0) - { - Debug.Console(0, panelController, "Adding environment driver"); - mainDriver.EnvironmentDriver = new EssentialsEnvironmentDriver(mainDriver, props); - - mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.PropertiesConfig.Environment); - } - - mainDriver.HeaderDriver.SetupHeaderButtons(avDriver, avDriver.CurrentRoom); - - panelController.LoadAndShowDriver(mainDriver); // This is a little convoluted. - - if (panelController.Panel is TswFt5ButtonSystem) - { - var tsw = panelController.Panel as TswFt5ButtonSystem; - // Wire up hard keys - tsw.Power.UserObject = new Action(b => { if (!b) avDriver.PowerButtonPressed(); }); - //tsw.Home.UserObject = new Action(b => { if (!b) HomePressed(); }); - if (mainDriver.EnvironmentDriver != null) - tsw.Lights.UserObject = new Action(b => - { - if (!b) - { - //mainDriver.AvDriver.PopupInterlock.ShowInterlockedWithToggle(mainDriver.EnvironmentDriver.BackgroundSubpageJoin); - mainDriver.EnvironmentDriver.Toggle(); - } - }); - tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); - tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); - } - } - else if (room is IEssentialsHuddleVtc1Room) - { - 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); - - // AV Driver - var avDriver = new EssentialsHuddleVtc1PanelAvFunctionsDriver(mainDriver, props); - - var codecDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver(panelController.Panel, avDriver, - (room as IEssentialsHuddleVtc1Room).VideoCodec, mainDriver.HeaderDriver); - avDriver.SetVideoCodecDriver(codecDriver); - avDriver.DefaultRoomKey = props.DefaultRoomKey; - mainDriver.AvDriver = avDriver; - avDriver.CurrentRoom = room as IEssentialsHuddleVtc1Room; - - // Environment Driver - if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0) - { - Debug.Console(0, panelController, "Adding environment driver"); - mainDriver.EnvironmentDriver = new EssentialsEnvironmentDriver(mainDriver, props); - - mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.PropertiesConfig.Environment); - } - - mainDriver.HeaderDriver.SetupHeaderButtons(avDriver, avDriver.CurrentRoom); - - panelController.LoadAndShowDriver(mainDriver); // This is a little convoluted. - - if (panelController.Panel is TswFt5ButtonSystem) - { - var tsw = panelController.Panel as TswFt5ButtonSystem; - // Wire up hard keys - tsw.Power.UserObject = new Action(b => { if (!b) avDriver.EndMeetingPress(); }); - //tsw.Home.UserObject = new Action(b => { if (!b) HomePressed(); }); - if (mainDriver.EnvironmentDriver != null) - tsw.Lights.UserObject = new Action(b => - { - if (!b) - { - //mainDriver.AvDriver.PopupInterlock.ShowInterlockedWithToggle(mainDriver.EnvironmentDriver.BackgroundSubpageJoin); - mainDriver.EnvironmentDriver.Toggle(); - } - }); - tsw.Up.UserObject = new Action(avDriver.VolumeUpPress); - tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); - } - } - else - { - Debug.Console(0, panelController, "ERROR: Cannot load AvFunctionsDriver for room '{0}'", props.DefaultRoomKey); - } - }); - return panelController; } } diff --git a/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs b/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs index 3e1869cc..27409007 100644 --- a/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs +++ b/PepperDashEssentials/UI/SubpageReferenceListSourceItem.cs @@ -14,6 +14,8 @@ namespace PepperDash.Essentials { public SourceListItem SourceItem { get; private set; } + private IHasCurrentSourceInfoChange _room; + public SubpageReferenceListSourceItem(uint index, SubpageReferenceList owner, SourceListItem sourceItem, Action routeAction) : base(index, owner) @@ -25,6 +27,7 @@ namespace PepperDash.Essentials public void RegisterForSourceChange(IHasCurrentSourceInfoChange room) { + _room = room; room.CurrentSourceChange -= room_CurrentSourceInfoChange; room.CurrentSourceChange += room_CurrentSourceInfoChange; } @@ -44,6 +47,9 @@ namespace PepperDash.Essentials { Owner.BoolInputSig(Index, 1).UserObject = null; Owner.StringInputSig(Index, 1).StringValue = ""; + + if(_room != null) + _room.CurrentSourceChange -= room_CurrentSourceInfoChange; } /// diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs index 9b92fc41..2b787063 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPanelMainInterfaceDriver.cs @@ -11,7 +11,7 @@ namespace PepperDash.Essentials /// /// /// - public class EssentialsPanelMainInterfaceDriver : PanelDriverBase, IHasScreenSaverController + public class EssentialsPanelMainInterfaceDriver : PanelDriverBase, IHasScreenSaverController, IDisposable { CTimer InactivityTimer; @@ -69,6 +69,35 @@ namespace PepperDash.Essentials } } + #region IDisposable Members + + public void Dispose() + { + var avDriver = AvDriver as PanelDriverBase; + if (avDriver != null) + { + avDriver.Hide(); + } + if (ScreenSaverController != null) + { + ScreenSaverController.Dispose(); + } + if (HeaderDriver != null) + { + HeaderDriver.Hide(); + } + if (EnvironmentDriver != null) + { + EnvironmentDriver.Hide(); + } + if (CurrentChildDriver != null) + { + CurrentChildDriver.Hide(); + } + } + + #endregion + void ExtenderTouchDetectionReservedSigs_DeviceExtenderSigChange(Crestron.SimplSharpPro.DeviceExtender currentDeviceExtender, Crestron.SimplSharpPro.SigEventArgs args) { @@ -130,7 +159,7 @@ namespace PepperDash.Essentials if(CurrentChildDriver != null) CurrentChildDriver.BackButtonPressed(); } - } + } public interface IHasScreenSaverController { diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs index 78a06301..e58172de 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs @@ -306,7 +306,7 @@ namespace PepperDash.Essentials TriList.SetSigFalseAction(UIBoolJoin.DisplayPowerTogglePress, () => { - if (CurrentRoom != null && CurrentRoom.DefaultDisplay is IHasPowerControl) + if (CurrentRoom != null && CurrentRoom.DefaultDisplay != null && CurrentRoom.DefaultDisplay is IHasPowerControl) (CurrentRoom.DefaultDisplay as IHasPowerControl).PowerToggle(); }); diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs index 32d8ebe4..57aed03e 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs @@ -1,326 +1,326 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.DeviceSupport; - -using PepperDash.Core; -using PepperDash.Essentials; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.SmartObjects; -using PepperDash.Essentials.Core.Touchpanels.Keyboards; -using PepperDash.Essentials.Devices.Displays; -using PepperDash.Essentials.Room.Config; - -namespace PepperDash.Essentials.UIDrivers -{ - public class EssentialsHuddleTechPageDriver : PanelDriverBase - { - /// - /// - /// - SmartObjectDynamicList MenuList; - /// - /// - /// - SubpageReferenceList StatusList; - /// - /// The list of display controls - /// - SubpageReferenceList DisplayList; - /// - /// References lines in the list against device instances - /// - Dictionary StatusListDeviceIndexes; - /// - /// - /// - JoinedSigInterlock PagesInterlock; - - /// - /// 1 - /// - public const uint JoinText = 1; - - CTimer PinAuthorizedTimer; - - EssentialsRoomTechConfig Config; - - StringBuilder PinEntryBuilder = new StringBuilder(4); - - bool IsAuthorized; - - SmartObjectNumeric PinKeypad; - - /// - /// - /// - /// - /// - public EssentialsHuddleTechPageDriver(BasicTriListWithSmartObject trilist, EssentialsRoomTechConfig config) - : base(trilist) - { - Config = config; - - PagesInterlock = new JoinedSigInterlock(trilist); - PagesInterlock.SetButDontShow(UIBoolJoin.TechSystemStatusVisible); - - trilist.SetSigFalseAction(UIBoolJoin.TechExitButton, Hide); - - MenuList = new SmartObjectDynamicList(trilist.SmartObjects[UISmartObjectJoin.TechMenuList], - true, 3100); - - MenuList.SetFeedback(1, true); // initial fb - ushort count = 0; - - MenuList.SetItemMainText(1, "System Status"); - MenuList.SetItemButtonAction(1, b => { - if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechSystemStatusVisible); - MenuList.SetFeedback(1, true); - }); - - MenuList.SetItemMainText(2, "Display Controls"); - MenuList.SetItemButtonAction(2, b => { - if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechDisplayControlsVisible); - MenuList.SetFeedback(2, true); - }); - - count = 2; - - // Don't show panel setup on iPad or xpanel - if (TriList is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button) - { - count++; - MenuList.SetItemMainText(count, "Panel Setup"); - MenuList.SetItemButtonAction(count, b => - { - if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechPanelSetupVisible); - MenuList.SetFeedback(count, true); - }); - } - - MenuList.Count = count; - BuildStatusList(); - BuildDisplayList(); - SetupPinModal(); - } - - /// - /// - /// - public override void Show() - { - // divert to PIN if we need auth - if (IsAuthorized) - { - // Cancel the auth timer so we don't deauth after coming back in - if (PinAuthorizedTimer != null) - PinAuthorizedTimer.Stop(); - - TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, true); - PagesInterlock.Show(); - base.Show(); - } - else - { - TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, true); - } - } - - /// - /// - /// - public override void Hide() - { - // Leave it authorized for 60 seconds. - if (IsAuthorized) - PinAuthorizedTimer = new CTimer(o => { - IsAuthorized = false; - PinAuthorizedTimer = null; - }, 60000); - TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, false); - PagesInterlock.Hide(); - base.Hide(); - } - - /// - /// Wire up the keypad and buttons - /// - void SetupPinModal() - { - TriList.SetSigFalseAction(UIBoolJoin.PinDialogCancelPress, CancelPinDialog); - PinKeypad = new SmartObjectNumeric(TriList.SmartObjects[UISmartObjectJoin.TechPinDialogKeypad], true); - PinKeypad.Digit0.UserObject = new Action(b => { if (b)DialPinDigit('0'); }); - PinKeypad.Digit1.UserObject = new Action(b => { if (b)DialPinDigit('1'); }); - PinKeypad.Digit2.UserObject = new Action(b => { if (b)DialPinDigit('2'); }); - PinKeypad.Digit3.UserObject = new Action(b => { if (b)DialPinDigit('3'); }); - PinKeypad.Digit4.UserObject = new Action(b => { if (b)DialPinDigit('4'); }); - PinKeypad.Digit5.UserObject = new Action(b => { if (b)DialPinDigit('5'); }); - PinKeypad.Digit6.UserObject = new Action(b => { if (b)DialPinDigit('6'); }); - PinKeypad.Digit7.UserObject = new Action(b => { if (b)DialPinDigit('7'); }); - PinKeypad.Digit8.UserObject = new Action(b => { if (b)DialPinDigit('8'); }); - PinKeypad.Digit9.UserObject = new Action(b => { if (b)DialPinDigit('9'); }); - } - - /// - /// - /// - /// - void DialPinDigit(char d) - { - PinEntryBuilder.Append(d); - var len = PinEntryBuilder.Length; - SetPinDotsFeedback(len); - - // check it! - if (len == 4) - { - if (Config.Password == PinEntryBuilder.ToString()) - { - IsAuthorized = true; - SetPinDotsFeedback(0); - TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false); - Show(); - } - else - { - SetPinDotsFeedback(0); - TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, true); - new CTimer(o => - { - TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, false); - }, 1500); - } - - PinEntryBuilder.Remove(0, len); // clear it either way - } - } - - /// - /// Draws the dots as pin is entered - /// - /// - void SetPinDotsFeedback(int len) - { - TriList.SetBool(UIBoolJoin.PinDialogDot1, len >= 1); - TriList.SetBool(UIBoolJoin.PinDialogDot2, len >= 2); - TriList.SetBool(UIBoolJoin.PinDialogDot3, len >= 3); - TriList.SetBool(UIBoolJoin.PinDialogDot4, len == 4); - - } - - /// - /// Does what it says - /// - void CancelPinDialog() - { - PinEntryBuilder.Remove(0, PinEntryBuilder.Length); - TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false); - } - - - /// - /// - /// - void BuildStatusList() - { - StatusList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechStatusList, 3, 3, 3); - StatusListDeviceIndexes = new Dictionary(); - uint i = 0; - foreach (var d in DeviceManager.AllDevices) - { - // make sure it is both ICommunicationMonitor and a Device - var sd = d as ICommunicationMonitor; - if (sd == null) - continue; - var dd = sd as Device; - if(dd == null) - continue; - i++; - StatusList.StringInputSig(i, 1).StringValue = dd.Name; - StatusList.UShortInputSig(i, 1).UShortValue = (ushort)sd.CommunicationMonitor.Status; - StatusListDeviceIndexes.Add(sd, i); - sd.CommunicationMonitor.StatusChange += CommunicationMonitor_StatusChange ; - } - StatusList.Count = (ushort)i; - } - - /// - /// Builds the list of display controls - /// - void BuildDisplayList() - { - DisplayList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechDisplayControlsList, 10, 3, 3); - - var devKeys = ConfigReader.ConfigObject.Devices.Where(d => - d.Group.Equals("display", StringComparison.OrdinalIgnoreCase) - || d.Group.Equals("projector", StringComparison.OrdinalIgnoreCase)) - .Select(dd => dd.Key); - var disps = DeviceManager.AllDevices.Where(d => - devKeys.Contains(d.Key)); - ushort i = 0; - foreach (var disp in disps) - { - var display = disp as DisplayBase; - if (display != null) - { - i++; - DisplayList.StringInputSig(i, 1).StringValue = display.Name; - DisplayList.GetBoolFeedbackSig(i, 1).SetSigFalseAction(display.PowerOn); - DisplayList.GetBoolFeedbackSig(i, 2).SetSigFalseAction(display.PowerOff); - if (display is TwoWayDisplayBase) - { - var powerOnSig = DisplayList.BoolInputSig(i, 1); - (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkInputSig(powerOnSig); - - var powerOffSig = DisplayList.BoolInputSig(1, 2); - (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkComplementInputSig(powerOffSig); - } - DisplayList.GetBoolFeedbackSig(i, 3).SetSigFalseAction(() => - { if (display is IInputHdmi1) (display as IInputHdmi1).InputHdmi1(); }); - DisplayList.GetBoolFeedbackSig(i, 4).SetSigFalseAction(() => - { if (display is IInputHdmi2) (display as IInputHdmi2).InputHdmi2(); }); - DisplayList.GetBoolFeedbackSig(i, 5).SetSigFalseAction(() => - { if (display is IInputHdmi3) (display as IInputHdmi3).InputHdmi3(); }); - //DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() => - //{ if (display is IInputHdmi4) (display as IInputHdmi4).InputHdmi4(); }); - DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() => - { if (display is IInputDisplayPort1) (display as IInputDisplayPort1).InputDisplayPort1(); }); - - - // Figure out some way to provide current input feedback - if (display is TwoWayDisplayBase) - { - (display as TwoWayDisplayBase).CurrentInputFeedback.OutputChange += CurrentInputFeedback_OutputChange; - } - } - - - } - - DisplayList.Count = i; - } - - - void CurrentInputFeedback_OutputChange(object sender, EventArgs e) - { - - } - - /// - /// - /// - void CommunicationMonitor_StatusChange(object sender, MonitorStatusChangeEventArgs e) - { - var c = sender as ICommunicationMonitor; - if (c != null && StatusListDeviceIndexes.ContainsKey(c)) - { - var i = StatusListDeviceIndexes[c]; - StatusList.UShortInputSig(i, 1).UShortValue = (ushort)e.Status; - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; +using PepperDash.Essentials; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.SmartObjects; +using PepperDash.Essentials.Core.Touchpanels.Keyboards; +using PepperDash.Essentials.Devices.Displays; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials.UIDrivers +{ + public class EssentialsHuddleTechPageDriver : PanelDriverBase + { + /// + /// + /// + SmartObjectDynamicList MenuList; + /// + /// + /// + SubpageReferenceList StatusList; + /// + /// The list of display controls + /// + SubpageReferenceList DisplayList; + /// + /// References lines in the list against device instances + /// + Dictionary StatusListDeviceIndexes; + /// + /// + /// + JoinedSigInterlock PagesInterlock; + + /// + /// 1 + /// + public const uint JoinText = 1; + + CTimer PinAuthorizedTimer; + + EssentialsRoomTechConfig Config; + + StringBuilder PinEntryBuilder = new StringBuilder(4); + + bool IsAuthorized; + + SmartObjectNumeric PinKeypad; + + /// + /// + /// + /// + /// + public EssentialsHuddleTechPageDriver(BasicTriListWithSmartObject trilist, EssentialsRoomTechConfig config) + : base(trilist) + { + Config = config; + + PagesInterlock = new JoinedSigInterlock(trilist); + PagesInterlock.SetButDontShow(UIBoolJoin.TechSystemStatusVisible); + + trilist.SetSigFalseAction(UIBoolJoin.TechExitButton, Hide); + + MenuList = new SmartObjectDynamicList(trilist.SmartObjects[UISmartObjectJoin.TechMenuList], + true, 3100); + + MenuList.SetFeedback(1, true); // initial fb + ushort count = 0; + + MenuList.SetItemMainText(1, "System Status"); + MenuList.SetItemButtonAction(1, b => { + if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechSystemStatusVisible); + MenuList.SetFeedback(1, true); + }); + + MenuList.SetItemMainText(2, "Display Controls"); + MenuList.SetItemButtonAction(2, b => { + if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechDisplayControlsVisible); + MenuList.SetFeedback(2, true); + }); + + count = 2; + + // Don't show panel setup on iPad or xpanel + if (TriList is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button) + { + count++; + MenuList.SetItemMainText(count, "Panel Setup"); + MenuList.SetItemButtonAction(count, b => + { + if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechPanelSetupVisible); + MenuList.SetFeedback(count, true); + }); + } + + MenuList.Count = count; + BuildStatusList(); + BuildDisplayList(); + SetupPinModal(); + } + + /// + /// + /// + public override void Show() + { + // divert to PIN if we need auth + if (IsAuthorized) + { + // Cancel the auth timer so we don't deauth after coming back in + if (PinAuthorizedTimer != null) + PinAuthorizedTimer.Stop(); + + TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, true); + PagesInterlock.Show(); + base.Show(); + } + else + { + TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, true); + } + } + + /// + /// + /// + public override void Hide() + { + // Leave it authorized for 60 seconds. + if (IsAuthorized) + PinAuthorizedTimer = new CTimer(o => { + IsAuthorized = false; + PinAuthorizedTimer = null; + }, 60000); + TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, false); + PagesInterlock.Hide(); + base.Hide(); + } + + /// + /// Wire up the keypad and buttons + /// + void SetupPinModal() + { + TriList.SetSigFalseAction(UIBoolJoin.PinDialogCancelPress, CancelPinDialog); + PinKeypad = new SmartObjectNumeric(TriList.SmartObjects[UISmartObjectJoin.TechPinDialogKeypad], true); + PinKeypad.Digit0.UserObject = new Action(b => { if (b)DialPinDigit('0'); }); + PinKeypad.Digit1.UserObject = new Action(b => { if (b)DialPinDigit('1'); }); + PinKeypad.Digit2.UserObject = new Action(b => { if (b)DialPinDigit('2'); }); + PinKeypad.Digit3.UserObject = new Action(b => { if (b)DialPinDigit('3'); }); + PinKeypad.Digit4.UserObject = new Action(b => { if (b)DialPinDigit('4'); }); + PinKeypad.Digit5.UserObject = new Action(b => { if (b)DialPinDigit('5'); }); + PinKeypad.Digit6.UserObject = new Action(b => { if (b)DialPinDigit('6'); }); + PinKeypad.Digit7.UserObject = new Action(b => { if (b)DialPinDigit('7'); }); + PinKeypad.Digit8.UserObject = new Action(b => { if (b)DialPinDigit('8'); }); + PinKeypad.Digit9.UserObject = new Action(b => { if (b)DialPinDigit('9'); }); + } + + /// + /// + /// + /// + void DialPinDigit(char d) + { + PinEntryBuilder.Append(d); + var len = PinEntryBuilder.Length; + SetPinDotsFeedback(len); + + // check it! + if (len == 4) + { + if (Config.Password == PinEntryBuilder.ToString()) + { + IsAuthorized = true; + SetPinDotsFeedback(0); + TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false); + Show(); + } + else + { + SetPinDotsFeedback(0); + TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, true); + new CTimer(o => + { + TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, false); + }, 1500); + } + + PinEntryBuilder.Remove(0, len); // clear it either way + } + } + + /// + /// Draws the dots as pin is entered + /// + /// + void SetPinDotsFeedback(int len) + { + TriList.SetBool(UIBoolJoin.PinDialogDot1, len >= 1); + TriList.SetBool(UIBoolJoin.PinDialogDot2, len >= 2); + TriList.SetBool(UIBoolJoin.PinDialogDot3, len >= 3); + TriList.SetBool(UIBoolJoin.PinDialogDot4, len == 4); + + } + + /// + /// Does what it says + /// + void CancelPinDialog() + { + PinEntryBuilder.Remove(0, PinEntryBuilder.Length); + TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false); + } + + + /// + /// + /// + void BuildStatusList() + { + StatusList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechStatusList, 3, 3, 3); + StatusListDeviceIndexes = new Dictionary(); + uint i = 0; + foreach (var d in DeviceManager.AllDevices) + { + // make sure it is both ICommunicationMonitor and a Device + var sd = d as ICommunicationMonitor; + if (sd == null) + continue; + var dd = sd as Device; + if(dd == null) + continue; + i++; + StatusList.StringInputSig(i, 1).StringValue = dd.Name; + StatusList.UShortInputSig(i, 1).UShortValue = (ushort)sd.CommunicationMonitor.Status; + StatusListDeviceIndexes.Add(sd, i); + sd.CommunicationMonitor.StatusChange += CommunicationMonitor_StatusChange ; + } + StatusList.Count = (ushort)i; + } + + /// + /// Builds the list of display controls + /// + void BuildDisplayList() + { + DisplayList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechDisplayControlsList, 10, 3, 3); + + var devKeys = ConfigReader.ConfigObject.Devices.Where(d => + d.Group.Equals("display", StringComparison.OrdinalIgnoreCase) + || d.Group.Equals("projector", StringComparison.OrdinalIgnoreCase)) + .Select(dd => dd.Key); + var disps = DeviceManager.AllDevices.Where(d => + devKeys.Contains(d.Key)); + ushort i = 0; + foreach (var disp in disps) + { + var display = disp as DisplayBase; + if (display != null) + { + i++; + DisplayList.StringInputSig(i, 1).StringValue = display.Name; + DisplayList.GetBoolFeedbackSig(i, 1).SetSigFalseAction(display.PowerOn); + DisplayList.GetBoolFeedbackSig(i, 2).SetSigFalseAction(display.PowerOff); + if (display is TwoWayDisplayBase) + { + var powerOnSig = DisplayList.BoolInputSig(i, 1); + (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkInputSig(powerOnSig); + + var powerOffSig = DisplayList.BoolInputSig(1, 2); + (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkComplementInputSig(powerOffSig); + } + DisplayList.GetBoolFeedbackSig(i, 3).SetSigFalseAction(() => + { if (display is IInputHdmi1) (display as IInputHdmi1).InputHdmi1(); }); + DisplayList.GetBoolFeedbackSig(i, 4).SetSigFalseAction(() => + { if (display is IInputHdmi2) (display as IInputHdmi2).InputHdmi2(); }); + DisplayList.GetBoolFeedbackSig(i, 5).SetSigFalseAction(() => + { if (display is IInputHdmi3) (display as IInputHdmi3).InputHdmi3(); }); + //DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() => + //{ if (display is IInputHdmi4) (display as IInputHdmi4).InputHdmi4(); }); + DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() => + { if (display is IInputDisplayPort1) (display as IInputDisplayPort1).InputDisplayPort1(); }); + + + // Figure out some way to provide current input feedback + if (display is TwoWayDisplayBase) + { + (display as TwoWayDisplayBase).CurrentInputFeedback.OutputChange += CurrentInputFeedback_OutputChange; + } + } + + + } + + DisplayList.Count = i; + } + + + void CurrentInputFeedback_OutputChange(object sender, EventArgs e) + { + + } + + /// + /// + /// + void CommunicationMonitor_StatusChange(object sender, MonitorStatusChangeEventArgs e) + { + var c = sender as ICommunicationMonitor; + if (c != null && StatusListDeviceIndexes.ContainsKey(c)) + { + var i = StatusListDeviceIndexes[c]; + StatusList.UShortInputSig(i, 1).UShortValue = (ushort)e.Status; + } + } + } } \ No newline at end of file diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 26bdaee9..80fb0ba8 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -354,7 +354,7 @@ namespace PepperDash.Essentials TriList.SetSigFalseAction(UIBoolJoin.DisplayPowerTogglePress, () => { - if (CurrentRoom != null && CurrentRoom.DefaultDisplay is IHasPowerControl) + if (CurrentRoom != null && CurrentRoom.DefaultDisplay != null && CurrentRoom.DefaultDisplay is IHasPowerControl) (CurrentRoom.DefaultDisplay as IHasPowerControl).PowerToggle(); }); @@ -493,10 +493,10 @@ namespace PepperDash.Essentials // and the LastMeetingDismissed != this meeting var lastMeetingDismissed = meetings.FirstOrDefault(m => m.Id == LastMeetingDismissedId); - Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", - CurrentRoom.OnFeedback.BoolValue, - LastMeetingDismissedId, - lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : ""); + //Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", + // CurrentRoom.OnFeedback.BoolValue, + // LastMeetingDismissedId, + // lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : ""); var meeting = meetings.LastOrDefault(m => m.Joinable); if (CurrentRoom.OnFeedback.BoolValue diff --git a/PepperDashEssentials/UIDrivers/JoinedSigInterlock.cs b/PepperDashEssentials/UIDrivers/JoinedSigInterlock.cs index 04e01eb0..a7dba246 100644 --- a/PepperDashEssentials/UIDrivers/JoinedSigInterlock.cs +++ b/PepperDashEssentials/UIDrivers/JoinedSigInterlock.cs @@ -52,7 +52,7 @@ namespace PepperDash.Essentials { var prevJoin = CurrentJoin; var wasShown = _IsShown; - Debug.Console(2, "Trilist {0:X2}, interlock swapping {1} for {2}", TriList.ID, CurrentJoin, join); + //Debug.Console(2, "Trilist {0:X2}, interlock swapping {1} for {2}", TriList.ID, CurrentJoin, join); if (CurrentJoin == join && TriList.BooleanInput[join].BoolValue) return; SetButDontShow(join); @@ -71,7 +71,7 @@ namespace PepperDash.Essentials var prevJoin = CurrentJoin; var wasShown = IsShown; - Debug.Console(2, "Trilist {0:X2}, interlock swapping {1} for {2}", TriList.ID, CurrentJoin, join); + //Debug.Console(2, "Trilist {0:X2}, interlock swapping {1} for {2}", TriList.ID, CurrentJoin, join); if (CurrentJoin == join) HideAndClear(); else @@ -92,7 +92,7 @@ namespace PepperDash.Essentials { var prevJoin = CurrentJoin; var wasShown = IsShown; - Debug.Console(2, "Trilist {0:X2}, interlock hiding {1}", TriList.ID, CurrentJoin); + //Debug.Console(2, "Trilist {0:X2}, interlock hiding {1}", TriList.ID, CurrentJoin); Hide(); CurrentJoin = 0; @@ -108,7 +108,7 @@ namespace PepperDash.Essentials var prevJoin = CurrentJoin; var wasShown = IsShown; - Debug.Console(2, "Trilist {0:X2}, interlock hiding {1}", TriList.ID, CurrentJoin); + //Debug.Console(2, "Trilist {0:X2}, interlock hiding {1}", TriList.ID, CurrentJoin); if (CurrentJoin > 0) { TriList.BooleanInput[CurrentJoin].BoolValue = false; @@ -125,7 +125,7 @@ namespace PepperDash.Essentials var prevJoin = CurrentJoin; var wasShown = IsShown; - Debug.Console(2, "Trilist {0:X2}, interlock showing {1}", TriList.ID, CurrentJoin); + //Debug.Console(2, "Trilist {0:X2}, interlock showing {1}", TriList.ID, CurrentJoin); if (CurrentJoin > 0) { TriList.BooleanInput[CurrentJoin].BoolValue = true; diff --git a/PepperDashEssentials/UIDrivers/ScreenSaverController.cs b/PepperDashEssentials/UIDrivers/ScreenSaverController.cs index e6d60d5b..11ce587a 100644 --- a/PepperDashEssentials/UIDrivers/ScreenSaverController.cs +++ b/PepperDashEssentials/UIDrivers/ScreenSaverController.cs @@ -12,7 +12,7 @@ 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 + public class ScreenSaverController : PanelDriverBase, IDisposable { /// @@ -42,7 +42,7 @@ namespace PepperDash.Essentials PositionInterlock = new JoinedSigInterlock(parent.TriList); - var cmdName = String.Format("shwscrsvr-{0}", parent.TriList.ID); + var cmdName = String.Format("shwscrsvr-{0:X2}", parent.TriList.ID); CrestronConsole.AddNewConsoleCommand((o) => Show(), cmdName, "Shows Panel Screensaver", ConsoleAccessLevelEnum.AccessOperator); @@ -51,6 +51,8 @@ namespace PepperDash.Essentials public override void Show() { + //Debug.Console(2, "Showing ScreenSaverController: {0:X2}", TriList.ID); + if (_parent.AvDriver != null) { _parent.AvDriver.PopupInterlock.ShowInterlocked(UIBoolJoin.MCScreenSaverVisible); @@ -65,10 +67,11 @@ namespace PepperDash.Essentials public override void Hide() { - Debug.Console(1, "Hiding ScreenSaverController"); + //Debug.Console(2, "Hiding ScreenSaverController: {0:X2}", TriList.ID); if (PositionTimer != null) { + //Debug.Console(2, "Stopping PositionTimer: {0:X2}", TriList.ID); PositionTimer.Stop(); PositionTimer.Dispose(); PositionTimer = null; @@ -86,6 +89,8 @@ namespace PepperDash.Essentials void StartPositionTimer() { + //Debug.Console(2, "Starting Position Timer: {0:X2}", TriList.ID); + if (PositionTimer == null) { PositionTimer = new CTimer((o) => PositionTimerExpired(), PositionTimeoutMs); @@ -117,7 +122,7 @@ namespace PepperDash.Essentials CurrentPositionIndex = 0; } - Debug.Console(1, "ScreenSaver Position Timer Expired: Setting new position: {0}", CurrentPositionIndex); + //Debug.Console(2, "ScreenSaver Position Timer Expired: Setting new position: {0} ID: {1:X2}", CurrentPositionIndex, TriList.ID); } // @@ -129,9 +134,19 @@ namespace PepperDash.Essentials void ClearAllPositions() { - Debug.Console(1, "Hiding all screensaver positions"); + //Debug.Console(2, "Hiding all screensaver positions: {0:X2}", TriList.ID); + PositionInterlock.HideAndClear(); } + + #region IDisposable Members + + public void Dispose() + { + Hide(); + } + + #endregion } } \ No newline at end of file diff --git a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index cc5ac265..40671238 100644 --- a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs +++ b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -632,7 +632,6 @@ namespace PepperDash.Essentials.UIDrivers.VC VCControlsInterlock.StatusChanged += new EventHandler(VCControlsInterlock_StatusChanged); - var codecOffCameras = Codec as IHasCameraOff; var supportsCameraOffMode = Codec.SupportsCameraOff; @@ -643,6 +642,7 @@ namespace PepperDash.Essentials.UIDrivers.VC if (codecAutoCameras != null && supportsAutoCameraMode) { + CameraModeList.SetItemButtonAction(1,(b) => codecAutoCameras.CameraAutoModeOn()); TriList.SmartObjects[UISmartObjectJoin.VCCameraMode].BooleanInput["Item 1 Visible"].BoolValue = true; codecAutoCameras.CameraAutoModeIsOnFeedback.LinkInputSig(CameraModeList.SmartObject.BooleanInput["Item 1 Selected"]); @@ -672,6 +672,7 @@ namespace PepperDash.Essentials.UIDrivers.VC } }; + } // Manual button always visible @@ -683,6 +684,7 @@ namespace PepperDash.Essentials.UIDrivers.VC if (codecOffCameras != null && supportsCameraOffMode) { + TriList.SmartObjects[UISmartObjectJoin.VCCameraMode].BooleanInput["Item 3 Visible"].BoolValue = true; codecOffCameras.CameraIsOffFeedback.LinkInputSig(CameraModeList.SmartObject.BooleanInput["Item 3 Selected"]); CameraModeList.SetItemButtonAction(3, (b) => codecOffCameras.CameraOff()); @@ -710,6 +712,7 @@ namespace PepperDash.Essentials.UIDrivers.VC } }; + } } diff --git a/devjson commands.json b/devjson commands.json index 62e675d1..c7ed7291 100644 --- a/devjson commands.json +++ b/devjson commands.json @@ -42,3 +42,6 @@ devjson:2 {"deviceKey":"display01Comm-com", "methodName":"SendText", "params": [ devjson:10 {"deviceKey":"dmLink-ssh", "methodName":"Connect", "params": []} +devjson:2 {"deviceKey":"roomCombiner", "methodName":"SetRoomCombinationScenario", "params": ["combined"]} + +devjson:2 {"deviceKey":"roomCombiner", "methodName":"SetRoomCombinationScenario", "params": ["divided"]} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs index 4e731f41..63684837 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs @@ -46,6 +46,14 @@ namespace PepperDash.Essentials.Core.Bridges public JoinDataComplete RawOccupancyUsFeedback = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 }, new JoinMetadata { Description = "Raw Occupancy Us Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }); + [JoinName("IdentityModeOn")] + public JoinDataComplete IdentityMode = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 }, + new JoinMetadata { Description = "Enable Identity Mode", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("IdentityModeFeedback")] + public JoinDataComplete IdentityModeFeedback = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 }, + new JoinMetadata { Description = "Identity Mode Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }); + [JoinName("EnableLedFlash")] public JoinDataComplete EnableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 }, new JoinMetadata { Description = "Enable Led Flash", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs index ee04bd45..3582e6cf 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs @@ -10,7 +10,7 @@ namespace PepperDash.Essentials.Core.Bridges new JoinMetadata { Description = "DM Chassis enable audio breakaway routing", - JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); @@ -20,7 +20,7 @@ namespace PepperDash.Essentials.Core.Bridges new JoinMetadata { Description = "DM Chassis enable USB breakaway routing", - JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); @@ -84,6 +84,10 @@ namespace PepperDash.Essentials.Core.Bridges public JoinDataComplete OutputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1601, JoinSpan = 32 }, new JoinMetadata { Description = "DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); + [JoinName("NoRouteName")] + public JoinDataComplete NoRouteName = new JoinDataComplete(new JoinData { JoinNumber = 100, JoinSpan = 1 }, + new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); + [JoinName("InputNames")] public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 }, new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); @@ -96,7 +100,7 @@ namespace PepperDash.Essentials.Core.Bridges new JoinDataComplete(new JoinData {JoinNumber = 501, JoinSpan = 200}, new JoinMetadata { - Description = "Video Input Name", + Description = "DM Chassis Video Input Names", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Serial }); @@ -106,7 +110,7 @@ namespace PepperDash.Essentials.Core.Bridges new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 200 }, new JoinMetadata { - Description = "Video Input Name", + Description = "DM Chassis Audio Input Names", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Serial }); @@ -115,7 +119,7 @@ namespace PepperDash.Essentials.Core.Bridges new JoinDataComplete(new JoinData { JoinNumber = 901, JoinSpan = 200 }, new JoinMetadata { - Description = "Video Input Name", + Description = "DM Chassis Video Output Names", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Serial }); @@ -124,7 +128,7 @@ namespace PepperDash.Essentials.Core.Bridges new JoinDataComplete(new JoinData { JoinNumber = 1101, JoinSpan = 200 }, new JoinMetadata { - Description = "Video Input Name", + Description = "DM Chassis Audio Output Names", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Serial }); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs index c995e8c8..ec4661a4 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmRmcControllerJoinMap.cs @@ -28,6 +28,10 @@ namespace PepperDash.Essentials.Core.Bridges public JoinDataComplete EdidSerialNumber = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 }, new JoinMetadata { Description = "DM RMC EDID Serial Number", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); + [JoinName("Name")] + public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 }, + new JoinMetadata { Description = "DM RMC Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); + [JoinName("AudioVideoSource")] public JoinDataComplete AudioVideoSource = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, new JoinMetadata { Description = "DM RMC Audio Video Source Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmTxControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmTxControllerJoinMap.cs index 06953467..6d783639 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmTxControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmTxControllerJoinMap.cs @@ -32,6 +32,10 @@ namespace PepperDash.Essentials.Core.Bridges public JoinDataComplete CurrentInputResolution = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, new JoinMetadata { Description = "DM TX Current Input Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); + [JoinName("Name")] + public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, + new JoinMetadata { Description = "DM TX Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); + [JoinName("VideoInput")] public JoinDataComplete VideoInput = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, new JoinMetadata { Description = "DM TX Video Input Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); @@ -42,7 +46,7 @@ namespace PepperDash.Essentials.Core.Bridges [JoinName("HdcpSupportCapability")] public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 }, - new JoinMetadata { Description = "DM TX HDCP Support Capability", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); + new JoinMetadata { Description = "DM TX HDCP Support Capability", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog }); [JoinName("Port1HdcpState")] public JoinDataComplete Port1HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 }, diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs index 247dfd8f..c7d5c0e5 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs @@ -7,7 +7,15 @@ namespace PepperDash.Essentials.Core.Bridges [JoinName("MasterVolumeLevel")] public JoinDataComplete MasterVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, - new JoinMetadata { Description = "Master Volume Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + new JoinMetadata { Description = "Master Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("MasterVolumeLevelScaled")] + public JoinDataComplete MasterVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, + new JoinMetadata { Description = "Master Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("MixerPresetRecall")] + public JoinDataComplete MixerPresetRecall = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 }, + new JoinMetadata { Description = "Mixer Preset Recall Set", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); [JoinName("MasterVolumeMuteOn")] public JoinDataComplete MasterVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, @@ -23,11 +31,19 @@ namespace PepperDash.Essentials.Core.Bridges [JoinName("MasterVolumeDown")] public JoinDataComplete MasterVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 }, - new JoinMetadata { Description = "Master Volume Mute Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + new JoinMetadata { Description = "Master Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MasterVolumeLevelScaledSend")] + public JoinDataComplete MasterVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 }, + new JoinMetadata { Description = "Master Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); [JoinName("SourceVolumeLevel")] public JoinDataComplete SourceVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 }, - new JoinMetadata { Description = "Source Volume Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + new JoinMetadata { Description = "Source Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("SourceVolumeLevelScaled")] + public JoinDataComplete SourceVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 }, + new JoinMetadata { Description = "Source Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); [JoinName("SourceVolumeMuteOn")] public JoinDataComplete SourceVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 }, @@ -43,11 +59,19 @@ namespace PepperDash.Essentials.Core.Bridges [JoinName("SourceVolumeDown")] public JoinDataComplete SourceVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 }, - new JoinMetadata { Description = "Source Volume Mute Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + new JoinMetadata { Description = "Source Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("SourceVolumeLevelScaledSend")] + public JoinDataComplete SourceVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 }, + new JoinMetadata { Description = "Source Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); [JoinName("Codec1VolumeLevel")] public JoinDataComplete Codec1VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 }, - new JoinMetadata { Description = "Codec1 Volume Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + new JoinMetadata { Description = "Codec1 Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("Codec1VolumeLevelScaled")] + public JoinDataComplete Codec1VolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 }, + new JoinMetadata { Description = "Codec1 Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); [JoinName("Codec1VolumeMuteOn")] public JoinDataComplete Codec1VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 }, @@ -63,11 +87,19 @@ namespace PepperDash.Essentials.Core.Bridges [JoinName("Codec1VolumeDown")] public JoinDataComplete Codec1VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 }, - new JoinMetadata { Description = "Codec1 Volume Mute Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + new JoinMetadata { Description = "Codec1 Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("Codec1VolumeLevelScaledSend")] + public JoinDataComplete Codec1VolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 }, + new JoinMetadata { Description = "Codec1 Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); [JoinName("Codec2VolumeLevel")] public JoinDataComplete Codec2VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 }, - new JoinMetadata { Description = "Codec2 Volume Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + new JoinMetadata { Description = "Codec2 Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("Codec2VolumeLevelScaled")] + public JoinDataComplete Codec2VolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 }, + new JoinMetadata { Description = "Codec2 Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); [JoinName("Codec2VolumeMuteOn")] public JoinDataComplete Codec2VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 }, @@ -83,8 +115,39 @@ namespace PepperDash.Essentials.Core.Bridges [JoinName("Codec2VolumeDown")] public JoinDataComplete Codec2VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 }, - new JoinMetadata { Description = "Codec2 Volume Mute Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + new JoinMetadata { Description = "Codec2 Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + [JoinName("Codec2VolumeLevelScaledSend")] + public JoinDataComplete Codec2VolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 }, + new JoinMetadata { Description = "Codec2 Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MicsMasterVolumeLevel")] + public JoinDataComplete MicsMasterVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 }, + new JoinMetadata { Description = "MicsMaster Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("MicsMasterVolumeLevelScaled")] + public JoinDataComplete MicsMasterVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 }, + new JoinMetadata { Description = "MicsMaster Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("MicsMasterVolumeMuteOn")] + public JoinDataComplete MicsMasterVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 }, + new JoinMetadata { Description = "MicsMaster Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MicsMasterVolumeMuteOff")] + public JoinDataComplete MicsMasterVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 }, + new JoinMetadata { Description = "MicsMaster Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MicsMasterVolumeUp")] + public JoinDataComplete MicsMasterVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 }, + new JoinMetadata { Description = "MicsMaster Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MicsMasterVolumeDown")] + public JoinDataComplete MicsMasterVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 44, JoinSpan = 1 }, + new JoinMetadata { Description = "MicsMaster Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MicsMasterVolumeLevelScaledSend")] + public JoinDataComplete MicsMasterVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 45, JoinSpan = 1 }, + new JoinMetadata { Description = "Mics Master Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); /// /// Constructor to use when instantiating this Join Map without inheriting from it diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsMicrophoneControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsMicrophoneControllerJoinMap.cs new file mode 100644 index 00000000..6922c569 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsMicrophoneControllerJoinMap.cs @@ -0,0 +1,50 @@ +using System; + +namespace PepperDash.Essentials.Core.Bridges +{ + public class DmpsMicrophoneControllerJoinMap : JoinMapBaseAdvanced + { + [JoinName("MicGain")] + public JoinDataComplete MicGain = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, + new JoinMetadata { Description = "Mic Gain dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("MicGainScaled")] + public JoinDataComplete MicGainScaled = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, + new JoinMetadata { Description = "Mic Gain 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("MicMuteOn")] + public JoinDataComplete MicMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, + new JoinMetadata { Description = "Mic Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MicMuteOff")] + public JoinDataComplete MicMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, + new JoinMetadata { Description = "Mic Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MicGainScaledSend")] + public JoinDataComplete MicGainScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 }, + new JoinMetadata { Description = "Mic Gain Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("MicName")] + public JoinDataComplete MicName = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, + new JoinMetadata { Description = "Mic Name Get", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); + + /// + /// Constructor to use when instantiating this Join Map without inheriting from it + /// + /// Join this join map will start at + public DmpsMicrophoneControllerJoinMap(uint joinStart) + : this(joinStart, typeof(DmpsMicrophoneControllerJoinMap)) + { + } + + /// + /// Constructor to use when extending this Join map + /// + /// Join this join map will start at + /// Type of the child join map + protected DmpsMicrophoneControllerJoinMap(uint joinStart, Type type) + : base(joinStart, type) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs index 11385916..44917899 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs @@ -1,9 +1,29 @@ using System; -namespace PepperDash.Essentials.Core.Bridges -{ - public class DmpsRoutingControllerJoinMap : JoinMapBaseAdvanced - { +namespace PepperDash.Essentials.Core.Bridges +{ + public class DmpsRoutingControllerJoinMap : JoinMapBaseAdvanced + { + [JoinName("EnableRouting")] + public JoinDataComplete EnableRouting = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, + new JoinMetadata { Description = "DMPS Enable Audio and Video Routing", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("SystemPowerOn")] + public JoinDataComplete SystemPowerOn = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 }, + new JoinMetadata { Description = "DMPS System Power On Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("SystemPowerOff")] + public JoinDataComplete SystemPowerOff = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 }, + new JoinMetadata { Description = "DMPS System Power Off Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("FrontPanelLockOn")] + public JoinDataComplete FrontPanelLockOn = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 }, + new JoinMetadata { Description = "DMPS Front Panel Lock On Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + + [JoinName("FrontPanelLockOff")] + public JoinDataComplete FrontPanelLockOff = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 }, + new JoinMetadata { Description = "DMPS Front Panel Lock Off Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital }); + [JoinName("VideoSyncStatus")] public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 }, new JoinMetadata { Description = "DM Input Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }); @@ -32,6 +52,44 @@ namespace PepperDash.Essentials.Core.Bridges public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 }, new JoinMetadata { Description = "DM Chassis Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); + [JoinName("InputVideoNames")] + public JoinDataComplete InputVideoNames = + new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 }, + new JoinMetadata + { + Description = "Video Input Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("InputAudioNames")] + public JoinDataComplete InputAudioNames = + new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 }, + new JoinMetadata + { + Description = "Audio Input Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + [JoinName("OutputVideoNames")] + public JoinDataComplete OutputVideoNames = + new JoinDataComplete(new JoinData { JoinNumber = 901, JoinSpan = 32 }, + new JoinMetadata + { + Description = "Video Output Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + [JoinName("OutputAudioNames")] + public JoinDataComplete OutputAudioNames = + new JoinDataComplete(new JoinData { JoinNumber = 1101, JoinSpan = 32 }, + new JoinMetadata + { + Description = "Audio Output Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + [JoinName("OutputCurrentVideoInputNames")] public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 32 }, new JoinMetadata { Description = "DM Chassis Video Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); @@ -61,5 +119,5 @@ namespace PepperDash.Essentials.Core.Bridges protected DmpsRoutingControllerJoinMap(uint joinStart, Type type) : base(joinStart, type) { } - } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs index 3854a4fb..5a583d69 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs @@ -5,6 +5,9 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps { public class GlsPartitionSensorJoinMap : JoinMapBaseAdvanced { + + #region Digital + [JoinName("IsOnline")] public JoinDataComplete IsOnline = new JoinDataComplete( new JoinData @@ -19,20 +22,7 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps JoinType = eJoinType.Digital }); - [JoinName("Name")] - public JoinDataComplete Name = new JoinDataComplete( - new JoinData - { - JoinNumber = 1, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Sensor Name", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - + [JoinName("Enable")] public JoinDataComplete Enable = new JoinDataComplete( new JoinData @@ -101,7 +91,11 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps Description = "Sensor Decrease Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital - }); + }); + + #endregion + + #region Analog [JoinName("Sensitivity")] public JoinDataComplete Sensitivity = new JoinDataComplete( @@ -117,6 +111,28 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps JoinType = eJoinType.Analog }); + #endregion + + + #region Serial + + [JoinName("Name")] + public JoinDataComplete Name = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Sensor Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + #endregion + + /// /// Constructor to use when instantiating this Join Map without inheriting from it /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/SystemMonitorJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/SystemMonitorJoinMap.cs index e07ad275..363d389b 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/SystemMonitorJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/SystemMonitorJoinMap.cs @@ -45,7 +45,7 @@ namespace PepperDash.Essentials.Core.Bridges new JoinMetadata { Description = "Processor Last Boot", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); [JoinName("ProgramOffsetJoin")] - public JoinDataComplete ProgramOffsetJoin = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 }, + public JoinDataComplete ProgramOffsetJoin = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 5 }, new JoinMetadata { Description = "All Program Data is offset between slots by 5 - First Joins Start at 11", JoinCapabilities = eJoinCapabilities.None, JoinType = eJoinType.None }); [JoinName("ProgramStart")] diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs index 21b88ed3..d1b383b0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs @@ -1,927 +1,1150 @@ -using System; -using PepperDash.Essentials.Core; -namespace PepperDash.Essentials.Core.Bridges.JoinMaps -{ - public class VideoCodecControllerJoinMap : JoinMapBaseAdvanced - { - #region Digital - - [JoinName("IsOnline")] - public JoinDataComplete IsOnline = new JoinDataComplete( - new JoinData - { - JoinNumber = 1, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Device is Online", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("1")] - public JoinDataComplete Dtmf1 = new JoinDataComplete( - new JoinData - { - JoinNumber = 11, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 1", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("2")] - public JoinDataComplete Dtmf2 = new JoinDataComplete( - new JoinData - { - JoinNumber = 12, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 2", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("3")] - public JoinDataComplete Dtmf3 = new JoinDataComplete( - new JoinData - { - JoinNumber = 13, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 3", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("4")] - public JoinDataComplete Dtmf4 = new JoinDataComplete( - new JoinData - { - JoinNumber = 14, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 4", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("5")] - public JoinDataComplete Dtmf5 = new JoinDataComplete( - new JoinData - { - JoinNumber = 15, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 5", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("6")] - public JoinDataComplete Dtmf6 = new JoinDataComplete( - new JoinData - { - JoinNumber = 16, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 6", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("7")] - public JoinDataComplete Dtmf7 = new JoinDataComplete( - new JoinData - { - JoinNumber = 17, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 7", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("8")] - public JoinDataComplete Dtmf8 = new JoinDataComplete( - new JoinData - { - JoinNumber = 18, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 8", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("9")] - public JoinDataComplete Dtmf9 = new JoinDataComplete( - new JoinData - { - JoinNumber = 19, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 9", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("0")] - public JoinDataComplete Dtmf0 = new JoinDataComplete( - new JoinData - { - JoinNumber = 20, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 0", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("*")] - public JoinDataComplete DtmfStar = new JoinDataComplete( - new JoinData - { - JoinNumber = 21, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF *", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("#")] - public JoinDataComplete DtmfPound = new JoinDataComplete( - new JoinData - { - JoinNumber = 22, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF #", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("EndCall")] - public JoinDataComplete EndCall = new JoinDataComplete( - new JoinData - { - JoinNumber = 24, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Hang Up", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("HookState")] - public JoinDataComplete HookState = new JoinDataComplete( - new JoinData - { - JoinNumber = 31, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Hook State", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("SpeedDialStart")] - public JoinDataComplete SpeedDialStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 41, - JoinSpan = 4 - }, - new JoinMetadata - { - Description = "Speed Dial", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("IncomingCall")] - public JoinDataComplete IncomingCall = new JoinDataComplete( - new JoinData - { - JoinNumber = 50, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Incoming Call", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("IncomingAnswer")] - public JoinDataComplete IncomingAnswer = new JoinDataComplete( - new JoinData - { - JoinNumber = 51, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Answer Incoming Call", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("IncomingReject")] - public JoinDataComplete IncomingReject = new JoinDataComplete( - new JoinData - { - JoinNumber = 52, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Reject Incoming Call", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("ManualDial")] - public JoinDataComplete ManualDial = new JoinDataComplete( - new JoinData - { - JoinNumber = 71, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Dial manual string", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DialPhoneCall")] - public JoinDataComplete DialPhone = new JoinDataComplete( - new JoinData - { - JoinNumber = 72, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Dial Phone", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("PhoneHookState")] - public JoinDataComplete PhoneHookState = new JoinDataComplete( - new JoinData - { - JoinNumber = 72, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Dial Phone", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("EndPhoneCall")] - public JoinDataComplete HangUpPhone = new JoinDataComplete( - new JoinData - { - JoinNumber = 73, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Hang Up PHone", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectorySearchBusy")] - public JoinDataComplete DirectorySearchBusy = new JoinDataComplete( - new JoinData - { - JoinNumber = 100, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Search Busy FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryEntryIsContact")] - public JoinDataComplete DirectoryEntryIsContact = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Selected Entry Is Contact FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryLineSelected")] - public JoinDataComplete DirectoryLineSelected = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Line Selected FB", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryIsRoot")] - public JoinDataComplete DirectoryIsRoot = new JoinDataComplete( - new JoinData - { - JoinNumber = 102, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory is on Root FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryHasChanged")] - public JoinDataComplete DirectoryHasChanged = new JoinDataComplete( - new JoinData - { - JoinNumber = 103, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory has changed FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryRoot")] - public JoinDataComplete DirectoryRoot = new JoinDataComplete( - new JoinData - { - JoinNumber = 104, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Go to Directory Root", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryFolderBack")] - public JoinDataComplete DirectoryFolderBack = new JoinDataComplete( - new JoinData - { - JoinNumber = 105, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Go back one directory level", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryDialSelectedLine")] - public JoinDataComplete DirectoryDialSelectedLine = new JoinDataComplete( - new JoinData - { - JoinNumber = 106, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Dial selected directory line", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - - [JoinName("CameraTiltUp")] - public JoinDataComplete CameraTiltUp = new JoinDataComplete( - new JoinData - { - JoinNumber = 111, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Tilt Up", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraTiltDown")] - public JoinDataComplete CameraTiltDown = new JoinDataComplete( - new JoinData - { - JoinNumber = 112, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Tilt Down", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraPanLeft")] - public JoinDataComplete CameraPanLeft = new JoinDataComplete( - new JoinData - { - JoinNumber = 113, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Pan Left", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraPanRight")] - public JoinDataComplete CameraPanRight = new JoinDataComplete( - new JoinData - { - JoinNumber = 114, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Pan Right", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraZoomIn")] - public JoinDataComplete CameraZoomIn = new JoinDataComplete( - new JoinData - { - JoinNumber = 115, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Zoom In", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraZoomOut")] - public JoinDataComplete CameraZoomOut = new JoinDataComplete( - new JoinData - { - JoinNumber = 116, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Zoom Out", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraPresetSave")] - public JoinDataComplete CameraPresetSave = new JoinDataComplete( - new JoinData - { - JoinNumber = 121, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Save Selected Preset", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraModeAuto")] - public JoinDataComplete CameraModeAuto = new JoinDataComplete( - new JoinData - { - JoinNumber = 131, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Mode Auto", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraModeManual")] - public JoinDataComplete CameraModeManual = new JoinDataComplete( - new JoinData - { - JoinNumber = 132, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Mode Manual", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraModeOff")] - public JoinDataComplete CameraModeOff = new JoinDataComplete( - new JoinData - { - JoinNumber = 133, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Mode Off", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraSelfView")] - public JoinDataComplete CameraSelfView = new JoinDataComplete( - new JoinData - { - JoinNumber = 141, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Self View Toggle/FB", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraLayout")] - public JoinDataComplete CameraLayout = new JoinDataComplete( - new JoinData - { - JoinNumber = 142, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Layout Toggle", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraSupportsAutoMode")] - public JoinDataComplete CameraSupportsAutoMode = new JoinDataComplete( - new JoinData - { - JoinNumber = 143, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Supports Auto Mode FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraSupportsOffMode")] - public JoinDataComplete CameraSupportsOffMode = new JoinDataComplete( - new JoinData - { - JoinNumber = 144, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Supports Off Mode FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("UpdateMeetings")] - public JoinDataComplete UpdateMeetings = new JoinDataComplete( - new JoinData - { - JoinNumber = 160, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Update Meetings", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DialMeeting1")] - public JoinDataComplete DialMeeting1 = new JoinDataComplete( - new JoinData - { - JoinNumber = 161, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Join first meeting", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DialMeeting2")] - public JoinDataComplete DialMeeting2 = new JoinDataComplete( - new JoinData - { - JoinNumber = 162, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Join second meeting", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DialMeeting3")] - public JoinDataComplete DialMeeting3 = new JoinDataComplete( - new JoinData - { - JoinNumber = 163, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Join third meeting", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("MicMuteOn")] - public JoinDataComplete MicMuteOn = new JoinDataComplete( - new JoinData - { - JoinNumber = 171, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Mic Mute On", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("MicMuteOff")] - public JoinDataComplete MicMuteOff = new JoinDataComplete( - new JoinData - { - JoinNumber = 172, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Mic Mute Off", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("MicMuteToggle")] - public JoinDataComplete MicMuteToggle = new JoinDataComplete( - new JoinData - { - JoinNumber = 173, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Mic Mute Toggle", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeUp")] - public JoinDataComplete VolumeUp = new JoinDataComplete( - new JoinData - { - JoinNumber = 174, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Up", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeDown")] - public JoinDataComplete VolumeDown = new JoinDataComplete( - new JoinData - { - JoinNumber = 175, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Down", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeMuteOn")] - public JoinDataComplete VolumeMuteOn = new JoinDataComplete( - new JoinData - { - JoinNumber = 176, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Mute On", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeMuteOff")] - public JoinDataComplete VolumeMuteOff = new JoinDataComplete( - new JoinData - { - JoinNumber = 177, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Mute Off", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeMuteToggle")] - public JoinDataComplete VolumeMuteToggle = new JoinDataComplete( - new JoinData - { - JoinNumber = 178, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Mute Toggle", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("SourceShareStart")] - public JoinDataComplete SourceShareStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 201, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Start Sharing & Feedback", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("SourceShareEnd")] - public JoinDataComplete SourceShareEnd = new JoinDataComplete( - new JoinData - { - JoinNumber = 202, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Stop Sharing & Feedback", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("AutoShareWhileInCall")] - public JoinDataComplete SourceShareAutoStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 203, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "When high, will autostart sharing when a call is joined", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("RecievingContent")] - public JoinDataComplete RecievingContent = new JoinDataComplete( - new JoinData - { - JoinNumber = 204, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Recieving content from the far end", - JoinType = eJoinType.Digital, - JoinCapabilities = eJoinCapabilities.ToSIMPL - }); - - [JoinName("SelfviewPosition")] - public JoinDataComplete SelfviewPosition = new JoinDataComplete( - new JoinData - { - JoinNumber = 211, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "advance selfview position", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("ParticipantAudioMuteToggleStart")] - public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 501, - JoinSpan = 50 - }, - new JoinMetadata - { - Description = "Toggles the participant's audio mute status", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("ParticipantVideoMuteToggleStart")] - public JoinDataComplete ParticipantVideoMuteToggleStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 801, - JoinSpan = 50 - }, - new JoinMetadata - { - Description = "Toggles the participant's video mute status", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("ParticipantPinToggleStart")] - public JoinDataComplete ParticipantPinToggleStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 1101, - JoinSpan = 50 - }, - new JoinMetadata - { - Description = "Toggles the participant's pin status", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - - #endregion - - - +using System; +using PepperDash.Essentials.Core; +namespace PepperDash.Essentials.Core.Bridges.JoinMaps +{ + public class VideoCodecControllerJoinMap : JoinMapBaseAdvanced + { + #region Digital + + [JoinName("IsOnline")] + public JoinDataComplete IsOnline = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device is Online", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SendDtmfToSpecificCallIndex")] + public JoinDataComplete SendDtmfToSpecificCallIndex = new JoinDataComplete( + new JoinData + { + JoinNumber = 10, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "If High, will send DTMF tones to the call set by SelectCall analog. If low sends DTMF tones to last connected call.", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf1")] + public JoinDataComplete Dtmf1 = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 1", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf2")] + public JoinDataComplete Dtmf2 = new JoinDataComplete( + new JoinData + { + JoinNumber = 12, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 2", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf3")] + public JoinDataComplete Dtmf3 = new JoinDataComplete( + new JoinData + { + JoinNumber = 13, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 3", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf4")] + public JoinDataComplete Dtmf4 = new JoinDataComplete( + new JoinData + { + JoinNumber = 14, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 4", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf5")] + public JoinDataComplete Dtmf5 = new JoinDataComplete( + new JoinData + { + JoinNumber = 15, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 5", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf6")] + public JoinDataComplete Dtmf6 = new JoinDataComplete( + new JoinData + { + JoinNumber = 16, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 6", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf7")] + public JoinDataComplete Dtmf7 = new JoinDataComplete( + new JoinData + { + JoinNumber = 17, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 7", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf8")] + public JoinDataComplete Dtmf8 = new JoinDataComplete( + new JoinData + { + JoinNumber = 18, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 8", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf9")] + public JoinDataComplete Dtmf9 = new JoinDataComplete( + new JoinData + { + JoinNumber = 19, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 9", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("Dtmf0")] + public JoinDataComplete Dtmf0 = new JoinDataComplete( + new JoinData + { + JoinNumber = 20, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 0", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DtmfStar")] + public JoinDataComplete DtmfStar = new JoinDataComplete( + new JoinData + { + JoinNumber = 21, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF *", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DtmfPound")] + public JoinDataComplete DtmfPound = new JoinDataComplete( + new JoinData + { + JoinNumber = 22, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF #", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("EndAllCalls")] + public JoinDataComplete EndAllCalls = new JoinDataComplete( + new JoinData + { + JoinNumber = 24, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "End All Calls", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("HookState")] + public JoinDataComplete HookState = new JoinDataComplete( + new JoinData + { + JoinNumber = 31, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Hook State", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SpeedDialStart")] + public JoinDataComplete SpeedDialStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 41, + JoinSpan = 4 + }, + new JoinMetadata + { + Description = "Speed Dial", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingCall")] + public JoinDataComplete IncomingCall = new JoinDataComplete( + new JoinData + { + JoinNumber = 50, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Incoming Call", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingAnswer")] + public JoinDataComplete IncomingAnswer = new JoinDataComplete( + new JoinData + { + JoinNumber = 51, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Answer Incoming Call", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingReject")] + public JoinDataComplete IncomingReject = new JoinDataComplete( + new JoinData + { + JoinNumber = 52, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Reject Incoming Call", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ManualDial")] + public JoinDataComplete ManualDial = new JoinDataComplete( + new JoinData + { + JoinNumber = 71, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Dial manual string specified by CurrentDialString serial join", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialPhone")] + public JoinDataComplete DialPhone = new JoinDataComplete( + new JoinData + { + JoinNumber = 72, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Dial Phone", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("PhoneHookState")] + public JoinDataComplete PhoneHookState = new JoinDataComplete( + new JoinData + { + JoinNumber = 72, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Dial Phone", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("HangUpPhone")] + public JoinDataComplete HangUpPhone = new JoinDataComplete( + new JoinData + { + JoinNumber = 73, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Hang Up Phone", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("EndCallStart")] + public JoinDataComplete EndCallStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 81, + JoinSpan = 8 + }, + new JoinMetadata + { + Description = "End a specific call by call index. ", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("JoinAllCalls")] + public JoinDataComplete JoinAllCalls = new JoinDataComplete( + new JoinData + { + JoinNumber = 90, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Join all calls", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("JoinCallStart")] + public JoinDataComplete JoinCallStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 91, + JoinSpan = 8 + }, + new JoinMetadata + { + Description = "Join a specific call by call index. ", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectorySearchBusy")] + public JoinDataComplete DirectorySearchBusy = new JoinDataComplete( + new JoinData + { + JoinNumber = 100, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Search Busy FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryEntryIsContact")] + public JoinDataComplete DirectoryEntryIsContact = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Selected Entry Is Contact FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryLineSelected")] + public JoinDataComplete DirectoryLineSelected = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Line Selected FB", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryIsRoot")] + public JoinDataComplete DirectoryIsRoot = new JoinDataComplete( + new JoinData + { + JoinNumber = 102, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory is on Root FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryHasChanged")] + public JoinDataComplete DirectoryHasChanged = new JoinDataComplete( + new JoinData + { + JoinNumber = 103, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory has changed FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryRoot")] + public JoinDataComplete DirectoryRoot = new JoinDataComplete( + new JoinData + { + JoinNumber = 104, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Go to Directory Root", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryFolderBack")] + public JoinDataComplete DirectoryFolderBack = new JoinDataComplete( + new JoinData + { + JoinNumber = 105, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Go back one directory level", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryDialSelectedLine")] + public JoinDataComplete DirectoryDialSelectedLine = new JoinDataComplete( + new JoinData + { + JoinNumber = 106, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Dial selected directory line", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryDisableAutoDialSelectedLine")] + public JoinDataComplete DirectoryDisableAutoDialSelectedLine = new JoinDataComplete( + new JoinData + { + JoinNumber = 107, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Set high to disable automatic dialing of a contact when selected", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryDialSelectedContactMethod")] + public JoinDataComplete DirectoryDialSelectedContactMethod = new JoinDataComplete( + new JoinData + { + JoinNumber = 108, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to dial the selected contact method", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraTiltUp")] + public JoinDataComplete CameraTiltUp = new JoinDataComplete( + new JoinData + { + JoinNumber = 111, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Tilt Up", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraTiltDown")] + public JoinDataComplete CameraTiltDown = new JoinDataComplete( + new JoinData + { + JoinNumber = 112, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Tilt Down", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraPanLeft")] + public JoinDataComplete CameraPanLeft = new JoinDataComplete( + new JoinData + { + JoinNumber = 113, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Pan Left", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraPanRight")] + public JoinDataComplete CameraPanRight = new JoinDataComplete( + new JoinData + { + JoinNumber = 114, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Pan Right", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraZoomIn")] + public JoinDataComplete CameraZoomIn = new JoinDataComplete( + new JoinData + { + JoinNumber = 115, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Zoom In", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraZoomOut")] + public JoinDataComplete CameraZoomOut = new JoinDataComplete( + new JoinData + { + JoinNumber = 116, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Zoom Out", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraFocusNear")] + public JoinDataComplete CameraFocusNear = new JoinDataComplete( + new JoinData + { + JoinNumber = 117, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Focus Near", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraFocusFar")] + public JoinDataComplete CameraFocusFar = new JoinDataComplete( + new JoinData + { + JoinNumber = 118, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Focus Far", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraFocusAuto")] + public JoinDataComplete CameraFocusAuto = new JoinDataComplete( + new JoinData + { + JoinNumber = 119, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Auto Focus Trigger", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraPresetSave")] + public JoinDataComplete CameraPresetSave = new JoinDataComplete( + new JoinData + { + JoinNumber = 121, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to save selected preset spcified by CameraPresetSelect analog join. FB will pulse for 3s when preset saved.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraModeAuto")] + public JoinDataComplete CameraModeAuto = new JoinDataComplete( + new JoinData + { + JoinNumber = 131, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Mode Auto. Enables camera auto tracking mode, with feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraModeManual")] + public JoinDataComplete CameraModeManual = new JoinDataComplete( + new JoinData + { + JoinNumber = 132, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Mode Manual. Disables camera auto tracking mode, with feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraModeOff")] + public JoinDataComplete CameraModeOff = new JoinDataComplete( + new JoinData + { + JoinNumber = 133, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Mode Off. Disables camera video, with feedback. Works like video mute.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSelfView")] + public JoinDataComplete CameraSelfView = new JoinDataComplete( + new JoinData + { + JoinNumber = 141, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Self View Toggle/FB", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraLayout")] + public JoinDataComplete CameraLayout = new JoinDataComplete( + new JoinData + { + JoinNumber = 142, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Layout Toggle", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSupportsAutoMode")] + public JoinDataComplete CameraSupportsAutoMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 143, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Supports Auto Mode FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSupportsOffMode")] + public JoinDataComplete CameraSupportsOffMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 144, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Supports Off Mode FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("UpdateMeetings")] + public JoinDataComplete UpdateMeetings = new JoinDataComplete( + new JoinData + { + JoinNumber = 160, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Update Meetings", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialMeeting1")] + public JoinDataComplete DialMeeting1 = new JoinDataComplete( + new JoinData + { + JoinNumber = 161, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Join first meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialMeeting2")] + public JoinDataComplete DialMeeting2 = new JoinDataComplete( + new JoinData + { + JoinNumber = 162, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Join second meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialMeeting3")] + public JoinDataComplete DialMeeting3 = new JoinDataComplete( + new JoinData + { + JoinNumber = 163, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Join third meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MicMuteOn")] + public JoinDataComplete MicMuteOn = new JoinDataComplete( + new JoinData + { + JoinNumber = 171, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Mic Mute On", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MicMuteOff")] + public JoinDataComplete MicMuteOff = new JoinDataComplete( + new JoinData + { + JoinNumber = 172, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Mic Mute Off", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MicMuteToggle")] + public JoinDataComplete MicMuteToggle = new JoinDataComplete( + new JoinData + { + JoinNumber = 173, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Mic Mute Toggle", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeUp")] + public JoinDataComplete VolumeUp = new JoinDataComplete( + new JoinData + { + JoinNumber = 174, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Up", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeDown")] + public JoinDataComplete VolumeDown = new JoinDataComplete( + new JoinData + { + JoinNumber = 175, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Down", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeMuteOn")] + public JoinDataComplete VolumeMuteOn = new JoinDataComplete( + new JoinData + { + JoinNumber = 176, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Mute On", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeMuteOff")] + public JoinDataComplete VolumeMuteOff = new JoinDataComplete( + new JoinData + { + JoinNumber = 177, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Mute Off", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeMuteToggle")] + public JoinDataComplete VolumeMuteToggle = new JoinDataComplete( + new JoinData + { + JoinNumber = 178, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Mute Toggle", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("RemoveSelectedRecentCallItem")] + public JoinDataComplete RemoveSelectedRecentCallItem = new JoinDataComplete( + new JoinData + { + JoinNumber = 181, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to remove the selected recent call item specified by the SelectRecentCallItem analog join", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialSelectedRecentCallItem")] + public JoinDataComplete DialSelectedRecentCallItem = new JoinDataComplete( + new JoinData + { + JoinNumber = 182, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to dial the selected recent call item specified by the SelectRecentCallItem analog join", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SourceShareStart")] + public JoinDataComplete SourceShareStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 201, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Start Sharing & Feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SourceShareEnd")] + public JoinDataComplete SourceShareEnd = new JoinDataComplete( + new JoinData + { + JoinNumber = 202, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Stop Sharing & Feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("AutoShareWhileInCall")] + public JoinDataComplete SourceShareAutoStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 203, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "When high, will autostart sharing when a call is joined", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("RecievingContent")] + public JoinDataComplete RecievingContent = new JoinDataComplete( + new JoinData + { + JoinNumber = 204, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Recieving content from the far end", + JoinType = eJoinType.Digital, + JoinCapabilities = eJoinCapabilities.ToSIMPL + }); + + [JoinName("SelfviewPosition")] + public JoinDataComplete SelfviewPosition = new JoinDataComplete( + new JoinData + { + JoinNumber = 211, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Toggles selfview position", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("HoldAllCalls")] + public JoinDataComplete HoldAllCalls = new JoinDataComplete( + new JoinData + { + JoinNumber = 220, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Holds all calls", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("HoldCallsStart")] + public JoinDataComplete HoldCallsStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 221, + JoinSpan = 8 + }, + new JoinMetadata + { + Description = "Holds Call at specified index. FB reported on Call Status XSIG", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ResumeCallsStart")] + public JoinDataComplete ResumeCallsStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 231, + JoinSpan = 8 + }, + new JoinMetadata + { + Description = "Resume Call at specified index", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MultiSiteOptionIsEnabled")] + public JoinDataComplete MultiSiteOptionIsEnabled = new JoinDataComplete( + new JoinData + { + JoinNumber = 301, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Multi site option is enabled FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("AutoAnswerEnabled")] + public JoinDataComplete AutoAnswerEnabled = new JoinDataComplete( + new JoinData + { + JoinNumber = 302, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Auto Answer is enabled FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ParticipantAudioMuteToggleStart")] + public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 501, + JoinSpan = 50 + }, + new JoinMetadata + { + Description = "Toggles the participant's audio mute status", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ParticipantVideoMuteToggleStart")] + public JoinDataComplete ParticipantVideoMuteToggleStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 801, + JoinSpan = 50 + }, + new JoinMetadata + { + Description = "Toggles the participant's video mute status", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ParticipantPinToggleStart")] + public JoinDataComplete ParticipantPinToggleStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 1101, + JoinSpan = 50 + }, + new JoinMetadata + { + Description = "Toggles the participant's pin status", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + + #endregion + + + #region Analog // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set @@ -937,1674 +1160,1955 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps Description = "Set/FB the number of meetings to display via the bridge xsig; default: 3 meetings.", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog - }); - - [JoinName("MinutesBeforeMeetingStart")] - public JoinDataComplete MinutesBeforeMeetingStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 41, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Minutes before meeting start that a meeting is joinable", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("CameraNumberSelect")] - public JoinDataComplete CameraNumberSelect = new JoinDataComplete( - new JoinData - { - JoinNumber = 60, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Number Select/FB", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("DirectoryRowCount")] - public JoinDataComplete DirectoryRowCount = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Row Count FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("DirectorySelectRow")] - public JoinDataComplete DirectorySelectRow = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Select Row", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("CameraPresetSelect")] - public JoinDataComplete CameraPresetSelect = new JoinDataComplete( - new JoinData - { - JoinNumber = 121, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Preset Select", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("ParticipantCount")] - public JoinDataComplete ParticipantCount = new JoinDataComplete( - new JoinData - { - JoinNumber = 151, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Participant Count", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("Meeting Count Fb")] - public JoinDataComplete MeetingCount = new JoinDataComplete( - new JoinData - { - JoinNumber = 161, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Meeting Count", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("VolumeLevel")] - public JoinDataComplete VolumeLevel = new JoinDataComplete( - new JoinData - { - JoinNumber = 174, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Level", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Analog - }); - - #endregion - - - - #region Serials - - [JoinName("CurrentDialString")] - public JoinDataComplete CurrentDialString = new JoinDataComplete( - new JoinData - { - JoinNumber = 1, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Dial String", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("PhoneString")] - public JoinDataComplete PhoneDialString = new JoinDataComplete( - new JoinData - { - JoinNumber = 2, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Phone Dial String", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CurrentCallName")] - public JoinDataComplete CurrentCallData = new JoinDataComplete( - new JoinData - { - JoinNumber = 2, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Call Data - XSIG", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CallDirection")] - public JoinDataComplete CallDirection = new JoinDataComplete( - new JoinData - { - JoinNumber = 22, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Call Direction", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("IncomingCallName")] - public JoinDataComplete IncomingCallName = new JoinDataComplete( - new JoinData - { - JoinNumber = 51, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Incoming Call Name", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("IncomingCallNumber")] - public JoinDataComplete IncomingCallNumber = new JoinDataComplete( - new JoinData - { - JoinNumber = 52, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Incoming Call Number", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - - [JoinName("DirectorySearchString")] - public JoinDataComplete DirectorySearchString = new JoinDataComplete( - new JoinData - { - JoinNumber = 100, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Search String", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("DirectoryEntries")] - public JoinDataComplete DirectoryEntries = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Entries - XSig, 255 entries", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("Schedule")] - public JoinDataComplete Schedule = new JoinDataComplete( - new JoinData - { - JoinNumber = 102, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Schedule Data - XSIG", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CameraPresetNames")] - public JoinDataComplete CameraPresetNames = new JoinDataComplete( - new JoinData - { - JoinNumber = 121, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Preset Names - XSIG, max of 15", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CameraLayoutStringFb")] - public JoinDataComplete CameraLayoutStringFb = new JoinDataComplete( - new JoinData - { - JoinNumber = 141, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Layout Fb", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CurrentParticipants")] - public JoinDataComplete CurrentParticipants = new JoinDataComplete( - new JoinData - { - JoinNumber = 151, - JoinSpan = 1 - }, - new JoinMetadata() - { - Description = "Current Participants XSig", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CurrentSource")] - public JoinDataComplete CurrentSource = new JoinDataComplete( - new JoinData - { - JoinNumber = 201, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Source", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("SelfviewPositionFb")] - public JoinDataComplete SelfviewPositionFb = new JoinDataComplete( - new JoinData - { - JoinNumber = 211, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "advance selfview position", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("DirectoryEntrySelectedName")] - public JoinDataComplete DirectoryEntrySelectedName = new JoinDataComplete( - new JoinData - { - JoinNumber = 356, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Selected Directory Entry Name", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("DirectoryEntrySelectedNumber")] - public JoinDataComplete DirectoryEntrySelectedNumber = new JoinDataComplete( - new JoinData - { - JoinNumber = 357, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Selected Directory Entry Number", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("DirectorySelectedFolderName")] - public JoinDataComplete DirectorySelectedFolderName = new JoinDataComplete( - new JoinData - { - JoinNumber = 358, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Selected Directory Folder Name", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - #endregion - - - public VideoCodecControllerJoinMap(uint joinStart) - : base(joinStart, typeof(VideoCodecControllerJoinMap)) - { - } - - public VideoCodecControllerJoinMap(uint joinStart, Type type) - : base(joinStart, type) - { - } - } -} - - -namespace PepperDash_Essentials_Core.Bridges.JoinMaps -{ - [Obsolete("Use PepperDash.Essentials.Core.Bridges.JoinMaps")] - public class VideoCodecControllerJoinMap : JoinMapBaseAdvanced - { - - #region Digital - - [JoinName("IsOnline")] - public JoinDataComplete IsOnline = new JoinDataComplete( - new JoinData - { - JoinNumber = 1, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Device is Online", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("1")] - public JoinDataComplete Dtmf1 = new JoinDataComplete( - new JoinData - { - JoinNumber = 11, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 1", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("2")] - public JoinDataComplete Dtmf2 = new JoinDataComplete( - new JoinData - { - JoinNumber = 12, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 2", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("3")] - public JoinDataComplete Dtmf3 = new JoinDataComplete( - new JoinData - { - JoinNumber = 13, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 3", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("4")] - public JoinDataComplete Dtmf4 = new JoinDataComplete( - new JoinData - { - JoinNumber = 14, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 4", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("5")] - public JoinDataComplete Dtmf5 = new JoinDataComplete( - new JoinData - { - JoinNumber = 15, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 5", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("6")] - public JoinDataComplete Dtmf6 = new JoinDataComplete( - new JoinData - { - JoinNumber = 16, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 6", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("7")] - public JoinDataComplete Dtmf7 = new JoinDataComplete( - new JoinData - { - JoinNumber = 17, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 7", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("8")] - public JoinDataComplete Dtmf8 = new JoinDataComplete( - new JoinData - { - JoinNumber = 18, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 8", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("9")] - public JoinDataComplete Dtmf9 = new JoinDataComplete( - new JoinData - { - JoinNumber = 19, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 9", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("0")] - public JoinDataComplete Dtmf0 = new JoinDataComplete( - new JoinData - { - JoinNumber = 20, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF 0", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("*")] - public JoinDataComplete DtmfStar = new JoinDataComplete( - new JoinData - { - JoinNumber = 21, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF *", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("#")] - public JoinDataComplete DtmfPound = new JoinDataComplete( - new JoinData - { - JoinNumber = 22, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "DTMF #", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("EndCall")] - public JoinDataComplete EndCall = new JoinDataComplete( - new JoinData - { - JoinNumber = 24, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Hang Up", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("HookState")] - public JoinDataComplete HookState = new JoinDataComplete( - new JoinData - { - JoinNumber = 31, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Hook State", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("SpeedDialStart")] - public JoinDataComplete SpeedDialStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 41, - JoinSpan = 4 - }, - new JoinMetadata - { - Description = "Speed Dial", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("IncomingCall")] - public JoinDataComplete IncomingCall = new JoinDataComplete( - new JoinData - { - JoinNumber = 50, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Incoming Call", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("IncomingAnswer")] - public JoinDataComplete IncomingAnswer = new JoinDataComplete( - new JoinData - { - JoinNumber = 51, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Answer Incoming Call", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("IncomingReject")] - public JoinDataComplete IncomingReject = new JoinDataComplete( - new JoinData - { - JoinNumber = 52, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Reject Incoming Call", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("ManualDial")] - public JoinDataComplete ManualDial = new JoinDataComplete( - new JoinData - { - JoinNumber = 71, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Dial manual string", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DialPhoneCall")] - public JoinDataComplete DialPhone = new JoinDataComplete( - new JoinData - { - JoinNumber = 72, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Dial Phone", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("PhoneHookState")] - public JoinDataComplete PhoneHookState = new JoinDataComplete( - new JoinData - { - JoinNumber = 72, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Dial Phone", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("EndPhoneCall")] - public JoinDataComplete HangUpPhone = new JoinDataComplete( - new JoinData - { - JoinNumber = 73, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Hang Up PHone", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectorySearchBusy")] - public JoinDataComplete DirectorySearchBusy = new JoinDataComplete( - new JoinData - { - JoinNumber = 100, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Search Busy FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryEntryIsContact")] - public JoinDataComplete DirectoryEntryIsContact = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Selected Entry Is Contact FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryLineSelected")] - public JoinDataComplete DirectoryLineSelected = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Line Selected FB", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryIsRoot")] - public JoinDataComplete DirectoryIsRoot = new JoinDataComplete( - new JoinData - { - JoinNumber = 102, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory is on Root FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryHasChanged")] - public JoinDataComplete DirectoryHasChanged = new JoinDataComplete( - new JoinData - { - JoinNumber = 103, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory has changed FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryRoot")] - public JoinDataComplete DirectoryRoot = new JoinDataComplete( - new JoinData - { - JoinNumber = 104, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Go to Directory Root", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryFolderBack")] - public JoinDataComplete DirectoryFolderBack = new JoinDataComplete( - new JoinData - { - JoinNumber = 105, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Go back one directory level", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DirectoryDialSelectedLine")] - public JoinDataComplete DirectoryDialSelectedLine = new JoinDataComplete( - new JoinData - { - JoinNumber = 106, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Dial selected directory line", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - - [JoinName("CameraTiltUp")] - public JoinDataComplete CameraTiltUp = new JoinDataComplete( - new JoinData - { - JoinNumber = 111, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Tilt Up", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraTiltDown")] - public JoinDataComplete CameraTiltDown = new JoinDataComplete( - new JoinData - { - JoinNumber = 112, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Tilt Down", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraPanLeft")] - public JoinDataComplete CameraPanLeft = new JoinDataComplete( - new JoinData - { - JoinNumber = 113, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Pan Left", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraPanRight")] - public JoinDataComplete CameraPanRight = new JoinDataComplete( - new JoinData - { - JoinNumber = 114, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Pan Right", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraZoomIn")] - public JoinDataComplete CameraZoomIn = new JoinDataComplete( - new JoinData - { - JoinNumber = 115, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Zoom In", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraZoomOut")] - public JoinDataComplete CameraZoomOut = new JoinDataComplete( - new JoinData - { - JoinNumber = 116, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Zoom Out", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraPresetSave")] - public JoinDataComplete CameraPresetSave = new JoinDataComplete( - new JoinData - { - JoinNumber = 121, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Save Selected Preset", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraModeAuto")] - public JoinDataComplete CameraModeAuto = new JoinDataComplete( - new JoinData - { - JoinNumber = 131, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Mode Auto", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraModeManual")] - public JoinDataComplete CameraModeManual = new JoinDataComplete( - new JoinData - { - JoinNumber = 132, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Mode Manual", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraModeOff")] - public JoinDataComplete CameraModeOff = new JoinDataComplete( - new JoinData - { - JoinNumber = 133, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Mode Off", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraSelfView")] - public JoinDataComplete CameraSelfView = new JoinDataComplete( - new JoinData - { - JoinNumber = 141, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Self View Toggle/FB", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraLayout")] - public JoinDataComplete CameraLayout = new JoinDataComplete( - new JoinData - { - JoinNumber = 142, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Layout Toggle", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraSupportsAutoMode")] - public JoinDataComplete CameraSupportsAutoMode = new JoinDataComplete( - new JoinData - { - JoinNumber = 143, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Supports Auto Mode FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("CameraSupportsOffMode")] - public JoinDataComplete CameraSupportsOffMode = new JoinDataComplete( - new JoinData - { - JoinNumber = 144, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Supports Off Mode FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("UpdateMeetings")] - public JoinDataComplete UpdateMeetings = new JoinDataComplete( - new JoinData - { - JoinNumber = 160, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Update Meetings", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DialMeeting1")] - public JoinDataComplete DialMeeting1 = new JoinDataComplete( - new JoinData - { - JoinNumber = 161, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Join first meeting", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DialMeeting2")] - public JoinDataComplete DialMeeting2 = new JoinDataComplete( - new JoinData - { - JoinNumber = 162, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Join second meeting", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("DialMeeting3")] - public JoinDataComplete DialMeeting3 = new JoinDataComplete( - new JoinData - { - JoinNumber = 163, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Join third meeting", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("MicMuteOn")] - public JoinDataComplete MicMuteOn = new JoinDataComplete( - new JoinData - { - JoinNumber = 171, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Mic Mute On", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("MicMuteOff")] - public JoinDataComplete MicMuteOff = new JoinDataComplete( - new JoinData - { - JoinNumber = 172, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Mic Mute Off", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("MicMuteToggle")] - public JoinDataComplete MicMuteToggle = new JoinDataComplete( - new JoinData - { - JoinNumber = 173, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Mic Mute Toggle", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeUp")] - public JoinDataComplete VolumeUp = new JoinDataComplete( - new JoinData - { - JoinNumber = 174, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Up", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeDown")] - public JoinDataComplete VolumeDown = new JoinDataComplete( - new JoinData - { - JoinNumber = 175, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Down", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeMuteOn")] - public JoinDataComplete VolumeMuteOn = new JoinDataComplete( - new JoinData - { - JoinNumber = 176, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Mute On", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeMuteOff")] - public JoinDataComplete VolumeMuteOff = new JoinDataComplete( - new JoinData - { - JoinNumber = 177, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Mute Off", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("VolumeMuteToggle")] - public JoinDataComplete VolumeMuteToggle = new JoinDataComplete( - new JoinData - { - JoinNumber = 178, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Mute Toggle", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("SourceShareStart")] - public JoinDataComplete SourceShareStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 201, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Start Sharing & Feedback", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("SourceShareEnd")] - public JoinDataComplete SourceShareEnd = new JoinDataComplete( - new JoinData - { - JoinNumber = 202, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Stop Sharing & Feedback", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("AutoShareWhileInCall")] - public JoinDataComplete SourceShareAutoStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 203, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "When high, will autostart sharing when a call is joined", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("RecievingContent")] - public JoinDataComplete RecievingContent = new JoinDataComplete( - new JoinData - { - JoinNumber = 204, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Recieving content from the far end", - JoinType = eJoinType.Digital, - JoinCapabilities = eJoinCapabilities.ToSIMPL - }); - - [JoinName("SelfviewPosition")] - public JoinDataComplete SelfviewPosition = new JoinDataComplete( - new JoinData - { - JoinNumber = 211, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "advance selfview position", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("ParticipantAudioMuteToggleStart")] - public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 500, - JoinSpan = 50 - }, - new JoinMetadata - { - Description = "Toggles the participant's audio mute status", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("ParticipantVideoMuteToggleStart")] - public JoinDataComplete ParticipantVideoMuteToggleStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 800, - JoinSpan = 50 - }, - new JoinMetadata - { - Description = "Toggles the participant's video mute status", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("ParticipantPinToggleStart")] - public JoinDataComplete ParticipantPinToggleStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 1100, - JoinSpan = 50 - }, - new JoinMetadata - { - Description = "Toggles the participant's pin status", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - #endregion - - - - #region Analog - - [JoinName("MinutesBeforeMeetingStart")] - public JoinDataComplete MinutesBeforeMeetingStart = new JoinDataComplete( - new JoinData - { - JoinNumber = 41, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Minutes before meeting start that a meeting is joinable", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("CameraNumberSelect")] - public JoinDataComplete CameraNumberSelect = new JoinDataComplete( - new JoinData - { - JoinNumber = 60, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Number Select/FB", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("DirectoryRowCount")] - public JoinDataComplete DirectoryRowCount = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Row Count FB", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("DirectorySelectRow")] - public JoinDataComplete DirectorySelectRow = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Select Row", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("CameraPresetSelect")] - public JoinDataComplete CameraPresetSelect = new JoinDataComplete( - new JoinData - { - JoinNumber = 121, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Preset Select", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("ParticipantCount")] - public JoinDataComplete ParticipantCount = new JoinDataComplete( - new JoinData - { - JoinNumber = 151, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Participant Count", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("Meeting Count Fb")] - public JoinDataComplete MeetingCount = new JoinDataComplete( - new JoinData - { - JoinNumber = 161, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Meeting Count", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("VolumeLevel")] - public JoinDataComplete VolumeLevel = new JoinDataComplete( - new JoinData - { - JoinNumber = 174, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Volume Level", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Analog - }); - - #endregion - - - - #region Serials - - [JoinName("CurrentDialString")] - public JoinDataComplete CurrentDialString = new JoinDataComplete( - new JoinData - { - JoinNumber = 1, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Dial String", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("PhoneString")] - public JoinDataComplete PhoneDialString = new JoinDataComplete( - new JoinData - { - JoinNumber = 2, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Phone Dial String", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CurrentCallName")] - public JoinDataComplete CurrentCallData = new JoinDataComplete( - new JoinData - { - JoinNumber = 2, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Call Data - XSIG", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CallDirection")] - public JoinDataComplete CallDirection = new JoinDataComplete( - new JoinData - { - JoinNumber = 22, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Call Direction", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("IncomingCallName")] - public JoinDataComplete IncomingCallName = new JoinDataComplete( - new JoinData - { - JoinNumber = 51, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Incoming Call Name", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("IncomingCallNumber")] - public JoinDataComplete IncomingCallNumber = new JoinDataComplete( - new JoinData - { - JoinNumber = 52, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Incoming Call Number", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - - [JoinName("DirectorySearchString")] - public JoinDataComplete DirectorySearchString = new JoinDataComplete( - new JoinData - { - JoinNumber = 100, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Search String", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("DirectoryEntries")] - public JoinDataComplete DirectoryEntries = new JoinDataComplete( - new JoinData - { - JoinNumber = 101, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Directory Entries - XSig, 255 entries", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("Schedule")] - public JoinDataComplete Schedule = new JoinDataComplete( - new JoinData - { - JoinNumber = 102, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Schedule Data - XSIG", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CameraPresetNames")] - public JoinDataComplete CameraPresetNames = new JoinDataComplete( - new JoinData - { - JoinNumber = 121, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Camera Preset Names - XSIG, max of 15", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CameraLayoutStringFb")] - public JoinDataComplete CameraLayoutStringFb = new JoinDataComplete( - new JoinData - { - JoinNumber = 141, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Layout Fb", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CurrentParticipants")] - public JoinDataComplete CurrentParticipants = new JoinDataComplete( - new JoinData - { - JoinNumber = 151, - JoinSpan = 1 - }, - new JoinMetadata() - { - Description = "Current Participants XSig", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("CurrentSource")] - public JoinDataComplete CurrentSource = new JoinDataComplete( - new JoinData - { - JoinNumber = 201, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Current Source", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("SelfviewPositionFb")] - public JoinDataComplete SelfviewPositionFb = new JoinDataComplete( - new JoinData - { - JoinNumber = 211, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "advance selfview position", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("DirectoryEntrySelectedName")] - public JoinDataComplete DirectoryEntrySelectedName = new JoinDataComplete( - new JoinData - { - JoinNumber = 356, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Selected Directory Entry Name", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("DirectoryEntrySelectedNumber")] - public JoinDataComplete DirectoryEntrySelectedNumber = new JoinDataComplete( - new JoinData - { - JoinNumber = 357, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Selected Directory Entry Number", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - [JoinName("DirectorySelectedFolderName")] - public JoinDataComplete DirectorySelectedFolderName = new JoinDataComplete( - new JoinData - { - JoinNumber = 358, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Selected Directory Folder Name", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - #endregion - - - public VideoCodecControllerJoinMap(uint joinStart) - : base(joinStart, typeof(VideoCodecControllerJoinMap)) - { - } - - public VideoCodecControllerJoinMap(uint joinStart, Type type) - : base(joinStart, type) - { - } - } -} + }); + + [JoinName("SelectCall")] + public JoinDataComplete SelectCall = new JoinDataComplete( + new JoinData + { + JoinNumber = 24, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Sets the selected Call for DTMF commands. Valid values 1-8", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + + [JoinName("ConnectedCallCount")] + public JoinDataComplete ConnectedCallCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 25, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Reports the number of currently connected calls", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("MinutesBeforeMeetingStart")] + public JoinDataComplete MinutesBeforeMeetingStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 41, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Minutes before meeting start that a meeting is joinable", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("CameraNumberSelect")] + public JoinDataComplete CameraNumberSelect = new JoinDataComplete( + new JoinData + { + JoinNumber = 60, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Number Select/FB. 1 based index. Valid range is 1 to the value reported by CameraCount.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("CameraCount")] + public JoinDataComplete CameraCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 61, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Reports the number of cameras", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("DirectoryRowCount")] + public JoinDataComplete DirectoryRowCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Row Count FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("DirectorySelectRow")] + public JoinDataComplete DirectorySelectRow = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Select Row", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("SelectedContactMethodCount")] + public JoinDataComplete SelectedContactMethodCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 102, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Reports the number of contact methods for the selected contact", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("SelectContactMethod")] + public JoinDataComplete SelectContactMethod = new JoinDataComplete( + new JoinData + { + JoinNumber = 103, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selects a contact method by index", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("CameraPresetSelect")] + public JoinDataComplete CameraPresetSelect = new JoinDataComplete( + new JoinData + { + JoinNumber = 121, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Preset Select", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("FarEndPresetSelect")] + public JoinDataComplete FarEndPresetSelect = new JoinDataComplete( + new JoinData + { + JoinNumber = 122, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Far End Preset Preset Select", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("ParticipantCount")] + public JoinDataComplete ParticipantCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 151, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Participant Count", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("Meeting Count Fb")] + public JoinDataComplete MeetingCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 161, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Meeting Count", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("VolumeLevel")] + public JoinDataComplete VolumeLevel = new JoinDataComplete( + new JoinData + { + JoinNumber = 174, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Level", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("SelectRecentCallItem")] + public JoinDataComplete SelectRecentCallItem = new JoinDataComplete( + new JoinData + { + JoinNumber = 180, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Select/FB for Recent Call Item. Valid values 1 - 10", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("RecentCallOccurrenceType")] + public JoinDataComplete RecentCallOccurrenceType = new JoinDataComplete( + new JoinData + { + JoinNumber = 181, + JoinSpan = 10 + }, + new JoinMetadata + { + Description = "Recent Call Occurrence Type. [0-3] 0 = Unknown, 1 = Placed, 2 = Received, 3 = NoAnswer", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("RecentCallCount")] + public JoinDataComplete RecentCallCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 191, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Recent Call Count", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + #endregion + + + + #region Serials + + [JoinName("CurrentDialString")] + public JoinDataComplete CurrentDialString = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Value to dial when ManualDial digital join is pulsed", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("PhoneDialString")] + public JoinDataComplete PhoneDialString = new JoinDataComplete( + new JoinData + { + JoinNumber = 2, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Phone Dial String", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentCallData")] + public JoinDataComplete CurrentCallData = new JoinDataComplete( + new JoinData + { + JoinNumber = 2, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Call Data - XSIG", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CallDirection")] + public JoinDataComplete CallDirection = new JoinDataComplete( + new JoinData + { + JoinNumber = 22, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Call Direction", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("IncomingCallName")] + public JoinDataComplete IncomingCallName = new JoinDataComplete( + new JoinData + { + JoinNumber = 51, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Incoming Call Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("IncomingCallNumber")] + public JoinDataComplete IncomingCallNumber = new JoinDataComplete( + new JoinData + { + JoinNumber = 52, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Incoming Call Number", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + + [JoinName("DirectorySearchString")] + public JoinDataComplete DirectorySearchString = new JoinDataComplete( + new JoinData + { + JoinNumber = 100, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Search String", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryEntries")] + public JoinDataComplete DirectoryEntries = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Entries - XSig, 255 entries", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("Schedule")] + public JoinDataComplete Schedule = new JoinDataComplete( + new JoinData + { + JoinNumber = 102, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Schedule Data - XSIG", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("ContactMethods")] + public JoinDataComplete ContactMethods = new JoinDataComplete( + new JoinData + { + JoinNumber = 103, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Contact Methods - XSig, 10 entries", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CameraPresetNames")] + public JoinDataComplete CameraPresetNames = new JoinDataComplete( + new JoinData + { + JoinNumber = 121, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Preset Names - XSIG, max of 15", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentLayoutStringFb")] + public JoinDataComplete CurrentLayoutStringFb = new JoinDataComplete( + new JoinData + { + JoinNumber = 141, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Layout Fb", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentParticipants")] + public JoinDataComplete CurrentParticipants = new JoinDataComplete( + new JoinData + { + JoinNumber = 151, + JoinSpan = 1 + }, + new JoinMetadata() + { + Description = "Current Participants XSig", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CameraNamesFb")] + public JoinDataComplete CameraNamesFb = new JoinDataComplete( + new JoinData + { + JoinNumber = 161, + JoinSpan = 10 + }, + new JoinMetadata + { + Description = "Camera Name Fb", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("SelectedRecentCallName")] + public JoinDataComplete SelectedRecentCallName = new JoinDataComplete( + new JoinData + { + JoinNumber = 171, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selected Recent Call Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("SelectedRecentCallNumber")] + public JoinDataComplete SelectedRecentCallNumber = new JoinDataComplete( + new JoinData + { + JoinNumber = 172, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selected Recent Call Number", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("RecentCallNamesStart")] + public JoinDataComplete RecentCallNamesStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 181, + JoinSpan = 10 + }, + new JoinMetadata + { + Description = "Recent Call Names", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("RecentCallTimesStart")] + public JoinDataComplete RecentCallTimesStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 191, + JoinSpan = 10 + }, + new JoinMetadata + { + Description = "Recent Calls Times", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentSource")] + public JoinDataComplete CurrentSource = new JoinDataComplete( + new JoinData + { + JoinNumber = 201, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Source", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("SelfviewPositionFb")] + public JoinDataComplete SelfviewPositionFb = new JoinDataComplete( + new JoinData + { + JoinNumber = 211, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "advance selfview position", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DeviceIpAddresss")] + public JoinDataComplete DeviceIpAddresss = new JoinDataComplete( + new JoinData + { + JoinNumber = 301, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "IP Address of device", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("SipPhoneNumber")] + public JoinDataComplete SipPhoneNumber = new JoinDataComplete( + new JoinData + { + JoinNumber = 302, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "SIP phone number of device", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("E164Alias")] + public JoinDataComplete E164Alias = new JoinDataComplete( + new JoinData + { + JoinNumber = 303, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "E164 alias of device", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("H323Id")] + public JoinDataComplete H323Id = new JoinDataComplete( + new JoinData + { + JoinNumber = 304, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "H323 ID of device", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("SipUri")] + public JoinDataComplete SipUri = new JoinDataComplete( + new JoinData + { + JoinNumber = 305, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "SIP URI of device", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryEntrySelectedName")] + public JoinDataComplete DirectoryEntrySelectedName = new JoinDataComplete( + new JoinData + { + JoinNumber = 356, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selected Directory Entry Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryEntrySelectedNumber")] + public JoinDataComplete DirectoryEntrySelectedNumber = new JoinDataComplete( + new JoinData + { + JoinNumber = 357, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selected Directory Entry Number", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectorySelectedFolderName")] + public JoinDataComplete DirectorySelectedFolderName = new JoinDataComplete( + new JoinData + { + JoinNumber = 358, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selected Directory Folder Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + #endregion + + + public VideoCodecControllerJoinMap(uint joinStart) + : base(joinStart, typeof(VideoCodecControllerJoinMap)) + { + } + + public VideoCodecControllerJoinMap(uint joinStart, Type type) + : base(joinStart, type) + { + } + } +} + + +namespace PepperDash_Essentials_Core.Bridges.JoinMaps +{ + [Obsolete("Use PepperDash.Essentials.Core.Bridges.JoinMaps")] + public class VideoCodecControllerJoinMap : JoinMapBaseAdvanced + { + + #region Digital + + [JoinName("IsOnline")] + public JoinDataComplete IsOnline = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device is Online", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("1")] + public JoinDataComplete Dtmf1 = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 1", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("2")] + public JoinDataComplete Dtmf2 = new JoinDataComplete( + new JoinData + { + JoinNumber = 12, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 2", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("3")] + public JoinDataComplete Dtmf3 = new JoinDataComplete( + new JoinData + { + JoinNumber = 13, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 3", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("4")] + public JoinDataComplete Dtmf4 = new JoinDataComplete( + new JoinData + { + JoinNumber = 14, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 4", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("5")] + public JoinDataComplete Dtmf5 = new JoinDataComplete( + new JoinData + { + JoinNumber = 15, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 5", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("6")] + public JoinDataComplete Dtmf6 = new JoinDataComplete( + new JoinData + { + JoinNumber = 16, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 6", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("7")] + public JoinDataComplete Dtmf7 = new JoinDataComplete( + new JoinData + { + JoinNumber = 17, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 7", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("8")] + public JoinDataComplete Dtmf8 = new JoinDataComplete( + new JoinData + { + JoinNumber = 18, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 8", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("9")] + public JoinDataComplete Dtmf9 = new JoinDataComplete( + new JoinData + { + JoinNumber = 19, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 9", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("0")] + public JoinDataComplete Dtmf0 = new JoinDataComplete( + new JoinData + { + JoinNumber = 20, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF 0", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("*")] + public JoinDataComplete DtmfStar = new JoinDataComplete( + new JoinData + { + JoinNumber = 21, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF *", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("#")] + public JoinDataComplete DtmfPound = new JoinDataComplete( + new JoinData + { + JoinNumber = 22, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "DTMF #", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("EndCall")] + public JoinDataComplete EndCall = new JoinDataComplete( + new JoinData + { + JoinNumber = 24, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Hang Up", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("HookState")] + public JoinDataComplete HookState = new JoinDataComplete( + new JoinData + { + JoinNumber = 31, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Hook State", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SpeedDialStart")] + public JoinDataComplete SpeedDialStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 41, + JoinSpan = 4 + }, + new JoinMetadata + { + Description = "Speed Dial", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingCall")] + public JoinDataComplete IncomingCall = new JoinDataComplete( + new JoinData + { + JoinNumber = 50, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Incoming Call", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingAnswer")] + public JoinDataComplete IncomingAnswer = new JoinDataComplete( + new JoinData + { + JoinNumber = 51, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Answer Incoming Call", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("IncomingReject")] + public JoinDataComplete IncomingReject = new JoinDataComplete( + new JoinData + { + JoinNumber = 52, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Reject Incoming Call", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ManualDial")] + public JoinDataComplete ManualDial = new JoinDataComplete( + new JoinData + { + JoinNumber = 71, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Dial manual string", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialPhoneCall")] + public JoinDataComplete DialPhone = new JoinDataComplete( + new JoinData + { + JoinNumber = 72, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Dial Phone", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("PhoneHookState")] + public JoinDataComplete PhoneHookState = new JoinDataComplete( + new JoinData + { + JoinNumber = 72, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Dial Phone", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("EndPhoneCall")] + public JoinDataComplete HangUpPhone = new JoinDataComplete( + new JoinData + { + JoinNumber = 73, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Hang Up PHone", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectorySearchBusy")] + public JoinDataComplete DirectorySearchBusy = new JoinDataComplete( + new JoinData + { + JoinNumber = 100, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Search Busy FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryEntryIsContact")] + public JoinDataComplete DirectoryEntryIsContact = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Selected Entry Is Contact FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryLineSelected")] + public JoinDataComplete DirectoryLineSelected = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Line Selected FB", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryIsRoot")] + public JoinDataComplete DirectoryIsRoot = new JoinDataComplete( + new JoinData + { + JoinNumber = 102, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory is on Root FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryHasChanged")] + public JoinDataComplete DirectoryHasChanged = new JoinDataComplete( + new JoinData + { + JoinNumber = 103, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory has changed FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryRoot")] + public JoinDataComplete DirectoryRoot = new JoinDataComplete( + new JoinData + { + JoinNumber = 104, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Go to Directory Root", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryFolderBack")] + public JoinDataComplete DirectoryFolderBack = new JoinDataComplete( + new JoinData + { + JoinNumber = 105, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Go back one directory level", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DirectoryDialSelectedLine")] + public JoinDataComplete DirectoryDialSelectedLine = new JoinDataComplete( + new JoinData + { + JoinNumber = 106, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Dial selected directory line", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + + [JoinName("CameraTiltUp")] + public JoinDataComplete CameraTiltUp = new JoinDataComplete( + new JoinData + { + JoinNumber = 111, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Tilt Up", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraTiltDown")] + public JoinDataComplete CameraTiltDown = new JoinDataComplete( + new JoinData + { + JoinNumber = 112, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Tilt Down", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraPanLeft")] + public JoinDataComplete CameraPanLeft = new JoinDataComplete( + new JoinData + { + JoinNumber = 113, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Pan Left", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraPanRight")] + public JoinDataComplete CameraPanRight = new JoinDataComplete( + new JoinData + { + JoinNumber = 114, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Pan Right", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraZoomIn")] + public JoinDataComplete CameraZoomIn = new JoinDataComplete( + new JoinData + { + JoinNumber = 115, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Zoom In", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraZoomOut")] + public JoinDataComplete CameraZoomOut = new JoinDataComplete( + new JoinData + { + JoinNumber = 116, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Zoom Out", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraPresetSave")] + public JoinDataComplete CameraPresetSave = new JoinDataComplete( + new JoinData + { + JoinNumber = 121, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Save Selected Preset", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraModeAuto")] + public JoinDataComplete CameraModeAuto = new JoinDataComplete( + new JoinData + { + JoinNumber = 131, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Mode Auto", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraModeManual")] + public JoinDataComplete CameraModeManual = new JoinDataComplete( + new JoinData + { + JoinNumber = 132, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Mode Manual", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraModeOff")] + public JoinDataComplete CameraModeOff = new JoinDataComplete( + new JoinData + { + JoinNumber = 133, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Mode Off", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSelfView")] + public JoinDataComplete CameraSelfView = new JoinDataComplete( + new JoinData + { + JoinNumber = 141, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Self View Toggle/FB", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraLayout")] + public JoinDataComplete CameraLayout = new JoinDataComplete( + new JoinData + { + JoinNumber = 142, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Layout Toggle", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSupportsAutoMode")] + public JoinDataComplete CameraSupportsAutoMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 143, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Supports Auto Mode FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CameraSupportsOffMode")] + public JoinDataComplete CameraSupportsOffMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 144, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Supports Off Mode FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("UpdateMeetings")] + public JoinDataComplete UpdateMeetings = new JoinDataComplete( + new JoinData + { + JoinNumber = 160, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Update Meetings", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialMeeting1")] + public JoinDataComplete DialMeeting1 = new JoinDataComplete( + new JoinData + { + JoinNumber = 161, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Join first meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialMeeting2")] + public JoinDataComplete DialMeeting2 = new JoinDataComplete( + new JoinData + { + JoinNumber = 162, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Join second meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DialMeeting3")] + public JoinDataComplete DialMeeting3 = new JoinDataComplete( + new JoinData + { + JoinNumber = 163, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Join third meeting", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MicMuteOn")] + public JoinDataComplete MicMuteOn = new JoinDataComplete( + new JoinData + { + JoinNumber = 171, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Mic Mute On", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MicMuteOff")] + public JoinDataComplete MicMuteOff = new JoinDataComplete( + new JoinData + { + JoinNumber = 172, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Mic Mute Off", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("MicMuteToggle")] + public JoinDataComplete MicMuteToggle = new JoinDataComplete( + new JoinData + { + JoinNumber = 173, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Mic Mute Toggle", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeUp")] + public JoinDataComplete VolumeUp = new JoinDataComplete( + new JoinData + { + JoinNumber = 174, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Up", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeDown")] + public JoinDataComplete VolumeDown = new JoinDataComplete( + new JoinData + { + JoinNumber = 175, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Down", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeMuteOn")] + public JoinDataComplete VolumeMuteOn = new JoinDataComplete( + new JoinData + { + JoinNumber = 176, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Mute On", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeMuteOff")] + public JoinDataComplete VolumeMuteOff = new JoinDataComplete( + new JoinData + { + JoinNumber = 177, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Mute Off", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("VolumeMuteToggle")] + public JoinDataComplete VolumeMuteToggle = new JoinDataComplete( + new JoinData + { + JoinNumber = 178, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Mute Toggle", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SourceShareStart")] + public JoinDataComplete SourceShareStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 201, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Start Sharing & Feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SourceShareEnd")] + public JoinDataComplete SourceShareEnd = new JoinDataComplete( + new JoinData + { + JoinNumber = 202, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Stop Sharing & Feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("AutoShareWhileInCall")] + public JoinDataComplete SourceShareAutoStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 203, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "When high, will autostart sharing when a call is joined", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("RecievingContent")] + public JoinDataComplete RecievingContent = new JoinDataComplete( + new JoinData + { + JoinNumber = 204, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Recieving content from the far end", + JoinType = eJoinType.Digital, + JoinCapabilities = eJoinCapabilities.ToSIMPL + }); + + [JoinName("SelfviewPosition")] + public JoinDataComplete SelfviewPosition = new JoinDataComplete( + new JoinData + { + JoinNumber = 211, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "advance selfview position", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ParticipantAudioMuteToggleStart")] + public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 500, + JoinSpan = 50 + }, + new JoinMetadata + { + Description = "Toggles the participant's audio mute status", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ParticipantVideoMuteToggleStart")] + public JoinDataComplete ParticipantVideoMuteToggleStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 800, + JoinSpan = 50 + }, + new JoinMetadata + { + Description = "Toggles the participant's video mute status", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ParticipantPinToggleStart")] + public JoinDataComplete ParticipantPinToggleStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 1100, + JoinSpan = 50 + }, + new JoinMetadata + { + Description = "Toggles the participant's pin status", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + #endregion + + + + #region Analog + + [JoinName("MinutesBeforeMeetingStart")] + public JoinDataComplete MinutesBeforeMeetingStart = new JoinDataComplete( + new JoinData + { + JoinNumber = 41, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Minutes before meeting start that a meeting is joinable", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("CameraNumberSelect")] + public JoinDataComplete CameraNumberSelect = new JoinDataComplete( + new JoinData + { + JoinNumber = 60, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Number Select/FB", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("DirectoryRowCount")] + public JoinDataComplete DirectoryRowCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Row Count FB", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("DirectorySelectRow")] + public JoinDataComplete DirectorySelectRow = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Select Row", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("CameraPresetSelect")] + public JoinDataComplete CameraPresetSelect = new JoinDataComplete( + new JoinData + { + JoinNumber = 121, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Preset Select", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("ParticipantCount")] + public JoinDataComplete ParticipantCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 151, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Participant Count", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("Meeting Count Fb")] + public JoinDataComplete MeetingCount = new JoinDataComplete( + new JoinData + { + JoinNumber = 161, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Meeting Count", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("VolumeLevel")] + public JoinDataComplete VolumeLevel = new JoinDataComplete( + new JoinData + { + JoinNumber = 174, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Volume Level", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + #endregion + + + + #region Serials + + [JoinName("CurrentDialString")] + public JoinDataComplete CurrentDialString = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Dial String", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("PhoneString")] + public JoinDataComplete PhoneDialString = new JoinDataComplete( + new JoinData + { + JoinNumber = 2, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Phone Dial String", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentCallName")] + public JoinDataComplete CurrentCallData = new JoinDataComplete( + new JoinData + { + JoinNumber = 2, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Call Data - XSIG", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CallDirection")] + public JoinDataComplete CallDirection = new JoinDataComplete( + new JoinData + { + JoinNumber = 22, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Call Direction", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("IncomingCallName")] + public JoinDataComplete IncomingCallName = new JoinDataComplete( + new JoinData + { + JoinNumber = 51, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Incoming Call Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("IncomingCallNumber")] + public JoinDataComplete IncomingCallNumber = new JoinDataComplete( + new JoinData + { + JoinNumber = 52, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Incoming Call Number", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + + [JoinName("DirectorySearchString")] + public JoinDataComplete DirectorySearchString = new JoinDataComplete( + new JoinData + { + JoinNumber = 100, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Search String", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryEntries")] + public JoinDataComplete DirectoryEntries = new JoinDataComplete( + new JoinData + { + JoinNumber = 101, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Directory Entries - XSig, 255 entries", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("Schedule")] + public JoinDataComplete Schedule = new JoinDataComplete( + new JoinData + { + JoinNumber = 102, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Schedule Data - XSIG", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CameraPresetNames")] + public JoinDataComplete CameraPresetNames = new JoinDataComplete( + new JoinData + { + JoinNumber = 121, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Camera Preset Names - XSIG, max of 15", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CameraLayoutStringFb")] + public JoinDataComplete CameraLayoutStringFb = new JoinDataComplete( + new JoinData + { + JoinNumber = 141, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Layout Fb", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentParticipants")] + public JoinDataComplete CurrentParticipants = new JoinDataComplete( + new JoinData + { + JoinNumber = 151, + JoinSpan = 1 + }, + new JoinMetadata() + { + Description = "Current Participants XSig", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("CurrentSource")] + public JoinDataComplete CurrentSource = new JoinDataComplete( + new JoinData + { + JoinNumber = 201, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Current Source", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("SelfviewPositionFb")] + public JoinDataComplete SelfviewPositionFb = new JoinDataComplete( + new JoinData + { + JoinNumber = 211, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "advance selfview position", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryEntrySelectedName")] + public JoinDataComplete DirectoryEntrySelectedName = new JoinDataComplete( + new JoinData + { + JoinNumber = 356, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selected Directory Entry Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectoryEntrySelectedNumber")] + public JoinDataComplete DirectoryEntrySelectedNumber = new JoinDataComplete( + new JoinData + { + JoinNumber = 357, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selected Directory Entry Number", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("DirectorySelectedFolderName")] + public JoinDataComplete DirectorySelectedFolderName = new JoinDataComplete( + new JoinData + { + JoinNumber = 358, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Selected Directory Folder Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + #endregion + + + public VideoCodecControllerJoinMap(uint joinStart) + : base(joinStart, typeof(VideoCodecControllerJoinMap)) + { + } + + public VideoCodecControllerJoinMap(uint joinStart, Type type) + : base(joinStart, type) + { + } + } +} diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/C2nIo/C2nIoController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/C2nIo/C2nIoController.cs index 0b70288f..f1f1891d 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/C2nIo/C2nIoController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/C2nIo/C2nIoController.cs @@ -66,7 +66,7 @@ namespace PepperDash.Essentials.Core.CrestronIO #endregion } - public class C2NIoControllerFactory : EssentialsDeviceFactory + public class C2NIoControllerFactory : EssentialsDeviceFactory { public C2NIoControllerFactory() { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/CenIoRy104Controller.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/CenIoRy104Controller.cs new file mode 100644 index 00000000..19ca8438 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/CenIoRy104Controller.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.GeneralIO; +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Core +{ + /// + /// Wrapper class for CEN-IO-RY-104 relay module + /// + [Description("Wrapper class for the CEN-IO-RY-104 relay module")] + public class CenIoRy104Controller : EssentialsDevice, IRelayPorts + { + private readonly CenIoRy104 _ry104; + + /// + /// Constructor + /// + /// + /// + /// + public CenIoRy104Controller(string key, string name, CenIoRy104 ry104) + : base(key, name) + { + _ry104 = ry104; + } + + /// + /// Relay port collection + /// + public CrestronCollection RelayPorts + { + get { return _ry104.RelayPorts; } + } + + /// + /// Number of relay ports property + /// + public int NumberOfRelayPorts + { + get { return _ry104.NumberOfRelayPorts; } + } + } + + /// + /// CEN-IO-RY Controller factory + /// + public class CenIoRy104ControllerFactory : EssentialsDeviceFactory + { + /// + /// Constructor + /// + public CenIoRy104ControllerFactory() + { + TypeNames = new List() { "ceniory104" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create a new CEN-IO-RY-104 Device"); + + var controlPropertiesConfig = CommFactory.GetControlPropertiesConfig(dc); + if (controlPropertiesConfig == null) + { + Debug.Console(1, "Factory failed to create a new CEN-IO-RY-104 Device"); + return null; + } + + var ipid = controlPropertiesConfig.IpIdInt; + if (ipid != 0) return new CenIoRy104Controller(dc.Key, dc.Name, new CenIoRy104(ipid, Global.ControlSystem)); + + Debug.Console(1, "Factory failed to create a new CEN-IO-RY-104 Device using IP-ID-{0}", ipid); + return null; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs index df2c3c56..b7534087 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs @@ -69,19 +69,28 @@ namespace PepperDash.Essentials.Core public override bool CustomActivate() { Debug.Console(0, this, "Activating"); - if (!PreventRegistration) - { + if (!PreventRegistration) + { //Debug.Console(1, this, " Does not require registration. Skipping"); - var response = Hardware.RegisterWithLogging(Key); - if (response != eDeviceRegistrationUnRegistrationResponse.Success) - { - //Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response); - return false; - } + var response = Hardware.RegisterWithLogging(Key); + if (response != eDeviceRegistrationUnRegistrationResponse.Success) + { + //Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response); + return false; + } IsRegistered.FireUpdate(); - } + } + else + { + AddPostActivationAction(() => + { + var response = Hardware.RegisterWithLogging(Key); + + IsRegistered.FireUpdate(); + }); + } foreach (var f in Feedbacks) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs index ba9e6609..bb800b44 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs @@ -8,7 +8,7 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces /// public interface IMobileControl : IKeyed { - void CreateMobileControlRoomBridge(EssentialsRoomBase room, IMobileControl parent); + void CreateMobileControlRoomBridge(IEssentialsRoom room, IMobileControl parent); void LinkSystemMonitorToAppServer(); } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs index aac38dfa..df864550 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs @@ -60,6 +60,7 @@ namespace PepperDash.Essentials.Core DeviceCriticalSection.Enter(); AddDeviceEnabled = false; // PreActivate all devices + Debug.Console(0,"****PreActivation starting...****"); foreach (var d in Devices.Values) { try @@ -69,9 +70,12 @@ namespace PepperDash.Essentials.Core } catch (Exception e) { - Debug.Console(0, d, "ERROR: Device PreActivation failure:\r{0}", e); + Debug.Console(0, d, "ERROR: Device {1} PreActivation failure: {0}", e.Message, d.Key); + Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace); } } + Debug.Console(0, "****PreActivation complete****"); + Debug.Console(0, "****Activation starting...****"); // Activate all devices foreach (var d in Devices.Values) @@ -83,10 +87,14 @@ namespace PepperDash.Essentials.Core } catch (Exception e) { - Debug.Console(0, d, "ERROR: Device Activation failure:\r{0}", e); + Debug.Console(0, d, "ERROR: Device {1} Activation failure: {0}", e.Message, d.Key); + Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace); } } + Debug.Console(0, "****Activation complete****"); + Debug.Console(0, "****PostActivation starting...****"); + // PostActivate all devices foreach (var d in Devices.Values) { @@ -97,10 +105,13 @@ namespace PepperDash.Essentials.Core } catch (Exception e) { - Debug.Console(0, d, "ERROR: Device PostActivation failure:\r{0}", e); + Debug.Console(0, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key); + Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace); } } + Debug.Console(0, "****PostActivation complete****"); + OnAllDevicesActivated(); } finally diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs index 46fa819b..a72bd282 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs @@ -57,7 +57,7 @@ namespace PepperDash.Essentials.Core public DescriptionAttribute(string description) { - Debug.Console(2, "Setting Description: {0}", description); + //Debug.Console(2, "Setting Description: {0}", description); _Description = description; } @@ -74,7 +74,7 @@ namespace PepperDash.Essentials.Core public ConfigSnippetAttribute(string configSnippet) { - Debug.Console(2, "Setting Config Snippet {0}", configSnippet); + //Debug.Console(2, "Setting Config Snippet {0}", configSnippet); _ConfigSnippet = configSnippet; } @@ -103,7 +103,7 @@ namespace PepperDash.Essentials.Core { foreach (var typeName in TypeNames) { - Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); + //Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[]; string description = descriptionAttribute[0].Description; var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs index eb529376..4ac61943 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs @@ -111,7 +111,12 @@ namespace PepperDash.Essentials.Core public override void ExecuteSwitch(object selector) { - Debug.Console(2, this, "ExecuteSwitch: {0}", selector); + Debug.Console(2, this, "ExecuteSwitch: {0}", selector); + + if (!_PowerIsOn) + { + PowerOn(); + } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs index a17e0d91..ebdc87b1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs @@ -69,13 +69,13 @@ namespace PepperDash.Essentials.Core /// public static void AddFactoryForType(string typeName, Func method) { - Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method}); } public static void AddFactoryForType(string typeName, string description, CType cType, Func method) { - Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); if(FactoryMethods.ContainsKey(typeName)) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs index f46b4767..055ed5b6 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs @@ -62,6 +62,11 @@ namespace PepperDash.Essentials.Core ValueFunc = valueFunc; } + public void SetValueFunc(Func newFunc) + { + ValueFunc = newFunc; + } + public override void FireUpdate() { bool newValue = InTestMode ? TestValue : ValueFunc.Invoke(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs index 25390c2c..53bae09a 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs @@ -51,6 +51,12 @@ namespace PepperDash.Essentials.Core ValueFunc = valueFunc; } + public void SetValueFunc(Func newFunc) + { + ValueFunc = newFunc; + } + + public override void FireUpdate() { var newValue = InTestMode ? TestValue : ValueFunc.Invoke(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs index 56251a2e..fb5cccb5 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs @@ -52,7 +52,10 @@ namespace PepperDash.Essentials.Core ValueFunc = valueFunc; } - + public void SetValueFunc(Func newFunc) + { + ValueFunc = newFunc; + } public override void FireUpdate() { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index a172ab49..1bf925d6 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -119,9 +119,21 @@ namespace PepperDash.Essentials.Core.Fusion var slot = Global.ControlSystem.ProgramNumber; var guidFilePath = Global.FilePathPrefix + - string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); + string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _ipId); - _guidFileExists = File.Exists(guidFilePath); + var oldGuidFilePath = Global.FilePathPrefix + + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); + + if (File.Exists(oldGuidFilePath)) + { + Debug.Console(0, this, "Migrating from old Fusion GUID file to new Fusion GUID File"); + + File.Copy(oldGuidFilePath, guidFilePath); + + File.Delete(oldGuidFilePath); + } + + _guidFileExists = File.Exists(guidFilePath); // Check if file exists if (!_guidFileExists) @@ -149,19 +161,7 @@ namespace PepperDash.Essentials.Core.Fusion } - AddPostActivationAction(() => - { - CreateSymbolAndBasicSigs(_ipId); - SetUpSources(); - SetUpCommunitcationMonitors(); - SetUpDisplay(); - SetUpError(); - ExecuteCustomSteps(); - - FusionRVI.GenerateFileForAllFusionDevices(); - - GenerateGuidFile(guidFilePath); - }); + AddPostActivationAction(() => PostActivate(guidFilePath)); } catch (Exception e) { @@ -169,6 +169,20 @@ namespace PepperDash.Essentials.Core.Fusion } } + private void PostActivate(string guidFilePath) + { + CreateSymbolAndBasicSigs(_ipId); + SetUpSources(); + SetUpCommunitcationMonitors(); + SetUpDisplay(); + SetUpError(); + ExecuteCustomSteps(); + + FusionRVI.GenerateFileForAllFusionDevices(); + + GenerateGuidFile(guidFilePath); + } + protected string RoomGuid { get { return _guiDs.RoomGuid; } @@ -314,7 +328,7 @@ namespace PepperDash.Essentials.Core.Fusion protected virtual void CreateSymbolAndBasicSigs(uint ipId) { - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId); FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs index 121bc9b7..9d792437 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs @@ -32,6 +32,27 @@ namespace PepperDash.Essentials.Core // TODO: consider making this configurable later public static IFormatProvider Culture = CultureInfo.CreateSpecificCulture("en-US"); + /// + /// True when the processor type is a DMPS variant + /// + public static bool ControlSystemIsDmpsType + { + get + { + return ControlSystem.ControllerPrompt.ToLower().IndexOf("dmps") > -1; + } + } + + /// + /// True when the processor type is a DMPS 4K variant + /// + public static bool ControlSystemIsDmps4kType + { + get + { + return ControlSystemIsDmpsType && ControlSystem.ControllerPrompt.ToLower().IndexOf("4k") > -1; + } + } /// /// The file path prefix to the folder containing configuration files diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/GenericCommunicationMonitor.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/GenericCommunicationMonitor.cs index 0d19762f..edb4c31b 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/GenericCommunicationMonitor.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/GenericCommunicationMonitor.cs @@ -16,11 +16,28 @@ namespace PepperDash.Essentials.Core /// /// Used for monitoring comms that are IBasicCommunication. Will send a poll string and provide an event when /// statuses change. + /// Default monitoring uses TextReceived event on Client. /// public class GenericCommunicationMonitor : StatusMonitorBase { public IBasicCommunication Client { get; private set; } + /// + /// Will monitor Client.BytesReceived if set to true. Otherwise the default is to monitor Client.TextReceived + /// + public bool MonitorBytesReceived { get; private set; } + + /// + /// Return true if the Client is ISocketStatus + /// + public bool IsSocket + { + get + { + return Client is ISocketStatus; + } + } + long PollTime; CTimer PollTimer; string PollString; @@ -46,8 +63,20 @@ namespace PepperDash.Essentials.Core Client = client; PollTime = pollTime; PollString = pollString; + + if (IsSocket) + { + (Client as ISocketStatus).ConnectionChange += new EventHandler(socket_ConnectionChange); + } } + public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, + long warningTime, long errorTime, string pollString, bool monitorBytesReceived) : + this(parent, client, pollTime, warningTime, errorTime, pollString) + { + SetMonitorBytesReceived(monitorBytesReceived); + } + /// /// Poll is a provided action instead of string /// @@ -69,6 +98,19 @@ namespace PepperDash.Essentials.Core Client = client; PollTime = pollTime; PollAction = pollAction; + + if (IsSocket) + { + (Client as ISocketStatus).ConnectionChange += new EventHandler(socket_ConnectionChange); + } + + } + + public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, + long warningTime, long errorTime, Action pollAction, bool monitorBytesReceived) : + this(parent, client, pollTime, warningTime, errorTime, pollAction) + { + SetMonitorBytesReceived(monitorBytesReceived); } @@ -79,23 +121,96 @@ namespace PepperDash.Essentials.Core CommunicationMonitorConfig props) : this(parent, client, props.PollInterval, props.TimeToWarning, props.TimeToError, props.PollString) { + if (IsSocket) + { + (Client as ISocketStatus).ConnectionChange += new EventHandler(socket_ConnectionChange); + } } + /// + /// Builds the monitor from a config object and takes a bool to specify whether to monitor BytesReceived + /// Default is to monitor TextReceived + /// + /// + /// + /// + /// + public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, CommunicationMonitorConfig props, bool monitorBytesReceived) : + this(parent, client, props.PollInterval, props.TimeToWarning, props.TimeToError, props.PollString) + { + SetMonitorBytesReceived(monitorBytesReceived); + } + + void SetMonitorBytesReceived(bool monitorBytesReceived) + { + MonitorBytesReceived = monitorBytesReceived; + } + public override void Start() { - Client.BytesReceived += Client_BytesReceived; - Poll(); - PollTimer = new CTimer(o => Poll(), null, PollTime, PollTime); + if (MonitorBytesReceived) + { + Client.BytesReceived += Client_BytesReceived; + } + else + { + Client.TextReceived += Client_TextReceived; + } + + if (!IsSocket) + { + BeginPolling(); + } } + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + if (!e.Client.IsConnected) + { + // Immediately stop polling and notify that device is offline + Stop(); + Status = MonitorStatus.InError; + ResetErrorTimers(); + } + else + { + // Start polling and set status to unknow and let poll result update the status to IsOk when a response is received + Status = MonitorStatus.StatusUnknown; + Start(); + BeginPolling(); + } + } + + void BeginPolling() + { + Poll(); + PollTimer = new CTimer(o => Poll(), null, PollTime, PollTime); + } + public override void Stop() { - Client.BytesReceived -= this.Client_BytesReceived; - PollTimer.Stop(); - PollTimer = null; - StopErrorTimers(); + if(MonitorBytesReceived) + { + Client.BytesReceived -= this.Client_BytesReceived; + } + else + { + Client.TextReceived -= Client_TextReceived; + } + + if (PollTimer != null) + { + PollTimer.Stop(); + PollTimer = null; + StopErrorTimers(); + } } + void Client_TextReceived(object sender, GenericCommMethodReceiveTextArgs e) + { + DataReceived(); + } + /// /// Upon any receipt of data, set everything to ok! /// @@ -103,10 +218,14 @@ namespace PepperDash.Essentials.Core /// void Client_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e) { - Status = MonitorStatus.IsOk; - ResetErrorTimers(); - // - } + DataReceived(); + } + + void DataReceived() + { + Status = MonitorStatus.IsOk; + ResetErrorTimers(); + } void Poll() { @@ -124,19 +243,6 @@ namespace PepperDash.Essentials.Core Debug.Console(2, this, "Comm not connected"); } } - - /// - /// When the client connects, and we're waiting for it, respond and disconect from event - /// - void OneTimeConnectHandler(object o, EventArgs a) - { - if (Client.IsConnected) - { - //Client.IsConnected -= OneTimeConnectHandler; - Debug.Console(2, this, "Comm connected"); - Poll(); - } - } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs index ec18c995..056686b1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs @@ -301,7 +301,7 @@ namespace PepperDash.Essentials.Core.Monitoring p.Value.AggregatedProgramInfoFeedback.LinkInputSig( trilist.StringInput[programSlotJoinStart + joinMap.AggregatedProgramInfo.JoinNumber]); - programSlotJoinStart = programSlotJoinStart + joinMap.ProgramOffsetJoin.JoinNumber; + programSlotJoinStart = programSlotJoinStart + joinMap.ProgramOffsetJoin.JoinSpan; } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/CenOdtOccupancySensorBaseController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/CenOdtOccupancySensorBaseController.cs index dcf0b193..eae2f993 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/CenOdtOccupancySensorBaseController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/CenOdtOccupancySensorBaseController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Resources; using System.Text; using Crestron.SimplSharp; using Crestron.SimplSharpPro.DeviceSupport; @@ -61,6 +62,8 @@ namespace PepperDash.Essentials.Core public BoolFeedback RawOccupancyUsFeedback { get; private set; } + public BoolFeedback IdentityModeFeedback { get; private set; } + // Debug properties public bool InTestMode { get; private set; } @@ -117,6 +120,8 @@ namespace PepperDash.Essentials.Core RawOccupancyUsFeedback = new BoolFeedback(() => OccSensor.RawOccupancyDetectedByUltrasonicSensorFeedback.BoolValue); + IdentityModeFeedback = new BoolFeedback(()=>OccSensor.IdentityModeOnFeedback.BoolValue); + UltrasonicSensitivityInVacantStateFeedback = new IntFeedback(() => (int)OccSensor.UltrasonicSensorSensitivityInVacantStateFeedback); UltrasonicSensitivityInOccupiedStateFeedback = new IntFeedback(() => (int)OccSensor.UltrasonicSensorSensitivityInOccupiedStateFeedback); @@ -199,6 +204,27 @@ namespace PepperDash.Essentials.Core { SetAndWhenVacatedState((bool)PropertiesConfig.AndWhenVacatedState); } + + // TODO [ ] feature/cenoodtcpoe-sensor-sensitivity-configuration + if (PropertiesConfig.UsSensitivityOccupied != null) + { + SetUsSensitivityOccupied((ushort)PropertiesConfig.UsSensitivityOccupied); + } + + if (PropertiesConfig.UsSensitivityVacant != null) + { + SetUsSensitivityVacant((ushort)PropertiesConfig.UsSensitivityVacant); + } + + if (PropertiesConfig.PirSensitivityOccupied != null) + { + SetPirSensitivityOccupied((ushort)PropertiesConfig.PirSensitivityOccupied); + } + + if (PropertiesConfig.PirSensitivityVacant != null) + { + SetPirSensitivityVacant((ushort)PropertiesConfig.PirSensitivityVacant); + } } /// @@ -279,7 +305,21 @@ namespace PepperDash.Essentials.Core } } - /// + /// + /// Sets the identity mode on or off + /// + /// + public void SetIdentityMode(bool state) + { + if (state) + OccSensor.IdentityModeOn(); + else + OccSensor.IdentityModeOff(); + + Debug.Console(1, this, "Identity Mode: {0}", OccSensor.IdentityModeOnFeedback.BoolValue ? "On" : "Off"); + } + + /// /// Enables or disables the PIR sensor /// /// @@ -506,6 +546,54 @@ namespace PepperDash.Essentials.Core } } + /// + /// Sets the US sensor sensitivity for occupied state + /// + /// + public void SetUsSensitivityOccupied(ushort sensitivity) + { + var level = (eSensitivityLevel) sensitivity; + if (level == 0) return; + + OccSensor.UltrasonicSensorSensitivityInOccupiedState = level; + } + + /// + /// Sets the US sensor sensitivity for vacant state + /// + /// + public void SetUsSensitivityVacant(ushort sensitivity) + { + var level = (eSensitivityLevel)sensitivity; + if (level == 0) return; + + OccSensor.UltrasonicSensorSensitivityInVacantState = level; + } + + /// + /// Sets the PIR sensor sensitivity for occupied state + /// + /// + public void SetPirSensitivityOccupied(ushort sensitivity) + { + var level = (eSensitivityLevel)sensitivity; + if (level == 0) return; + + OccSensor.PassiveInfraredSensorSensitivityInOccupiedState = level; + } + + /// + /// Sets the PIR sensor sensitivity for vacant state + /// + /// + public void SetPirSensitivityVacant(ushort sensitivity) + { + var level = (eSensitivityLevel)sensitivity; + if (level == 0) return; + + OccSensor.PassiveInfraredSensorSensitivityInVacantState = level; + } + /// /// Method to print current settings to console /// @@ -647,8 +735,11 @@ namespace PepperDash.Essentials.Core //Sensor Raw States occController.RawOccupancyPirFeedback.LinkInputSig(trilist.BooleanInput[joinMap.RawOccupancyPirFeedback.JoinNumber]); - occController.RawOccupancyUsFeedback.LinkInputSig(trilist.BooleanInput[joinMap.RawOccupancyUsFeedback.JoinNumber]); - + occController.RawOccupancyUsFeedback.LinkInputSig(trilist.BooleanInput[joinMap.RawOccupancyUsFeedback.JoinNumber]); + + // Identity mode + trilist.SetBoolSigAction(joinMap.IdentityMode.JoinNumber, occController.SetIdentityMode); + occController.IdentityModeFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IdentityModeFeedback.JoinNumber]); } public class CenOdtOccupancySensorBaseControllerFactory : EssentialsDeviceFactory diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/GlsOccupancySensorPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/GlsOccupancySensorPropertiesConfig.cs index 1f21b2a8..392c05b0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/GlsOccupancySensorPropertiesConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/GlsOccupancySensorPropertiesConfig.cs @@ -47,5 +47,35 @@ namespace PepperDash.Essentials.Core [JsonProperty("andWhenVacatedState")] public bool? AndWhenVacatedState { get; set; } + + // PoE Sensors: CenOdtCPoe + + /// + /// Sets the sensitivity level for US while sensor is in occupied state + /// 1 = low; 2 = medium; 3 = high; 4 = xlow; 5 = 2xlow; 6 = 3xlow + /// + [JsonProperty("usSensitivityOccupied")] + public ushort? UsSensitivityOccupied { get; set; } + + /// + /// Sets the sensitivity level for US while sensor is in vacant state + /// 1 = low; 2 = medium; 3 = high; 4 = xlow; 5 = 2xlow; 6 = 3xlow + /// + [JsonProperty("usSensitivityVacant")] + public ushort? UsSensitivityVacant { get; set; } + + /// + /// Sets the sensitivity level for PIR while sensor is in occupied state + /// 1 = low; 2 = medium; 3 = high + /// + [JsonProperty("pirSensitivityOccupied")] + public ushort? PirSensitivityOccupied { get; set; } + + /// + /// Sets the sensitivity level for PIR while sensor is in vacant state + /// 1 = low; 2 = medium; 3 = high + /// + [JsonProperty("pirSensitivityVacant")] + public ushort? PirSensitivityVacant { get; set; } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs new file mode 100644 index 00000000..7066be0e --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Represents an abstract controller device for a partition dividing rooms that are combinable + /// + /// In Auto mode, it can use a partition sensor to automatically determine whether the partition is present. + /// + /// In Manual mode it accepts user input to tell it whether the partition is present. + /// + public class EssentialsPartitionController : IPartitionController + { + private IPartitionStateProvider _partitionSensor; + + private bool isInAutoMode; + + private bool partitionPresent; + + public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List adjacentRoomKeys) + { + Key = key; + + Name = name; + + AdjacentRoomKeys = adjacentRoomKeys; + + if (sensor != null) + { + _partitionSensor = sensor; + + if (!defaultToManualMode) + { + SetAutoMode(); + } + else + { + SetManualMode(); + } + } + else + { + SetManualMode(); + } + + PartitionPresentFeedback.FireUpdate(); + } + + void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + if (isInAutoMode) + { + PartitionPresentFeedback.FireUpdate(); + } + } + + #region IPartitionController Members + + public List AdjacentRoomKeys { get; private set; } + + public void SetAutoMode() + { + isInAutoMode = true; + if (PartitionPresentFeedback != null) + { + PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue); + } + else + { + PartitionPresentFeedback = new BoolFeedback(() => _partitionSensor.PartitionPresentFeedback.BoolValue); + } + + if (_partitionSensor != null) + { + _partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange; + } + } + + public void SetManualMode() + { + isInAutoMode = false; + if (PartitionPresentFeedback != null) + { + PartitionPresentFeedback.SetValueFunc(() => partitionPresent); + } + else + { + PartitionPresentFeedback = new BoolFeedback(() => partitionPresent); + } + + if (_partitionSensor != null) + { + _partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange; + } + } + + + public void SetPartitionStatePresent() + { + if (!isInAutoMode) + { + partitionPresent = true; + PartitionPresentFeedback.FireUpdate(); + } + } + + public void SetPartitionStateNotPresent() + { + if (!isInAutoMode) + { + partitionPresent = false; + PartitionPresentFeedback.FireUpdate(); + } + } + + public void ToggglePartitionState() + { + if (!isInAutoMode) + { + partitionPresent = !partitionPresent; + PartitionPresentFeedback.FireUpdate(); + } + } + + #endregion + + #region IPartitionStateProvider Members + + public BoolFeedback PartitionPresentFeedback { get; private set; } + + #endregion + + #region IKeyName Members + + public string Name { get; private set; } + + #endregion + + #region IKeyed Members + + public string Key { get; private set; } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs index bd67dfec..16b2f265 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs @@ -1,4 +1,5 @@ -using Crestron.SimplSharpPro; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.GeneralIO; using Newtonsoft.Json; @@ -9,17 +10,20 @@ using PepperDash.Essentials.Core.Bridges.JoinMaps; using System; using System.Collections.Generic; using PepperDash.Essentials.Core.Config; +using PepperDash_Essentials_Core.PartitionSensor; namespace PepperDash.Essentials.Core { [Description("Wrapper class for GLS Cresnet Partition Sensor")] - public class GlsPartitionSensorController : CrestronGenericBridgeableBaseDevice + public class GlsPartitionSensorController : CrestronGenericBridgeableBaseDevice, IPartitionStateProvider { - private GlsPartCn _partitionSensor; - public StringFeedback NameFeedback { get; private set; } + public GlsPartitionSensorPropertiesConfig PropertiesConfig { get; private set; } + + private GlsPartCn _partitionSensor; + public BoolFeedback EnableFeedback { get; private set; } - public BoolFeedback PartitionSensedFeedback { get; private set; } + public BoolFeedback PartitionPresentFeedback { get; private set; } public BoolFeedback PartitionNotSensedFeedback { get; private set; } public IntFeedback SensitivityFeedback { get; private set; } @@ -32,23 +36,71 @@ namespace PepperDash.Essentials.Core public GlsPartitionSensorController(string key, Func preActivationFunc, DeviceConfig config) : base(key, config.Name) { + + var props = config.Properties.ToObject(); + if (props != null) + { + PropertiesConfig = props; + } + else + { + Debug.Console(1, this, "props are null. Unable to deserialize into GlsPartSensorPropertiesConfig"); + } + AddPreActivationAction(() => { _partitionSensor = preActivationFunc(config); - + RegisterCrestronGenericBase(_partitionSensor); + + EnableFeedback = new BoolFeedback(() => InTestMode ? TestEnableFeedback : _partitionSensor.EnableFeedback.BoolValue); + PartitionPresentFeedback = new BoolFeedback(() => InTestMode ? TestPartitionSensedFeedback : _partitionSensor.PartitionSensedFeedback.BoolValue); + PartitionNotSensedFeedback = new BoolFeedback(() => InTestMode ? !TestPartitionSensedFeedback : _partitionSensor.PartitionNotSensedFeedback.BoolValue); + SensitivityFeedback = new IntFeedback(() => InTestMode ? TestSensitivityFeedback : _partitionSensor.SensitivityFeedback.UShortValue); - NameFeedback = new StringFeedback(() => Name); - EnableFeedback = new BoolFeedback(() => _partitionSensor.EnableFeedback.BoolValue); - PartitionSensedFeedback = new BoolFeedback(() => _partitionSensor.PartitionSensedFeedback.BoolValue); - PartitionNotSensedFeedback = new BoolFeedback(() => _partitionSensor.PartitionNotSensedFeedback.BoolValue); - SensitivityFeedback = new IntFeedback(() => _partitionSensor.SensitivityFeedback.UShortValue); - - if (_partitionSensor != null) _partitionSensor.BaseEvent += PartitionSensor_BaseEvent; + if (_partitionSensor != null) + { + _partitionSensor.BaseEvent += PartitionSensor_BaseEvent; + } }); - } - private void PartitionSensor_BaseEvent(GenericBase device, BaseEventArgs args) + AddPostActivationAction(() => + { + _partitionSensor.OnlineStatusChange += (o, a) => + { + if (a.DeviceOnLine) + { + ApplySettingsToSensorFromConfig(); + } + }; + + if (_partitionSensor.IsOnline) + { + ApplySettingsToSensorFromConfig(); + } + }); + } + + private void ApplySettingsToSensorFromConfig() + { + if (_partitionSensor.IsOnline == false) return; + + Debug.Console(1, this, "Attempting to apply settings to sensor from config"); + + if (PropertiesConfig.Sensitivity != null) + { + Debug.Console(1, this, "Sensitivity found, attempting to set value '{0}' from config", + PropertiesConfig.Sensitivity); + _partitionSensor.Sensitivity.UShortValue = (ushort) PropertiesConfig.Sensitivity; + } + else + { + Debug.Console(1, this, "Sensitivity null, no value specified in config"); + } + + } + + private void PartitionSensor_BaseEvent(GenericBase device, BaseEventArgs args) { Debug.Console(2, this, "EventId: {0}, Index: {1}", args.EventId, args.Index); @@ -61,11 +113,13 @@ namespace PepperDash.Essentials.Core } case (GlsPartCn.PartitionSensedFeedbackEventId): { - PartitionSensedFeedback.FireUpdate(); + Debug.Console(1, this, "Partition Sensed State: {0}", _partitionSensor.PartitionSensedFeedback.BoolValue); + PartitionPresentFeedback.FireUpdate(); break; } case (GlsPartCn.PartitionNotSensedFeedbackEventId): { + Debug.Console(1, this, "Partition Not Sensed State: {0}", _partitionSensor.PartitionNotSensedFeedback.BoolValue); PartitionNotSensedFeedback.FireUpdate(); break; } @@ -73,7 +127,7 @@ namespace PepperDash.Essentials.Core { SensitivityFeedback.FireUpdate(); break; - } + } default: { Debug.Console(2, this, "Unhandled args.EventId: {0}", args.EventId); @@ -93,6 +147,9 @@ namespace PepperDash.Essentials.Core if (InTestMode) { TestEnableFeedback = state; + + EnableFeedback.FireUpdate(); + Debug.Console(1, this, "TestEnableFeedback: {0}", TestEnableFeedback.ToString()); return; } @@ -105,6 +162,10 @@ namespace PepperDash.Essentials.Core if (InTestMode) { TestPartitionSensedFeedback = state; + + PartitionPresentFeedback.FireUpdate(); + PartitionNotSensedFeedback.FireUpdate(); + Debug.Console(1, this, "TestPartitionSensedFeedback: {0}", TestPartitionSensedFeedback.ToString()); return; } @@ -117,6 +178,8 @@ namespace PepperDash.Essentials.Core if (InTestMode) { TestSensitivityFeedback = value; + + SensitivityFeedback.FireUpdate(); Debug.Console(1, this, "TestSensitivityFeedback: {0}", TestSensitivityFeedback); return; } @@ -124,7 +187,22 @@ namespace PepperDash.Essentials.Core Debug.Console(1, this, "InTestMode: {0}, unable to set sensitivity value: {1}", InTestMode.ToString(), value); } - public void SetEnableState(bool state) + public void GetSettings() + { + var dash = new string('*', 50); + CrestronConsole.PrintLine(string.Format("{0}\n", dash)); + + Debug.Console(0, this, "Enabled State: {0}", _partitionSensor.EnableFeedback.BoolValue); + + Debug.Console(0, this, "Partition Sensed State: {0}", _partitionSensor.PartitionSensedFeedback.BoolValue); + Debug.Console(0, this, "Partition Not Sensed State: {0}", _partitionSensor.PartitionNotSensedFeedback.BoolValue); + + Debug.Console(0, this, "Sensitivity Value: {0}", _partitionSensor.SensitivityFeedback.UShortValue); + + CrestronConsole.PrintLine(string.Format("{0}\n", dash)); + } + + public void SetEnableState(bool state) { Debug.Console(2, this, "Sensor is {0}, SetEnableState: {1}", _partitionSensor == null ? "null" : "not null", state); if (_partitionSensor == null) @@ -180,18 +258,20 @@ namespace PepperDash.Essentials.Core Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); Debug.Console(0, this, "Linking to Bridge Type {0}", GetType().Name); - // link input from simpl + IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + trilist.StringInput[joinMap.Name.JoinNumber].StringValue = _partitionSensor.Name; + trilist.SetBoolSigAction(joinMap.Enable.JoinNumber, SetEnableState); + EnableFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Enable.JoinNumber]); + + PartitionPresentFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionSensed.JoinNumber]); + PartitionNotSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionNotSensed.JoinNumber]); + trilist.SetSigTrueAction(joinMap.IncreaseSensitivity.JoinNumber, IncreaseSensitivity); trilist.SetSigTrueAction(joinMap.DecreaseSensitivity.JoinNumber, DecreaseSensitivity); - trilist.SetUShortSigAction(joinMap.Sensitivity.JoinNumber, SetSensitivity); - // link output to simpl - IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - EnableFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Enable.JoinNumber]); - PartitionSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionSensed.JoinNumber]); - PartitionNotSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionNotSensed.JoinNumber]); - SensitivityFeedback.LinkInputSig(trilist.UShortInput[joinMap.Sensitivity.JoinNumber]); + SensitivityFeedback.LinkInputSig(trilist.UShortInput[joinMap.Sensitivity.JoinNumber]); + trilist.SetUShortSigAction(joinMap.Sensitivity.JoinNumber, SetSensitivity); FeedbacksFireUpdates(); @@ -209,6 +289,7 @@ namespace PepperDash.Essentials.Core { if (a.DeviceOnLine) { + trilist.StringInput[joinMap.Name.JoinNumber].StringValue = _partitionSensor.Name; FeedbacksFireUpdates(); } }; @@ -216,10 +297,9 @@ namespace PepperDash.Essentials.Core private void FeedbacksFireUpdates() { - IsOnline.FireUpdate(); - NameFeedback.FireUpdate(); + IsOnline.FireUpdate(); EnableFeedback.FireUpdate(); - PartitionSensedFeedback.FireUpdate(); + PartitionPresentFeedback.FireUpdate(); PartitionNotSensedFeedback.FireUpdate(); SensitivityFeedback.FireUpdate(); } @@ -260,7 +340,7 @@ namespace PepperDash.Essentials.Core public override EssentialsDevice BuildDevice(DeviceConfig dc) { - Debug.Console(1, "Factory Attempting to create new C2N-RTHS Device"); + Debug.Console(1, "Factory Attempting to create new GlsPartitionSensorController Device"); return new GlsPartitionSensorController(dc.Key, GetGlsPartCnDevice, dc); } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs new file mode 100644 index 00000000..8a303662 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorPropertiesConfig.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; + +namespace PepperDash_Essentials_Core.PartitionSensor +{ + public class GlsPartitionSensorPropertiesConfig + { + /// + /// Sets the sensor sensitivity + /// + /// + /// The sensitivity range shall be between 1(lowest) to 10 (highest). + /// + [JsonProperty("sensitivity")] + public ushort? Sensitivity { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs new file mode 100644 index 00000000..adb420b7 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Describes the functionality of a device that senses and provides partition state + /// + public interface IPartitionStateProvider : IKeyName + { + BoolFeedback PartitionPresentFeedback { get; } + } + + /// + /// Describes the functionality of a device that can provide partition state either manually via user input or optionally via a sensor state + /// + public interface IPartitionController : IPartitionStateProvider + { + List AdjacentRoomKeys { get; } + + void SetPartitionStatePresent(); + + void SetPartitionStateNotPresent(); + + void ToggglePartitionState(); + + void SetManualMode(); + + void SetAutoMode(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 85c66a13..cdf6406c 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -131,6 +131,7 @@ + @@ -182,6 +183,7 @@ + @@ -235,6 +237,9 @@ + + + @@ -290,6 +295,10 @@ + + + + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs index 7437d75a..136303e3 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs @@ -123,7 +123,7 @@ namespace PepperDash.Essentials /// static LoadedAssembly LoadAssembly(string filePath) { - Debug.Console(2, "Attempting to load {0}", filePath); + //Debug.Console(2, "Attempting to load {0}", filePath); var assembly = Assembly.LoadFrom(filePath); if (assembly != null) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs index f6b0794c..9080435e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs @@ -118,7 +118,7 @@ namespace PepperDash.Essentials.Core.Queues /// public GenericQueue(string key, int pacing, Thread.eThreadPriority priority, int capacity) : this(key, priority, capacity, pacing) - { + { } /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs new file mode 100644 index 00000000..820d363f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public class EssentialsRoomCombiner : EssentialsDevice, IEssentialsRoomCombiner + { + private EssentialsRoomCombinerPropertiesConfig _propertiesConfig; + + private IRoomCombinationScenario _currentScenario; + + private List _rooms; + + private bool isInAutoMode; + + private CTimer _scenarioChangeDebounceTimer; + + private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s + + public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props) + : base(key) + { + _propertiesConfig = props; + + Partitions = new List(); + RoomCombinationScenarios = new List(); + + if (_propertiesConfig.ScenarioChangeDebounceTimeSeconds > 0) + { + _scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds; + } + + IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode); + + // default to auto mode + isInAutoMode = true; + + if (_propertiesConfig.defaultToManualMode) + { + isInAutoMode = false; + } + + IsInAutoModeFeedback.FireUpdate(); + + CreateScenarios(); + + AddPostActivationAction(() => + { + SetupPartitionStateProviders(); + + SetRooms(); + + if (isInAutoMode) + { + DetermineRoomCombinationScenario(); + } + else + { + SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey); + } + }); + } + + void CreateScenarios() + { + foreach (var scenarioConfig in _propertiesConfig.Scenarios) + { + var scenario = new RoomCombinationScenario(scenarioConfig); + RoomCombinationScenarios.Add(scenario); + } + } + + void SetRooms() + { + _rooms = new List(); + + foreach (var roomKey in _propertiesConfig.RoomKeys) + { + var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom; + if (room != null) + { + _rooms.Add(room); + } + } + } + + void SetupPartitionStateProviders() + { + foreach (var pConfig in _propertiesConfig.Partitions) + { + var sensor = DeviceManager.GetDeviceForKey(pConfig.DeviceKey) as IPartitionStateProvider; + + var partition = new EssentialsPartitionController(pConfig.Key, pConfig.Name, sensor, _propertiesConfig.defaultToManualMode, pConfig.AdjacentRoomKeys); + + partition.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange; + + Partitions.Add(partition); + } + } + + void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + StartDebounceTimer(); + } + + void StartDebounceTimer() + { + var time = _scenarioChangeDebounceTimeSeconds * 1000; + + if (_scenarioChangeDebounceTimer == null) + { + _scenarioChangeDebounceTimer = new CTimer((o) => DetermineRoomCombinationScenario(), time); + } + else + { + _scenarioChangeDebounceTimer.Reset(time); + } + } + + /// + /// Determines the current room combination scenario based on the state of the partition sensors + /// + void DetermineRoomCombinationScenario() + { + if (_scenarioChangeDebounceTimer != null) + { + _scenarioChangeDebounceTimer.Dispose(); + _scenarioChangeDebounceTimer = null; + } + + var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) => + { + // iterate the partition states + foreach (var partitionState in s.PartitionStates) + { + // get the partition by key + var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey)); + + if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue) + { + // the partition can't be found or the state doesn't match + return false; + } + } + // if it hasn't returned false by now we have the matching scenario + return true; + }); + + if (currentScenario != null) + { + CurrentScenario = currentScenario; + } + } + + #region IEssentialsRoomCombiner Members + + public event EventHandler RoomCombinationScenarioChanged; + + public IRoomCombinationScenario CurrentScenario + { + get + { + return _currentScenario; + } + private set + { + if (value != _currentScenario) + { + // Deactivate the old scenario first + if (_currentScenario != null) + { + _currentScenario.Deactivate(); + } + + _currentScenario = value; + + // Activate the new scenario + if (_currentScenario != null) + { + _currentScenario.Activate(); + + Debug.Console(1, this, "Current Scenario: {0}", _currentScenario.Name); + } + + var handler = RoomCombinationScenarioChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + } + } + + public BoolFeedback IsInAutoModeFeedback { get; private set; } + + public void SetAutoMode() + { + isInAutoMode = true; + IsInAutoModeFeedback.FireUpdate(); + } + + public void SetManualMode() + { + isInAutoMode = false; + IsInAutoModeFeedback.FireUpdate(); + } + + public void ToggleMode() + { + isInAutoMode = !isInAutoMode; + IsInAutoModeFeedback.FireUpdate(); + } + + public List RoomCombinationScenarios { get; private set; } + + public List Partitions { get; private set; } + + public void TogglePartitionState(string partitionKey) + { + var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController; + + if (partition != null) + { + partition.ToggglePartitionState(); + } + } + + public void SetRoomCombinationScenario(string scenarioKey) + { + if (isInAutoMode) + { + Debug.Console(0, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first."); + return; + } + + // Get the scenario + var scenario = RoomCombinationScenarios.FirstOrDefault((s) => s.Key.Equals(scenarioKey)); + + + // Set the parition states from the scenario manually + if (scenario != null) + { + Debug.Console(0, this, "Manually setting scenario to '{0}'", scenario.Key); + foreach (var partitionState in scenario.PartitionStates) + { + var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey)); + + if (partition != null) + { + if (partitionState.PartitionPresent) + { + Debug.Console(0, this, "Manually setting state to Present for: '{0}'", partition.Key); + partition.SetPartitionStatePresent(); + } + else + { + Debug.Console(0, this, "Manually setting state to Not Present for: '{0}'", partition.Key); + partition.SetPartitionStateNotPresent(); + } + } + else + { + Debug.Console(1, this, "Unable to find partition with key: '{0}'", partitionState.PartitionKey); + } + } + } + else + { + Debug.Console(1, this, "Unable to find scenario with key: '{0}'", scenarioKey); + } + } + + #endregion + } + + public class EssentialsRoomCombinerFactory : EssentialsDeviceFactory + { + public EssentialsRoomCombinerFactory() + { + TypeNames = new List { "essentialsroomcombiner" }; + } + + public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new EssentialsRoomCombiner Device"); + + var props = dc.Properties.ToObject(); + + return new EssentialsRoomCombiner(dc.Key, props); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs new file mode 100644 index 00000000..6fb15042 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core +{ + /// + /// Config properties for an EssentialsRoomCombiner device + /// + public class EssentialsRoomCombinerPropertiesConfig + { + /// + /// The list of partitions that device the rooms + /// + [JsonProperty("partitions")] + public List Partitions {get; set;} + + /// + /// The list of combinations scenarios for the rooms + /// + [JsonProperty("scenarios")] + public List Scenarios { get; set; } + + /// + /// The list of rooms keys that can be combined + /// + [JsonProperty("roomKeys")] + public List RoomKeys {get; set;} + + /// + /// Set to true to default to manual mode + /// + [JsonProperty("defaultToManualMode")] + public bool defaultToManualMode { get; set; } + + /// + /// The key of the scenario to default to at system startup if in manual mode + /// + [JsonProperty("defaultScenarioKey")] + public string defaultScenarioKey { get; set; } + + [JsonProperty("scenarioChangeDebounceTimeSeconds")] + public int ScenarioChangeDebounceTimeSeconds { get; set; } + } + + /// + /// Config properties for a partition that separates rooms + /// + public class PartitionConfig : IKeyName + { + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Key of the device that implements IPartitionStateProvider to provide the state of the partition + /// + [JsonProperty("deviceKey")] + public string DeviceKey { get; set; } + + /// + /// Keys of the rooms that this partion would be located between + /// + [JsonProperty("adjacentRoomKeys")] + public List AdjacentRoomKeys { get; set; } + } + + /// + /// Config propeties for a room combination scenario + /// + public class RoomCombinationScenarioConfig : IKeyName + { + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("partitionStates")] + public List PartitionStates { get; set; } + + /// + /// Determines which UI devices get mapped to which room in this scenario. The Key should be the key of the UI device and the Value should be the key of the room to map to + /// + [JsonProperty("uiMap")] + public Dictionary UiMap { get; set; } + + [JsonProperty("activationActions")] + public List ActivationActions { get; set; } + + [JsonProperty("deactivationActions")] + public List DeactivationActions { get; set; } + } + + /// + /// Config properties to represent the state of a partition sensor in a RoomCombinationScenario + /// + public class PartitionState + { + [JsonProperty("partitionKey")] + public string PartitionKey { get; set; } + + [JsonProperty("partitionSensedState")] + public bool PartitionPresent { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs new file mode 100644 index 00000000..e6bdd983 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Describes the functionality for an EssentailsRoomCombiner device + /// + public interface IEssentialsRoomCombiner : IKeyed + { + /// + /// Indicates that the room combination scenario has changed + /// + event EventHandler RoomCombinationScenarioChanged; + + /// + /// The current room combination scenario + /// + IRoomCombinationScenario CurrentScenario { get; } + + /// + /// When true, indicates the current mode is auto mode + /// + BoolFeedback IsInAutoModeFeedback {get;} + + /// + /// Sets auto mode + /// + void SetAutoMode(); + + /// + /// Sets manual mode + /// + void SetManualMode(); + + /// + /// Toggles the current mode between auto and manual + /// + void ToggleMode(); + + /// + /// The available room combinatino scenarios + /// + List RoomCombinationScenarios { get; } + + /// + /// The partition + /// + List Partitions { get; } + + /// + /// Toggles the state of a manual partition sensor + /// + /// + void TogglePartitionState(string partitionKey); + + /// + /// Sets the room combination scenario (if in manual mode) + /// + /// + void SetRoomCombinationScenario(string scenarioKey); + } + + public interface IRoomCombinationScenario : IKeyName + { + /// + /// When true, indicates that this room combination scenario is active + /// + BoolFeedback IsActiveFeedback { get; } + + /// + /// Activates this room combination scenario + /// + void Activate(); + + /// + /// Deactivates this room combination scenario + /// + void Deactivate(); + + /// + /// The state of the partitions that would activate this scenario + /// + List PartitionStates { get; } + + /// + /// The mapping of UIs by key to rooms by key + /// + Dictionary UiMap { get; set; } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs new file mode 100644 index 00000000..460241a0 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core +{ + /// + /// Represents a room combination scenario + /// + public class RoomCombinationScenario: IRoomCombinationScenario + { + private RoomCombinationScenarioConfig _config; + + public string Key { get; set; } + + public string Name { get; set; } + + public List PartitionStates { get; private set; } + + public Dictionary UiMap { get; set; } + + private bool _isActive; + + public BoolFeedback IsActiveFeedback { get; private set; } + + private List activationActions; + + private List deactivationActions; + + public RoomCombinationScenario(RoomCombinationScenarioConfig config) + { + Key = config.Key; + + Name = config.Name; + + PartitionStates = config.PartitionStates; + + UiMap = config.UiMap; + + activationActions = config.ActivationActions; + + deactivationActions = config.DeactivationActions; + + _config = config; + + IsActiveFeedback = new BoolFeedback(() => _isActive); + } + + public void Activate() + { + Debug.Console(1, "Activating Scenario: '{0}' with {1} action(s) defined", Name, activationActions.Count); + + if (activationActions != null) + { + foreach (var action in activationActions) + { + DeviceJsonApi.DoDeviceAction(action); + } + } + + _isActive = true; + IsActiveFeedback.FireUpdate(); + } + + public void Deactivate() + { + Debug.Console(1, "Deactivating Scenario: '{0}' with {1} action(s) defined", Name, deactivationActions.Count); + + if (deactivationActions != null) + { + foreach (var action in deactivationActions) + { + DeviceJsonApi.DoDeviceAction(action); + } + } + + _isActive = false; + IsActiveFeedback.FireUpdate(); + } + + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs index d9cadb24..bfbb73f0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs @@ -206,6 +206,27 @@ namespace PepperDash.Essentials.Core return ClearSigAction(tl.StringOutput[sigNum]) as StringOutputSig; } + /// + /// Clears all actions on all sigs + /// + public static void ClearAllSigActions(this BasicTriList t1) + { + foreach (var sig in t1.BooleanOutput) + { + ClearSigAction(sig); + } + + foreach (var sig in t1.UShortOutput) + { + ClearSigAction(sig); + } + + foreach (var sig in t1.StringOutput) + { + ClearSigAction(sig); + } + } + /// /// Helper method to set the value of a bool Sig on TriList /// diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs index 22e792b5..412dee6b 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs @@ -16,7 +16,8 @@ using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.DM.Config; -namespace PepperDash.Essentials.DM { +namespace PepperDash.Essentials.DM +{ /// /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions /// @@ -75,8 +76,10 @@ namespace PepperDash.Essentials.DM { /// Factory method to create a new chassis controller from config data. Limited to 8x8 right now /// public static DmBladeChassisController GetDmChassisController(string key, string name, - string type, DMChassisPropertiesConfig properties) { - try { + string type, DMChassisPropertiesConfig properties) + { + try + { type = type.ToLower(); uint ipid = properties.Control.IpIdInt; @@ -85,7 +88,8 @@ namespace PepperDash.Essentials.DM { else if (type == "dmmd128x128") { chassis = new DmMd128x128(ipid, Global.ControlSystem); } - if (chassis == null) { + if (chassis == null) + { return null; } @@ -93,11 +97,13 @@ namespace PepperDash.Essentials.DM { // add the cards and port names foreach (var kvp in properties.InputSlots) controller.AddInputBlade(kvp.Value, kvp.Key); - foreach (var kvp in properties.OutputSlots) { + foreach (var kvp in properties.OutputSlots) + { controller.AddOutputBlade(kvp.Value, kvp.Key); } - foreach (var kvp in properties.VolumeControls) { + foreach (var kvp in properties.VolumeControls) + { // get the card // check it for an audio-compatible type // make a something-something that will make it work @@ -123,7 +129,8 @@ namespace PepperDash.Essentials.DM { controller.PropertiesConfig = properties; return controller; } - catch (System.Exception e) { + catch (System.Exception e) + { Debug.Console(0, "Error creating DM chassis:\r{0}", e); } return null; @@ -137,7 +144,8 @@ namespace PepperDash.Essentials.DM { /// /// public DmBladeChassisController(string key, string name, BladeSwitch chassis) - : base(key, name, chassis) { + : base(key, name, chassis) + { Chassis = chassis; InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); @@ -161,68 +169,87 @@ namespace PepperDash.Essentials.DM { InputCardHdcpCapabilityFeedbacks = new Dictionary(); InputCardHdcpCapabilityTypes = new Dictionary(); - for (uint x = 1; x <= Chassis.NumberOfOutputs; x++) { + for (uint x = 1; x <= Chassis.NumberOfOutputs; x++) + { var tempX = x; - if (Chassis.Outputs[tempX] != null) { - VideoOutputFeedbacks[tempX] = new IntFeedback(() => { + if (Chassis.Outputs[tempX] != null) + { + VideoOutputFeedbacks[tempX] = new IntFeedback(() => + { if (Chassis.Outputs[tempX].VideoOutFeedback != null) { return (ushort)Chassis.Outputs[tempX].VideoOutFeedback.Number; } else { return 0; }; }); - OutputNameFeedbacks[tempX] = new StringFeedback(() => { - if (Chassis.Outputs[tempX].NameFeedback != null) { + OutputNameFeedbacks[tempX] = new StringFeedback(() => + { + if (Chassis.Outputs[tempX].NameFeedback != null) + { return Chassis.Outputs[tempX].NameFeedback.StringValue; } - else { + else + { return ""; } }); - OutputVideoRouteNameFeedbacks[tempX] = new StringFeedback(() => { - if (Chassis.Outputs[tempX].VideoOutFeedback != null) { + OutputVideoRouteNameFeedbacks[tempX] = new StringFeedback(() => + { + if (Chassis.Outputs[tempX].VideoOutFeedback != null) + { return Chassis.Outputs[tempX].VideoOutFeedback.NameFeedback.StringValue; } - else { + else + { return ""; } }); - OutputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => { + OutputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => + { //if (Chassis.Outputs[tempX].Endpoint != null) // return Chassis.Outputs[tempX].Endpoint.IsOnline; //else - return Chassis.Outputs[tempX].EndpointOnlineFeedback; + return Chassis.Outputs[tempX].EndpointOnlineFeedback; }); } - if (Chassis.Inputs[tempX] != null) { - UsbInputRoutedToFeebacks[tempX] = new IntFeedback(() => { + if (Chassis.Inputs[tempX] != null) + { + UsbInputRoutedToFeebacks[tempX] = new IntFeedback(() => + { if (Chassis.Inputs[tempX].USBRoutedToFeedback != null) { return (ushort)Chassis.Inputs[tempX].USBRoutedToFeedback.Number; } else { return 0; }; }); - VideoInputSyncFeedbacks[tempX] = new BoolFeedback(() => { + VideoInputSyncFeedbacks[tempX] = new BoolFeedback(() => + { if (Chassis.Inputs[tempX].VideoDetectedFeedback != null) return Chassis.Inputs[tempX].VideoDetectedFeedback.BoolValue; else return false; }); - InputNameFeedbacks[tempX] = new StringFeedback(() => { - if (Chassis.Inputs[tempX].NameFeedback != null) { + InputNameFeedbacks[tempX] = new StringFeedback(() => + { + if (Chassis.Inputs[tempX].NameFeedback != null) + { return Chassis.Inputs[tempX].NameFeedback.StringValue; } - else { + else + { return ""; } }); - InputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => { + InputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => + { return Chassis.Inputs[tempX].EndpointOnlineFeedback; }); - InputCardHdcpCapabilityFeedbacks[tempX] = new IntFeedback(() => { + InputCardHdcpCapabilityFeedbacks[tempX] = new IntFeedback(() => + { var inputCard = Chassis.Inputs[tempX]; - if (inputCard.Card is DmHdmi4kInputBladeCard) { + if (inputCard.Card is DmHdmi4kInputBladeCard) + { InputCardHdcpCapabilityTypes[tempX] = eHdcpCapabilityType.Hdcp2_2Support; if ((inputCard.Card as DmHdmi4kInputBladeCard).Hdmi4kInput.HdcpSupportOnFeedback.BoolValue) @@ -231,7 +258,8 @@ namespace PepperDash.Essentials.DM { return 0; } - if (inputCard.Card is DmC4kInputBladeCard) { + if (inputCard.Card is DmC4kInputBladeCard) + { InputCardHdcpCapabilityTypes[tempX] = eHdcpCapabilityType.Hdcp2_2Support; if ((inputCard.Card as DmC4kInputBladeCard).DmInput.HdcpCapabilityFeedback.Equals(eHdcpCapabilityType.HdcpSupportOff)) @@ -252,45 +280,56 @@ namespace PepperDash.Essentials.DM { /// /// /// - public void AddInputBlade(string type, uint number) { + public void AddInputBlade(string type, uint number) + { Debug.Console(2, this, "Adding input blade '{0}', slot {1}", type, number); type = type.ToLower(); - if (type == "dmb4kihd") { + if (type == "dmb4kihd") + { var inputBlade = new Dmb4kIHd(number, this.Chassis); - foreach (var item in inputBlade.Inputs) { + foreach (var item in inputBlade.Inputs) + { var card = (item.Card as DmHdmi4kInputBladeCard).Hdmi4kInput; var cecPort = card as ICec; AddHdmiInBladePorts(item.Number, cecPort); } } - else if (type == "dmb4kihddnt") { + else if (type == "dmb4kihddnt") + { var inputBlade = new Dmb4kIHd(number, this.Chassis); - foreach (var item in inputBlade.Inputs) { + foreach (var item in inputBlade.Inputs) + { var card = (item.Card as DmHdmi4kInputBladeCard).Hdmi4kInput; var cecPort = card as ICec; AddHdmiInBladePorts(item.Number, cecPort); } } - else if (type == "dmb4kic") { + else if (type == "dmb4kic") + { var inputBlade = new Dmb4kIC(number, this.Chassis); - foreach (var item in inputBlade.Inputs) { + foreach (var item in inputBlade.Inputs) + { AddDmInBladePorts(item.Number); } } - else if (type == "dmbis") { + else if (type == "dmbis") + { var inputBlade = new DmbIS(number, this.Chassis); - foreach (var item in inputBlade.Inputs) { + foreach (var item in inputBlade.Inputs) + { AddDmInMmFiberPorts(item.Number); } } - else if (type == "dmbis2") { + else if (type == "dmbis2") + { var inputBlade = new DmbIS2(number, this.Chassis); - foreach (var item in inputBlade.Inputs) { + foreach (var item in inputBlade.Inputs) + { AddDmInSmFiberPorts(item.Number); } } @@ -307,19 +346,23 @@ namespace PepperDash.Essentials.DM { } - void AddHdmiInBladePorts(uint number, ICec cecPort) { + void AddHdmiInBladePorts(uint number, ICec cecPort) + { AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, cecPort); } - void AddDmInBladePorts(uint number) { + void AddDmInBladePorts(uint number) + { AddInputPortWithDebug(number, "dmCIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat); } - void AddDmInMmFiberPorts(uint number) { + void AddDmInMmFiberPorts(uint number) + { AddInputPortWithDebug(number, "dmMmIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber); } - void AddDmInSmFiberPorts(uint number) { + void AddDmInSmFiberPorts(uint number) + { AddInputPortWithDebug(number, "dmSmIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber); } @@ -328,64 +371,81 @@ namespace PepperDash.Essentials.DM { /// /// /// - public void AddOutputBlade(string type, uint number) { + public void AddOutputBlade(string type, uint number) + { type = type.ToLower(); Debug.Console(2, this, "Adding output blade '{0}', slot {1}", type, number); - if (type == "dmb4kohd") { + if (type == "dmb4kohd") + { var outputBlade = new Dmb4KOHD(number, Chassis); - foreach (var item in outputBlade.Outputs) { + foreach (var item in outputBlade.Outputs) + { AddHdmiOutBladePorts(item.Number); } } - else if (type == "dmb4kohddnt") { + else if (type == "dmb4kohddnt") + { var outputBlade = new Dmb4KOHD(number, Chassis); - foreach (var item in outputBlade.Outputs) { + foreach (var item in outputBlade.Outputs) + { AddHdmiOutBladePorts(item.Number); } } - else if (type == "dmb4koc") { + else if (type == "dmb4koc") + { var outputBlade = new Dmb4KOC(number, Chassis); - foreach (var item in outputBlade.Outputs) { + foreach (var item in outputBlade.Outputs) + { AddDmOutBladePorts(item.Number); } } - else if (type == "dmb4koc") { + else if (type == "dmb4koc") + { var outputBlade = new Dmb4KOC(number, Chassis); - foreach (var item in outputBlade.Outputs) { + foreach (var item in outputBlade.Outputs) + { AddDmOutBladePorts(item.Number); } } - else if (type == "dmbos") { + else if (type == "dmbos") + { var outputBlade = new DmbOS(number, Chassis); - foreach (var item in outputBlade.Outputs) { + foreach (var item in outputBlade.Outputs) + { AddDmOutMmFiberBladePorts(item.Number); } } - else if (type == "dmbos2") { + else if (type == "dmbos2") + { var outputBlade = new DmbOS2(number, Chassis); - foreach (var item in outputBlade.Outputs) { + foreach (var item in outputBlade.Outputs) + { AddDmOutSmFiberBladePorts(item.Number); } } } - void AddHdmiOutBladePorts(uint number) { - AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("hdmiOut{0}", number) , eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, Chassis.Outputs[number]); + void AddHdmiOutBladePorts(uint number) + { + AddOutputPortWithDebug(number, "hdmiOut", eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, Chassis.Outputs[number]); } - void AddDmOutBladePorts(uint number) { - AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, Chassis.Outputs[number]); + void AddDmOutBladePorts(uint number) + { + AddOutputPortWithDebug(number, "dmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, Chassis.Outputs[number]); } - void AddDmOutMmFiberBladePorts(uint number) { - AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, Chassis.Outputs[number]); + void AddDmOutMmFiberBladePorts(uint number) + { + AddOutputPortWithDebug(number, "dmMmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, Chassis.Outputs[number]); } - void AddDmOutSmFiberBladePorts(uint number) { - AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, Chassis.Outputs[number]); + void AddDmOutSmFiberBladePorts(uint number) + { + AddOutputPortWithDebug(number, "dmSmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, Chassis.Outputs[number]); } @@ -417,23 +477,44 @@ namespace PepperDash.Essentials.DM { } - /// - /// Adds OutputPort - /// - void AddOutputPortWithDebug(string cardName, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) { + + /*void AddOutputPortWithDebug(string cardName, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) { var portKey = string.Format("{0}--{1}", cardName, portName); Debug.Console(2, this, "Adding output port '{0}'", portKey); OutputPorts.Add(new RoutingOutputPort(portKey, sigType, portType, selector, this) { FeedbackMatchObject = selector }); + }*/ + + /// + /// Adds OutputPort + /// + void AddOutputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) + { + try + { + var portKey = string.Format("outputCard{0}--{1}", cardNum, portName); + Debug.Console(2, this, "Adding output port '{0}'", portKey); + var outputPort = new RoutingOutputPort(portKey, sigType, portType, selector, this) + { + FeedbackMatchObject = Chassis.Outputs[cardNum] + }; + OutputPorts.Add(outputPort); + } + catch (Exception ex) + { + Debug.Console(0, this, "Exception : {0}", ex); + } + } /// /// /// - void AddVolumeControl(uint number, Audio.Output audio) { + void AddVolumeControl(uint number, Audio.Output audio) + { VolumeControls.Add(number, new DmCardAudioOutputController(audio)); } @@ -443,35 +524,43 @@ namespace PepperDash.Essentials.DM { //} - void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) + { - switch (args.EventId) { - case DMInputEventIds.EndpointOnlineEventId: { + switch (args.EventId) + { + case DMInputEventIds.EndpointOnlineEventId: + { Debug.Console(2, this, "DM Input EndpointOnlineEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback); InputEndpointOnlineFeedbacks[args.Number].FireUpdate(); break; } - case DMInputEventIds.OnlineFeedbackEventId: { + case DMInputEventIds.OnlineFeedbackEventId: + { Debug.Console(2, this, "DM Input OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback); InputEndpointOnlineFeedbacks[args.Number].FireUpdate(); break; } - case DMInputEventIds.VideoDetectedEventId: { + case DMInputEventIds.VideoDetectedEventId: + { Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number); VideoInputSyncFeedbacks[args.Number].FireUpdate(); break; } - case DMInputEventIds.InputNameEventId: { + case DMInputEventIds.InputNameEventId: + { Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number); InputNameFeedbacks[args.Number].FireUpdate(); break; } - case DMInputEventIds.HdcpCapabilityFeedbackEventId: { + case DMInputEventIds.HdcpCapabilityFeedbackEventId: + { Debug.Console(2, this, "DM Input {0} HdcpCapabilityFeedbackEventId", args.Number); InputCardHdcpCapabilityFeedbacks[args.Number].FireUpdate(); break; } - default: { + default: + { Debug.Console(2, this, "DMInputChange fired for Input {0} with Unhandled EventId: {1}", args.Number, args.EventId); break; } @@ -487,74 +576,74 @@ namespace PepperDash.Essentials.DM { switch (args.EventId) { case DMOutputEventIds.VolumeEventId: - { - if (VolumeControls.ContainsKey(output)) { - VolumeControls[args.Number].VolumeEventFromChassis(); + if (VolumeControls.ContainsKey(output)) + { + VolumeControls[args.Number].VolumeEventFromChassis(); + } + break; } - break; - } case DMOutputEventIds.EndpointOnlineEventId: - { - Debug.Console(2, this, - "Output {0} DMOutputEventIds.EndpointOnlineEventId fired. EndpointOnlineFeedback State: {1}", - args.Number, Chassis.Outputs[output].EndpointOnlineFeedback); - if (Chassis.Outputs[output].Endpoint != null) + { Debug.Console(2, this, - "Output {0} DMOutputEventIds.EndpointOnlineEventId fired. Endpoint.IsOnline State: {1}", - args.Number, Chassis.Outputs[output].Endpoint.IsOnline); + "Output {0} DMOutputEventIds.EndpointOnlineEventId fired. EndpointOnlineFeedback State: {1}", + args.Number, Chassis.Outputs[output].EndpointOnlineFeedback); + if (Chassis.Outputs[output].Endpoint != null) + Debug.Console(2, this, + "Output {0} DMOutputEventIds.EndpointOnlineEventId fired. Endpoint.IsOnline State: {1}", + args.Number, Chassis.Outputs[output].Endpoint.IsOnline); - OutputEndpointOnlineFeedbacks[output].FireUpdate(); - break; - } + OutputEndpointOnlineFeedbacks[output].FireUpdate(); + break; + } case DMOutputEventIds.OnlineFeedbackEventId: - { - Debug.Console(2, this, "Output {0} DMInputEventIds.OnlineFeedbackEventId fired. State: {1}", - args.Number, Chassis.Outputs[output].EndpointOnlineFeedback); - OutputEndpointOnlineFeedbacks[output].FireUpdate(); - break; - } + { + Debug.Console(2, this, "Output {0} DMInputEventIds.OnlineFeedbackEventId fired. State: {1}", + args.Number, Chassis.Outputs[output].EndpointOnlineFeedback); + OutputEndpointOnlineFeedbacks[output].FireUpdate(); + break; + } case DMOutputEventIds.VideoOutEventId: - { - - var inputNumber = Chassis.Outputs[output].VideoOutFeedback == null ? 0 : Chassis.Outputs[output].VideoOutFeedback.Number; - - Debug.Console(2, this, "DMSwitchAudioVideo:{0} Routed Input:{1} Output:{2}'", this.Name, - inputNumber, output); - - if (VideoOutputFeedbacks.ContainsKey(output)) { - var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == Chassis.Outputs[output].VideoOutFeedback); - var localOutputPort = - OutputPorts.FirstOrDefault(p => (DMOutput) p.FeedbackMatchObject == Chassis.Outputs[output]); + + var inputNumber = Chassis.Outputs[output].VideoOutFeedback == null ? 0 : Chassis.Outputs[output].VideoOutFeedback.Number; + + Debug.Console(2, this, "DMSwitchAudioVideo:{0} Routed Input:{1} Output:{2}'", this.Name, + inputNumber, output); + + if (VideoOutputFeedbacks.ContainsKey(output)) + { + var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == Chassis.Outputs[output].VideoOutFeedback); + var localOutputPort = + OutputPorts.FirstOrDefault(p => (DMOutput)p.FeedbackMatchObject == Chassis.Outputs[output]); - VideoOutputFeedbacks[output].FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs(output, - inputNumber, - localOutputPort, - localInputPort, - eRoutingSignalType.AudioVideo)); + VideoOutputFeedbacks[output].FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, + inputNumber, + localOutputPort, + localInputPort, + eRoutingSignalType.AudioVideo)); + } + if (OutputVideoRouteNameFeedbacks.ContainsKey(output)) + { + OutputVideoRouteNameFeedbacks[output].FireUpdate(); + } + break; } - if (OutputVideoRouteNameFeedbacks.ContainsKey(output)) - { - OutputVideoRouteNameFeedbacks[output].FireUpdate(); - } - break; - } case DMOutputEventIds.OutputNameEventId: - { - Debug.Console(2, this, "DM Output {0} NameFeedbackEventId", output); - OutputNameFeedbacks[output].FireUpdate(); - break; - } + { + Debug.Console(2, this, "DM Output {0} NameFeedbackEventId", output); + OutputNameFeedbacks[output].FireUpdate(); + break; + } default: - { - Debug.Console(2, this, "DMOutputChange fired for Output {0} with Unhandled EventId: {1}", - args.Number, args.EventId); - break; - } + { + Debug.Console(2, this, "DMOutputChange fired for Output {0} with Unhandled EventId: {1}", + args.Number, args.EventId); + break; + } } } @@ -564,7 +653,8 @@ namespace PepperDash.Essentials.DM { /// /// /// - void StartOffTimer(PortNumberType pnt) { + void StartOffTimer(PortNumberType pnt) + { if (RouteOffTimers.ContainsKey(pnt)) return; RouteOffTimers[pnt] = new CTimer(o => ExecuteSwitch(null, pnt.Selector, pnt.Type), RouteOffTime); @@ -572,8 +662,10 @@ namespace PepperDash.Essentials.DM { // Send out sigs when coming online - void IsOnline_OutputChange(object sender, EventArgs e) { - if (IsOnline.BoolValue) { + void IsOnline_OutputChange(object sender, EventArgs e) + { + if (IsOnline.BoolValue) + { Chassis.EnableUSBBreakaway.BoolValue = true; if (InputNames != null) @@ -587,12 +679,13 @@ namespace PepperDash.Essentials.DM { #region IRouting Members - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType sigType) { + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType sigType) + { Debug.Console(2, this, "Making an awesome DM route from {0} to {1} {2}", inputSelector, outputSelector, sigType); var input = inputSelector as DMInput; // Cast can sometimes fail var output = outputSelector as DMOutput; - + if (output == null) { @@ -605,11 +698,14 @@ namespace PepperDash.Essentials.DM { // Check to see if there's an off timer waiting on this and if so, cancel var key = new PortNumberType(output, sigType); - if (input == null) { + if (input == null) + { StartOffTimer(key); } - else { - if (RouteOffTimers.ContainsKey(key)) { + else + { + if (RouteOffTimers.ContainsKey(key)) + { Debug.Console(2, this, "{0} cancelling route off due to new source", output); RouteOffTimers[key].Stop(); RouteOffTimers.Remove(key); @@ -671,7 +767,7 @@ namespace PepperDash.Essentials.DM { var ioSlotJoin = ioSlot - 1; // Control - trilist.SetUShortSigAction(joinMap.OutputVideo.JoinNumber + ioSlotJoin, o => ExecuteNumericSwitch(o, (ushort) ioSlot, eRoutingSignalType.Video)); + trilist.SetUShortSigAction(joinMap.OutputVideo.JoinNumber + ioSlotJoin, o => ExecuteNumericSwitch(o, (ushort)ioSlot, eRoutingSignalType.Video)); if (TxDictionary.ContainsKey(ioSlot)) { diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 030536df..ef7fc577 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -1901,6 +1901,8 @@ namespace PepperDash.Essentials.DM EnableAudioBreakawayFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAudioBreakaway.JoinNumber]); EnableUsbBreakawayFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableUsbBreakaway.JoinNumber]); + trilist.SetString(joinMap.NoRouteName.JoinNumber, NoRouteText); + trilist.OnlineStatusChange += (o, a) => { if (!a.DeviceOnLine) @@ -1912,6 +1914,8 @@ namespace PepperDash.Essentials.DM EnableUsbBreakawayFeedback.FireUpdate(); SystemIdBusyFeedback.FireUpdate(); SystemIdFeebdack.FireUpdate(); + + trilist.SetString(joinMap.NoRouteName.JoinNumber, NoRouteText); }; } diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsAudioOutputController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsAudioOutputController.cs index 9f839c89..751c1a5a 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsAudioOutputController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsAudioOutputController.cs @@ -25,10 +25,10 @@ namespace PepperDash.Essentials.DM public DmpsAudioOutput MasterVolumeLevel { get; private set; } public DmpsAudioOutput SourceVolumeLevel { get; private set; } + public DmpsAudioOutput MicsMasterVolumeLevel { get; private set; } public DmpsAudioOutput Codec1VolumeLevel { get; private set; } public DmpsAudioOutput Codec2VolumeLevel { get; private set; } - public DmpsAudioOutputController(string key, string name, Card.Dmps3OutputBase card) : base(key, name) { @@ -36,88 +36,143 @@ namespace PepperDash.Essentials.DM OutputCard.BaseDevice.DMOutputChange += new DMOutputEventHandler(BaseDevice_DMOutputChange); - MasterVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Master); - SourceVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Source); - if (card is Card.Dmps3ProgramOutput) { - //(card as Card.Dmps3ProgramOutput).OutputMixer.MicLevel - //TODO: Hook up mic levels and mutes + MasterVolumeLevel = new DmpsAudioOutputWithMixer(card, eDmpsLevelType.Master, (card as Card.Dmps3ProgramOutput).OutputMixer); + SourceVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Source); + MicsMasterVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.MicsMaster); Codec1VolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Codec1); Codec2VolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Codec2); } else if (card is Card.Dmps3Aux1Output) { + MasterVolumeLevel = new DmpsAudioOutputWithMixer(card, eDmpsLevelType.Master, (card as Card.Dmps3Aux1Output).OutputMixer); + SourceVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Source); + MicsMasterVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.MicsMaster); Codec2VolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Codec2); } else if (card is Card.Dmps3Aux2Output) { + MasterVolumeLevel = new DmpsAudioOutputWithMixer(card, eDmpsLevelType.Master, (card as Card.Dmps3Aux2Output).OutputMixer); + SourceVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Source); + MicsMasterVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.MicsMaster); Codec1VolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Codec1); } + else //Digital Outputs + { + MasterVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Master); + SourceVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.Source); + MicsMasterVolumeLevel = new DmpsAudioOutput(card, eDmpsLevelType.MicsMaster); + } } void BaseDevice_DMOutputChange(Switch device, DMOutputEventArgs args) { + Debug.Console(2, this, "Dmps Audio Controller Event Output: {0} EventId: {1}", args.Number, args.EventId.ToString()); switch (args.EventId) { + case DMOutputEventIds.OutputVuFeedBackEventId: + { + //Frequently called event that isn't needed + return; + } case DMOutputEventIds.MasterVolumeFeedBackEventId: - { - MasterVolumeLevel.VolumeLevelFeedback.FireUpdate(); - break; - } + { + MasterVolumeLevel.VolumeLevelFeedback.FireUpdate(); + MasterVolumeLevel.VolumeLevelScaledFeedback.FireUpdate(); + break; + } case DMOutputEventIds.MasterMuteOnFeedBackEventId: - { - MasterVolumeLevel.MuteFeedback.FireUpdate(); - break; - } + { + MasterVolumeLevel.MuteFeedback.FireUpdate(); + break; + } case DMOutputEventIds.SourceLevelFeedBackEventId: - { - SourceVolumeLevel.VolumeLevelFeedback.FireUpdate(); - break; - } + { + SourceVolumeLevel.VolumeLevelFeedback.FireUpdate(); + SourceVolumeLevel.VolumeLevelScaledFeedback.FireUpdate(); + break; + } + case DMOutputEventIds.SourceMuteOnFeedBackEventId: + { + SourceVolumeLevel.MuteFeedback.FireUpdate(); + break; + } + case DMOutputEventIds.MicMasterLevelFeedBackEventId: + { + MicsMasterVolumeLevel.VolumeLevelFeedback.FireUpdate(); + MicsMasterVolumeLevel.VolumeLevelScaledFeedback.FireUpdate(); + break; + } + case DMOutputEventIds.MicMasterMuteOnFeedBackEventId: + { + MicsMasterVolumeLevel.MuteFeedback.FireUpdate(); + break; + } case DMOutputEventIds.Codec1LevelFeedBackEventId: - { - if(Codec1VolumeLevel != null) - Codec1VolumeLevel.VolumeLevelFeedback.FireUpdate(); - break; - } + { + if (Codec1VolumeLevel != null) + { + Codec1VolumeLevel.VolumeLevelFeedback.FireUpdate(); + Codec1VolumeLevel.VolumeLevelScaledFeedback.FireUpdate(); + } + break; + } case DMOutputEventIds.Codec1MuteOnFeedBackEventId: - { - if (Codec1VolumeLevel != null) - Codec1VolumeLevel.MuteFeedback.FireUpdate(); - break; - } + { + if (Codec1VolumeLevel != null) + Codec1VolumeLevel.MuteFeedback.FireUpdate(); + break; + } case DMOutputEventIds.Codec2LevelFeedBackEventId: - { - if (Codec2VolumeLevel != null) - Codec2VolumeLevel.VolumeLevelFeedback.FireUpdate(); - break; - } + { + if (Codec2VolumeLevel != null) + { + Codec2VolumeLevel.VolumeLevelFeedback.FireUpdate(); + Codec2VolumeLevel.VolumeLevelScaledFeedback.FireUpdate(); + } + break; + } case DMOutputEventIds.Codec2MuteOnFeedBackEventId: - { - if (Codec2VolumeLevel != null) - Codec2VolumeLevel.MuteFeedback.FireUpdate(); - break; - } + { + if (Codec2VolumeLevel != null) + Codec2VolumeLevel.MuteFeedback.FireUpdate(); + break; + } + case DMOutputEventIds.MinVolumeFeedBackEventId: + { + Debug.Console(2, this, "MinVolumeFeedBackEventId: {0}", args.Index); + var level = MasterVolumeLevel as DmpsAudioOutputWithMixer; + if (level != null) + { + level.GetVolumeMin(); + } + break; + } + case DMOutputEventIds.MaxVolumeFeedBackEventId: + { + Debug.Console(2, this, "MaxVolumeFeedBackEventId: {0}", args.Index); + var level = MasterVolumeLevel as DmpsAudioOutputWithMixer; + if (level != null) + { + level.GetVolumeMax(); + } + break; + } } } public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { + { var joinMap = new DmpsAudioOutputControllerJoinMap(joinStart); - var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); - - if (!string.IsNullOrEmpty(joinMapSerialized)) - joinMap = JsonConvert.DeserializeObject(joinMapSerialized); - - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - else - { - Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); } Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); @@ -125,6 +180,11 @@ namespace PepperDash.Essentials.DM if (MasterVolumeLevel != null) { SetUpDmpsAudioOutputJoins(trilist, MasterVolumeLevel, joinMap.MasterVolumeLevel.JoinNumber); + var mixer = MasterVolumeLevel as DmpsAudioOutputWithMixer; + if (mixer != null) + { + trilist.SetUShortSigAction(joinMap.MixerPresetRecall.JoinNumber, mixer.RecallPreset); + } } if (SourceVolumeLevel != null) @@ -132,6 +192,11 @@ namespace PepperDash.Essentials.DM SetUpDmpsAudioOutputJoins(trilist, SourceVolumeLevel, joinMap.SourceVolumeLevel.JoinNumber); } + if (MicsMasterVolumeLevel != null) + { + SetUpDmpsAudioOutputJoins(trilist, MicsMasterVolumeLevel, joinMap.MicsMasterVolumeLevel.JoinNumber); + } + if (Codec1VolumeLevel != null) { SetUpDmpsAudioOutputJoins(trilist, Codec1VolumeLevel, joinMap.Codec1VolumeLevel.JoinNumber); @@ -141,20 +206,20 @@ namespace PepperDash.Essentials.DM { SetUpDmpsAudioOutputJoins(trilist, Codec2VolumeLevel, joinMap.Codec2VolumeLevel.JoinNumber); } - } static void SetUpDmpsAudioOutputJoins(BasicTriList trilist, DmpsAudioOutput output, uint joinStart) { var volumeLevelJoin = joinStart; + var volumeLevelScaledJoin = joinStart + 1; var muteOnJoin = joinStart; var muteOffJoin = joinStart + 1; var volumeUpJoin = joinStart + 2; var volumeDownJoin = joinStart + 3; + var sendScaledVolumeJoin = joinStart + 4; - - trilist.SetUShortSigAction(volumeLevelJoin, output.SetVolume); output.VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[volumeLevelJoin]); + output.VolumeLevelScaledFeedback.LinkInputSig(trilist.UShortInput[volumeLevelScaledJoin]); trilist.SetSigTrueAction(muteOnJoin, output.MuteOn); output.MuteFeedback.LinkInputSig(trilist.BooleanInput[muteOnJoin]); @@ -163,30 +228,78 @@ namespace PepperDash.Essentials.DM trilist.SetBoolSigAction(volumeUpJoin, output.VolumeUp); trilist.SetBoolSigAction(volumeDownJoin, output.VolumeDown); + trilist.SetBoolSigAction(sendScaledVolumeJoin, output.SendScaledVolume); + trilist.SetUShortSigAction(volumeLevelJoin, output.SetVolume); + trilist.SetUShortSigAction(volumeLevelScaledJoin, output.SetVolumeScaled); + } + } + + public class DmpsAudioOutputWithMixer : DmpsAudioOutput + { + CrestronControlSystem.Dmps3OutputMixerWithMonoAndStereo Mixer; + + public DmpsAudioOutputWithMixer(Card.Dmps3OutputBase output, eDmpsLevelType type, CrestronControlSystem.Dmps3OutputMixerWithMonoAndStereo mixer) + : base(output, type) + { + Mixer = mixer; + GetVolumeMax(); + GetVolumeMin(); + } + + public void GetVolumeMin() + { + MinLevel = (short)Mixer.MinVolumeFeedback.UShortValue; + if (VolumeLevelScaledFeedback != null) + { + VolumeLevelScaledFeedback.FireUpdate(); + } + } + + public void GetVolumeMax() + { + MaxLevel = (short)Mixer.MaxVolumeFeedback.UShortValue; + if (VolumeLevelScaledFeedback != null) + { + VolumeLevelScaledFeedback.FireUpdate(); + } + } + + public void RecallPreset(ushort preset) + { + Debug.Console(1, "DMPS Recalling Preset {0}", preset); + Mixer.PresetNumber.UShortValue = preset; + Mixer.RecallPreset(); } } public class DmpsAudioOutput : IBasicVolumeWithFeedback { Card.Dmps3OutputBase Output; - + eDmpsLevelType Type; UShortInputSig Level; - eDmpsLevelType Type; + private bool EnableVolumeSend; + private ushort VolumeLevelInput; + protected short MinLevel { get; set; } + protected short MaxLevel { get; set; } public BoolFeedback MuteFeedback { get; private set; } public IntFeedback VolumeLevelFeedback { get; private set; } + public IntFeedback VolumeLevelScaledFeedback { get; private set; } Action MuteOnAction; Action MuteOffAction; Action VolumeUpAction; Action VolumeDownAction; - + public DmpsAudioOutput(Card.Dmps3OutputBase output, eDmpsLevelType type) { Output = output; - + VolumeLevelInput = 0; + EnableVolumeSend = false; Type = type; + MinLevel = -800; + MaxLevel = 100; switch (type) { @@ -194,14 +307,12 @@ namespace PepperDash.Essentials.DM { Level = output.MasterVolume; - MuteFeedback = new BoolFeedback( new Func (() => Output.MasterMuteOnFeedBack.BoolValue)); + MuteFeedback = new BoolFeedback(new Func(() => Output.MasterMuteOnFeedBack.BoolValue)); VolumeLevelFeedback = new IntFeedback(new Func(() => Output.MasterVolumeFeedBack.UShortValue)); MuteOnAction = new Action(Output.MasterMuteOn); MuteOffAction = new Action(Output.MasterMuteOff); VolumeUpAction = new Action((b) => Output.MasterVolumeUp.BoolValue = b); VolumeDownAction = new Action((b) => Output.MasterVolumeDown.BoolValue = b); - - break; } case eDmpsLevelType.MicsMaster: @@ -214,7 +325,6 @@ namespace PepperDash.Essentials.DM MuteOffAction = new Action(Output.MicMasterMuteOff); VolumeUpAction = new Action((b) => Output.MicMasterLevelUp.BoolValue = b); VolumeDownAction = new Action((b) => Output.MicMasterLevelDown.BoolValue = b); - break; } case eDmpsLevelType.Source: @@ -243,7 +353,6 @@ namespace PepperDash.Essentials.DM MuteOffAction = new Action(programOutput.Codec1MuteOff); VolumeUpAction = new Action((b) => programOutput.Codec1LevelUp.BoolValue = b); VolumeDownAction = new Action((b) => programOutput.Codec1LevelDown.BoolValue = b); - } else { @@ -274,7 +383,6 @@ namespace PepperDash.Essentials.DM MuteOffAction = new Action(programOutput.Codec2MuteOff); VolumeUpAction = new Action((b) => programOutput.Codec2LevelUp.BoolValue = b); VolumeDownAction = new Action((b) => programOutput.Codec2LevelDown.BoolValue = b); - } else { @@ -292,7 +400,38 @@ namespace PepperDash.Essentials.DM break; } } + if (VolumeLevelFeedback != null) + { + VolumeLevelScaledFeedback = new IntFeedback(new Func(() => ScaleVolumeFeedback(VolumeLevelFeedback.UShortValue))); + VolumeLevelFeedback.FireUpdate(); + VolumeLevelScaledFeedback.FireUpdate(); + } + } + public void SetVolumeScaled(ushort level) + { + Debug.Console(2, Debug.ErrorLogLevel.None, "Scaling DMPS volume:{0} level:{1} min:{2} max:{3}", Output.Name, level.ToString(), MinLevel.ToString(), MaxLevel.ToString()); + VolumeLevelInput = (ushort)(level * (MaxLevel - MinLevel) / ushort.MaxValue + MinLevel); + if (EnableVolumeSend == true) + { + Level.UShortValue = VolumeLevelInput; + } + } + + public ushort ScaleVolumeFeedback(ushort level) + { + short signedLevel = (short)level; + Debug.Console(2, Debug.ErrorLogLevel.None, "Scaling DMPS volume:{0} feedback:{1} min:{2} max:{3}", Output.Name, signedLevel.ToString(), MinLevel.ToString(), MaxLevel.ToString()); + return (ushort)((signedLevel - MinLevel) * ushort.MaxValue / (MaxLevel - MinLevel)); + } + + public void SendScaledVolume(bool pressRelease) + { + EnableVolumeSend = pressRelease; + if (pressRelease == false) + { + SetVolumeScaled(VolumeLevelInput); + } } #region IBasicVolumeWithFeedback Members diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsMicrophoneController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsMicrophoneController.cs new file mode 100644 index 00000000..30b2cfab --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsMicrophoneController.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; + + +namespace PepperDash.Essentials.DM +{ + /// + /// Exposes the volume levels for microphones DMPS3 chassis + /// + public class DmpsMicrophoneController + { + private Dictionary Mics; + + public DmpsMicrophoneController(CrestronControlSystem dmps) + { + Debug.Console(2, "Creating Dmps Microphone Controller"); + Mics = new Dictionary(); + + foreach (var mic in dmps.Microphones) + { + Debug.Console(0, "Dmps Microphone Controller Adding Mic: {0} Name: {1}", mic.ID, mic.Name); + var dmpsMic = new DmpsMicrophone("processor-microphone" + mic.ID, mic.Name, mic); + + DeviceManager.AddDevice(dmpsMic); + Mics.Add(mic.ID, dmpsMic); + } + + dmps.MicrophoneChange += new MicrophoneChangeEventHandler(Dmps_MicrophoneChange); + } + + void Dmps_MicrophoneChange(MicrophoneBase mic, GenericEventArgs args) + { + Debug.Console(2, "Dmps Microphone Controller Index: {0} EventId: {1}", mic.ID, args.EventId.ToString()); + + if(Mics.ContainsKey(mic.ID)) + { + Mics[mic.ID].Event(args.EventId); + } + } + } + + public class DmpsMicrophone : EssentialsBridgeableDevice, IBasicVolumeWithFeedback + { + MicrophoneBase Mic; + + private bool EnableVolumeSend; + private ushort VolumeLevelInput; + protected short MinLevel { get; set; } + protected short MaxLevel { get; set; } + + public BoolFeedback MuteFeedback { get; private set; } + public IntFeedback VolumeLevelFeedback { get; private set; } + public IntFeedback VolumeLevelScaledFeedback { get; private set; } + public StringFeedback NameFeedback { get; private set; } + + Action MuteOnAction; + Action MuteOffAction; + + public DmpsMicrophone(string key, string name, MicrophoneBase mic) : base(key, name) + { + Mic = mic; + VolumeLevelInput = 0; + EnableVolumeSend = false; + MinLevel = 0; + MaxLevel = 600; + + MuteFeedback = new BoolFeedback(new Func(() => Mic.MuteOnFeedBack.BoolValue)); + VolumeLevelFeedback = new IntFeedback(new Func(() => Mic.GainFeedBack.UShortValue)); + VolumeLevelScaledFeedback = new IntFeedback(new Func(() => ScaleVolumeFeedback(VolumeLevelFeedback.UShortValue))); + NameFeedback = new StringFeedback(new Func(() => "Microphone " + Mic.ID)); + MuteOnAction = new Action(Mic.MuteOn); + MuteOffAction = new Action(Mic.MuteOff); + + VolumeLevelFeedback.FireUpdate(); + VolumeLevelScaledFeedback.FireUpdate(); + NameFeedback.FireUpdate(); + MuteFeedback.FireUpdate(); + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new DmpsMicrophoneControllerJoinMap(joinStart); + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + } + + Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + + VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.MicGain.JoinNumber]); + VolumeLevelScaledFeedback.LinkInputSig(trilist.UShortInput[joinMap.MicGainScaled.JoinNumber ]); + MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]); + MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]); + NameFeedback.LinkInputSig(trilist.StringInput[joinMap.MicName.JoinNumber]); + + trilist.SetUShortSigAction(joinMap.MicGain.JoinNumber, SetVolume); + trilist.SetUShortSigAction(joinMap.MicGainScaled.JoinNumber, SetVolumeScaled); + trilist.SetBoolSigAction(joinMap.MicGainScaledSend.JoinNumber, SendScaledVolume); + trilist.SetSigTrueAction(joinMap.MicMuteOn.JoinNumber, MuteOnAction); + trilist.SetSigTrueAction(joinMap.MicMuteOff.JoinNumber, MuteOffAction); + } + + public void Event(int id) + { + if (id == MicrophoneEventIds.MuteOnFeedBackEventId) + { + MuteFeedback.FireUpdate(); + } + else if (id == MicrophoneEventIds.GainFeedBackEventId) + { + VolumeLevelFeedback.FireUpdate(); + VolumeLevelScaledFeedback.FireUpdate(); + } + } + + public void SetVolumeScaled(ushort level) + { + VolumeLevelInput = (ushort)(level * (MaxLevel - MinLevel) / ushort.MaxValue + MinLevel); + if (EnableVolumeSend == true) + { + Mic.Gain.UShortValue = VolumeLevelInput; + } + } + + public ushort ScaleVolumeFeedback(ushort level) + { + short signedLevel = (short)level; + return (ushort)((signedLevel - MinLevel) * ushort.MaxValue / (MaxLevel - MinLevel)); + } + + public void SendScaledVolume(bool pressRelease) + { + EnableVolumeSend = pressRelease; + if (pressRelease == false) + { + SetVolumeScaled(VolumeLevelInput); + } + } + + #region IBasicVolumeWithFeedback Members + + public void SetVolume(ushort level) + { + Mic.Gain.UShortValue = level; + } + + public void MuteOn() + { + MuteOnAction(); + } + + public void MuteOff() + { + MuteOffAction(); + } + + #endregion + + #region IBasicVolumeControls Members + + public void VolumeUp(bool pressRelease) + { + } + + public void VolumeDown(bool pressRelease) + { + } + + public void MuteToggle() + { + if (MuteFeedback.BoolValue) + MuteOff(); + else + MuteOn(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs index 484293eb..f6592699 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs @@ -25,9 +25,19 @@ namespace PepperDash.Essentials.DM public CrestronControlSystem Dmps { get; set; } public ISystemControl SystemControl { get; private set; } + public bool? EnableRouting { get; private set; } + + //Check if DMPS is a DMPS3-4K type for endpoint creation + public bool Dmps4kType { get; private set; } //IroutingNumericEvent public event EventHandler NumericSwitchChange; + + //Feedback for DMPS System Control + public BoolFeedback SystemPowerOnFeedback { get; private set; } + public BoolFeedback SystemPowerOffFeedback { get; private set; } + public BoolFeedback FrontPanelLockOnFeedback { get; private set; } + public BoolFeedback FrontPanelLockOffFeedback { get; private set; } // Feedbacks for EssentialDM public Dictionary VideoOutputFeedbacks { get; private set; } @@ -52,6 +62,7 @@ namespace PepperDash.Essentials.DM public Dictionary InputNames { get; set; } public Dictionary OutputNames { get; set; } public Dictionary VolumeControls { get; private set; } + public DmpsMicrophoneController Microphones { get; private set; } public const int RouteOffTime = 500; Dictionary RouteOffTimers = new Dictionary(); @@ -112,10 +123,40 @@ namespace PepperDash.Essentials.DM /// public DmpsRoutingController(string key, string name, ISystemControl systemControl) : base(key, name) - { - + { Dmps = Global.ControlSystem; - SystemControl = systemControl; + + switch (systemControl.SystemControlType) + { + case eSystemControlType.Dmps34K150CSystemControl: + SystemControl = systemControl as Dmps34K150CSystemControl; + Dmps4kType = true; + SystemPowerOnFeedback = new BoolFeedback(() => { return true; }); + SystemPowerOffFeedback = new BoolFeedback(() => { return false; }); + break; + case eSystemControlType.Dmps34K200CSystemControl: + case eSystemControlType.Dmps34K250CSystemControl: + case eSystemControlType.Dmps34K300CSystemControl: + case eSystemControlType.Dmps34K350CSystemControl: + SystemControl = systemControl as Dmps34K300CSystemControl; + Dmps4kType = true; + SystemPowerOnFeedback = new BoolFeedback(() => { return true; }); + SystemPowerOffFeedback = new BoolFeedback(() => { return false; }); + break; + default: + SystemControl = systemControl as Dmps3SystemControl; + Dmps4kType = false; + SystemPowerOnFeedback = new BoolFeedback(() => + { + return ((Dmps3SystemControl)SystemControl).SystemPowerOnFeedBack.BoolValue; + }); + SystemPowerOffFeedback = new BoolFeedback(() => + { + return ((Dmps3SystemControl)SystemControl).SystemPowerOffFeedBack.BoolValue; + }); + break; + } + Debug.Console(1, this, "DMPS Type = {0}, 4K Type = {1}", systemControl.SystemControlType, Dmps4kType); InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); @@ -123,6 +164,15 @@ namespace PepperDash.Essentials.DM TxDictionary = new Dictionary(); RxDictionary = new Dictionary(); + FrontPanelLockOnFeedback = new BoolFeedback(() => + { + return SystemControl.FrontPanelLockOnFeedback.BoolValue; + }); + FrontPanelLockOffFeedback = new BoolFeedback(() => + { + return SystemControl.FrontPanelLockOffFeedback.BoolValue; + }); + VideoOutputFeedbacks = new Dictionary(); AudioOutputFeedbacks = new Dictionary(); VideoInputSyncFeedbacks = new Dictionary(); @@ -142,6 +192,8 @@ namespace PepperDash.Essentials.DM SetupOutputCards(); SetupInputCards(); + + Microphones = new DmpsMicrophoneController(Dmps); } public override bool CustomActivate() @@ -154,6 +206,42 @@ namespace PepperDash.Essentials.DM // Subscribe to events Dmps.DMInputChange += Dmps_DMInputChange; Dmps.DMOutputChange += Dmps_DMOutputChange; + Dmps.DMSystemChange += Dmps_DMSystemChange; + + foreach (var x in VideoOutputFeedbacks) + { + x.Value.FireUpdate(); + } + foreach (var x in AudioOutputFeedbacks) + { + x.Value.FireUpdate(); + } + foreach (var x in VideoInputSyncFeedbacks) + { + x.Value.FireUpdate(); + } + foreach (var x in InputEndpointOnlineFeedbacks) + { + x.Value.FireUpdate(); + } + foreach (var x in InputNameFeedbacks) + { + x.Value.FireUpdate(); + } + foreach (var x in OutputNameFeedbacks) + { + x.Value.FireUpdate(); + } + foreach (var x in OutputEndpointOnlineFeedbacks) + { + x.Value.FireUpdate(); + } + + SystemPowerOnFeedback.FireUpdate(); + SystemPowerOffFeedback.FireUpdate(); + + FrontPanelLockOnFeedback.FireUpdate(); + FrontPanelLockOffFeedback.FireUpdate(); return base.CustomActivate(); } @@ -191,6 +279,12 @@ namespace PepperDash.Essentials.DM } } + public void SetRoutingEnable(bool enable) + { + CrestronEnvironment.Sleep(1000); + EnableRouting = enable; + } + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) { var joinMap = new DmpsRoutingControllerJoinMap(joinStart); @@ -211,9 +305,26 @@ namespace PepperDash.Essentials.DM Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + //Link up system power only for non-4k DMPS3 + if (SystemControl is Dmps3SystemControl) + { + trilist.SetBoolSigAction(joinMap.SystemPowerOn.JoinNumber, a => { if (a) { ((Dmps3SystemControl)SystemControl).SystemPowerOn(); } }); + trilist.SetBoolSigAction(joinMap.SystemPowerOff.JoinNumber, a => { if (a) { ((Dmps3SystemControl)SystemControl).SystemPowerOff(); } }); + } + + SystemPowerOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SystemPowerOn.JoinNumber]); + SystemPowerOffFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SystemPowerOff.JoinNumber]); + + trilist.SetBoolSigAction(joinMap.FrontPanelLockOn.JoinNumber, a => { if (a) {SystemControl.FrontPanelLockOn();}}); + trilist.SetBoolSigAction(joinMap.FrontPanelLockOff.JoinNumber, a => { if (a) {SystemControl.FrontPanelLockOff();}}); + + FrontPanelLockOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.FrontPanelLockOn.JoinNumber]); + FrontPanelLockOffFeedback.LinkInputSig(trilist.BooleanInput[joinMap.FrontPanelLockOff.JoinNumber]); + + trilist.SetBoolSigAction(joinMap.EnableRouting.JoinNumber, SetRoutingEnable); + // Link up outputs LinkInputsToApi(trilist, joinMap); - LinkOutputsToApi(trilist, joinMap); } @@ -272,6 +383,8 @@ namespace PepperDash.Essentials.DM if (OutputNameFeedbacks[ioSlot] != null) { OutputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.OutputNames.JoinNumber + ioSlotJoin]); + OutputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.OutputVideoNames.JoinNumber + ioSlotJoin]); + OutputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.OutputAudioNames.JoinNumber + ioSlotJoin]); } if (OutputVideoRouteNameFeedbacks[ioSlot] != null) { @@ -293,6 +406,14 @@ namespace PepperDash.Essentials.DM private void LinkInputsToApi(BasicTriList trilist, DmpsRoutingControllerJoinMap joinMap) { + if (Dmps4kType) + { + //DMPS-4K audio inputs 1-5 are aux inputs + for (uint i = 1; i <= 5; i++) + { + trilist.StringInput[joinMap.InputAudioNames.JoinNumber + i - 1].StringValue = String.Format("Aux Input {0}", i); + } + } for (uint i = 1; i <= Dmps.SwitcherInputs.Count; i++) { Debug.Console(2, this, "Linking Input Card {0}", i); @@ -300,15 +421,26 @@ namespace PepperDash.Essentials.DM var ioSlot = i; var ioSlotJoin = ioSlot - 1; - if (VideoInputSyncFeedbacks[ioSlot] != null) + if (VideoInputSyncFeedbacks.ContainsKey(ioSlot) && VideoInputSyncFeedbacks[ioSlot] != null) { VideoInputSyncFeedbacks[ioSlot].LinkInputSig( trilist.BooleanInput[joinMap.VideoSyncStatus.JoinNumber + ioSlotJoin]); } - if (InputNameFeedbacks[ioSlot] != null) + if (InputNameFeedbacks.ContainsKey(ioSlot) && InputNameFeedbacks[ioSlot] != null) { InputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.InputNames.JoinNumber + ioSlotJoin]); + InputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.InputVideoNames.JoinNumber + ioSlotJoin]); + + if (Dmps4kType) + { + //DMPS-4K Audio Inputs are offset by 5 + InputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.InputAudioNames.JoinNumber + ioSlotJoin + 5]); + } + else + { + InputNameFeedbacks[ioSlot].LinkInputSig(trilist.StringInput[joinMap.InputAudioNames.JoinNumber + ioSlotJoin]); + } } trilist.SetStringSigAction(joinMap.InputNames.JoinNumber + ioSlotJoin, s => @@ -333,7 +465,7 @@ namespace PepperDash.Essentials.DM }); - if (InputEndpointOnlineFeedbacks[ioSlot] != null) + if (InputEndpointOnlineFeedbacks.ContainsKey(ioSlot) && InputEndpointOnlineFeedbacks[ioSlot] != null) { InputEndpointOnlineFeedbacks[ioSlot].LinkInputSig( trilist.BooleanInput[joinMap.InputEndpointOnline.JoinNumber + ioSlotJoin]); @@ -384,7 +516,7 @@ namespace PepperDash.Essentials.DM OutputNameFeedbacks[outputCard.Number] = new StringFeedback(() => { - if (outputCard.NameFeedback != null && !string.IsNullOrEmpty(outputCard.NameFeedback.StringValue)) + if (outputCard.NameFeedback != null && outputCard.NameFeedback != CrestronControlSystem.NullStringOutputSig && !string.IsNullOrEmpty(outputCard.NameFeedback.StringValue)) { Debug.Console(2, this, "Output Card {0} Name: {1}", outputCard.Number, outputCard.NameFeedback.StringValue); return outputCard.NameFeedback.StringValue; @@ -430,14 +562,14 @@ namespace PepperDash.Essentials.DM InputEndpointOnlineFeedbacks[inputCard.Number] = new BoolFeedback(() => inputCard.EndpointOnlineFeedback); - if (inputCard.VideoDetectedFeedback != null) + if (inputCard.VideoDetectedFeedback != null && inputCard.VideoDetectedFeedback.Supported) { VideoInputSyncFeedbacks[inputCard.Number] = new BoolFeedback(() => inputCard.VideoDetectedFeedback.BoolValue); } InputNameFeedbacks[inputCard.Number] = new StringFeedback(() => { - if (inputCard.NameFeedback != null && !string.IsNullOrEmpty(inputCard.NameFeedback.StringValue)) + if (inputCard.NameFeedback != null && inputCard.NameFeedback != CrestronControlSystem.NullStringOutputSig && !string.IsNullOrEmpty(inputCard.NameFeedback.StringValue)) { Debug.Console(2, this, "Input Card {0} Name: {1}", inputCard.Number, inputCard.NameFeedback.StringValue); return inputCard.NameFeedback.StringValue; @@ -586,7 +718,7 @@ namespace PepperDash.Essentials.DM { AddAudioOnlyOutputPort(number, "Program"); - var programOutput = new DmpsAudioOutputController(string.Format("processor-programAudioOutput"), "Program Audio Output", outputCard as Card.Dmps3OutputBase); + var programOutput = new DmpsAudioOutputController(string.Format("processor-programAudioOutput"), "Program Audio Output", outputCard as Card.Dmps3ProgramOutput); DeviceManager.AddDevice(programOutput); } @@ -598,7 +730,7 @@ namespace PepperDash.Essentials.DM { AddAudioOnlyOutputPort(number, "Aux1"); - var aux1Output = new DmpsAudioOutputController(string.Format("processor-aux1AudioOutput"), "Program Audio Output", outputCard as Card.Dmps3OutputBase); + var aux1Output = new DmpsAudioOutputController(string.Format("processor-aux1AudioOutput"), "Program Audio Output", outputCard as Card.Dmps3Aux1Output); DeviceManager.AddDevice(aux1Output); } @@ -607,7 +739,7 @@ namespace PepperDash.Essentials.DM { AddAudioOnlyOutputPort(number, "Aux2"); - var aux2Output = new DmpsAudioOutputController(string.Format("processor-aux2AudioOutput"), "Program Audio Output", outputCard as Card.Dmps3OutputBase); + var aux2Output = new DmpsAudioOutputController(string.Format("processor-aux2AudioOutput"), "Program Audio Output", outputCard as Card.Dmps3Aux2Output); DeviceManager.AddDevice(aux2Output); } @@ -719,40 +851,53 @@ namespace PepperDash.Essentials.DM void Dmps_DMInputChange(Switch device, DMInputEventArgs args) { - //Debug.Console(2, this, "DMSwitch:{0} Input:{1} Event:{2}'", this.Name, args.Number, args.EventId.ToString()); - - switch (args.EventId) + try { - case (DMInputEventIds.OnlineFeedbackEventId): - { - Debug.Console(2, this, "DM Input OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback); - InputEndpointOnlineFeedbacks[args.Number].FireUpdate(); - break; - } - case (DMInputEventIds.VideoDetectedEventId): - { - Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number); - VideoInputSyncFeedbacks[args.Number].FireUpdate(); - break; - } - case (DMInputEventIds.InputNameEventId): - { - Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number); - InputNameFeedbacks[args.Number].FireUpdate(); - break; - } + switch (args.EventId) + { + case (DMInputEventIds.OnlineFeedbackEventId): + { + Debug.Console(2, this, "DM Input OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback); + InputEndpointOnlineFeedbacks[args.Number].FireUpdate(); + break; + } + case (DMInputEventIds.VideoDetectedEventId): + { + Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number); + VideoInputSyncFeedbacks[args.Number].FireUpdate(); + break; + } + case (DMInputEventIds.InputNameEventId): + { + Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number); + if(InputNameFeedbacks.ContainsKey(args.Number)) + { + InputNameFeedbacks[args.Number].FireUpdate(); + } + break; + } + } + } + catch (Exception e) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "DMSwitch Input Change:{0} Input:{1} Event:{2}\rException: {3}", this.Name, args.Number, args.EventId.ToString(), e.ToString()); } } void Dmps_DMOutputChange(Switch device, DMOutputEventArgs args) { Debug.Console(2, this, "DMOutputChange Output: {0} EventId: {1}", args.Number, args.EventId.ToString()); + if (args.EventId == DMOutputEventIds.OutputVuFeedBackEventId) + { + //Frequently called event that isn't needed + return; + } + var output = args.Number; DMOutput outputCard = Dmps.SwitcherOutputs[output] as DMOutput; - if (args.EventId == DMOutputEventIds.VolumeEventId && - VolumeControls.ContainsKey(output)) + if (args.EventId == DMOutputEventIds.VolumeEventId && VolumeControls.ContainsKey(output)) { VolumeControls[args.Number].VolumeEventFromChassis(); } @@ -812,6 +957,27 @@ namespace PepperDash.Essentials.DM } + void Dmps_DMSystemChange(Switch device, DMSystemEventArgs args) + { + switch (args.EventId) + { + case DMSystemEventIds.SystemPowerOnEventId: + case DMSystemEventIds.SystemPowerOffEventId: + { + SystemPowerOnFeedback.FireUpdate(); + SystemPowerOffFeedback.FireUpdate(); + break; + } + case DMSystemEventIds.FrontPanelLockOnEventId: + case DMSystemEventIds.FrontPanelLockOffEventId: + { + FrontPanelLockOnFeedback.FireUpdate(); + FrontPanelLockOffFeedback.FireUpdate(); + break; + } + } + } + /// /// /// @@ -829,6 +995,10 @@ namespace PepperDash.Essentials.DM { try { + if (EnableRouting == false) + { + return; + } Debug.Console(2, this, "Attempting a DM route from input {0} to output {1} {2}", inputSelector, outputSelector, sigType); @@ -879,7 +1049,6 @@ namespace PepperDash.Essentials.DM // NOTE THAT BITWISE COMPARISONS - TO CATCH ALL ROUTING TYPES if ((sigType & eRoutingSignalType.Video) == eRoutingSignalType.Video) { - output.VideoOut = input; } @@ -903,7 +1072,6 @@ namespace PepperDash.Essentials.DM if ((sigType & eRoutingSignalType.UsbOutput) == eRoutingSignalType.UsbOutput) { - output.USBRoutedTo = input; } diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs index 83c386bc..a9282693 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs @@ -61,6 +61,7 @@ namespace PepperDash.Essentials.DM Debug.Console(1, rmc, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); rmc.IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + trilist.StringInput[joinMap.Name.JoinNumber].StringValue = rmc.Name; if (rmc.VideoOutputResolutionFeedback != null) rmc.VideoOutputResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentOutputResolution.JoinNumber]); if (rmc.EdidManufacturerFeedback != null) @@ -329,7 +330,14 @@ namespace PepperDash.Essentials.DM var parentDev = DeviceManager.GetDeviceForKey(pKey); if (parentDev is DmpsRoutingController) { - return GetDmRmcControllerForDmps(key, name, typeName, parentDev as DmpsRoutingController, props.ParentOutputNumber); + if ((parentDev as DmpsRoutingController).Dmps4kType) + { + return GetDmRmcControllerForDmps4k(key, name, typeName, parentDev as DmpsRoutingController, props.ParentOutputNumber); + } + else + { + return GetDmRmcControllerForDmps(key, name, typeName, ipid, parentDev as DmpsRoutingController, props.ParentOutputNumber); + } } if (!(parentDev is IDmSwitch)) { @@ -395,25 +403,47 @@ namespace PepperDash.Essentials.DM return null; } - private static CrestronGenericBaseDevice GetDmRmcControllerForDmps(string key, string name, string typeName, + private static CrestronGenericBaseDevice GetDmRmcControllerForDmps(string key, string name, string typeName, + uint ipid, DmpsRoutingController controller, uint num) + { + Func dmpsHandler; + if (ChassisDict.TryGetValue(typeName.ToLower(), out dmpsHandler)) + { + var output = controller.Dmps.SwitcherOutputs[num] as DMOutput; + + if (output != null) + { + return dmpsHandler(key, name, ipid, output); + } + Debug.Console(0, Debug.ErrorLogLevel.Error, + "Cannot attach DM-RMC of type '{0}' to output {1} on DMPS chassis. Output is not a DM Output.", + typeName, num); + return null; + } + + Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot create DM-RMC of type '{0}' to output {1} on DMPS chassis", typeName, num); + return null; + } + + private static CrestronGenericBaseDevice GetDmRmcControllerForDmps4k(string key, string name, string typeName, DmpsRoutingController controller, uint num) { - Func dmpsHandler; - if (ChassisCpu3Dict.TryGetValue(typeName.ToLower(), out dmpsHandler)) + Func dmps4kHandler; + if (ChassisCpu3Dict.TryGetValue(typeName.ToLower(), out dmps4kHandler)) { var output = controller.Dmps.SwitcherOutputs[num] as DMOutput; if (output != null) { - return dmpsHandler(key, name, output); + return dmps4kHandler(key, name, output); } Debug.Console(0, Debug.ErrorLogLevel.Error, - "Cannot attach DM-RMC of type '{0}' to output {1} on DMPS chassis. Output is not a DM Output.", + "Cannot attach DM-RMC of type '{0}' to output {1} on DMPS-4K chassis. Output is not a DM Output.", typeName, num); return null; } - Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot create DM-RMC of type '{0}' to output {1} on DMPS chassis", typeName, num); + Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot create DM-RMC of type '{0}' to output {1} on DMPS-4K chassis", typeName, num); return null; } diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs index 4d8e41f6..5eb2d7b5 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs @@ -36,7 +36,7 @@ namespace PepperDash.Essentials.DM var ipid = props.Control.IpIdInt; var pKey = props.ParentDeviceKey.ToLower(); - if (pKey == "processor") + if (pKey == "processor") { // Catch constructor failures, mainly dues to IPID try @@ -65,99 +65,135 @@ namespace PepperDash.Essentials.DM { Debug.Console(0, "[{0}] WARNING: Cannot create DM-TX device: {1}", key, e); } + return null; } - else - { - var parentDev = DeviceManager.GetDeviceForKey(pKey); - if (!(parentDev is IDmSwitch)) - { - Debug.Console(0, "Cannot create DM device '{0}'. '{1}' is not a DM Chassis.", - key, pKey); - return null; - } + var parentDev = DeviceManager.GetDeviceForKey(pKey); + DMInput dmInput; + bool isCpu3 = false; + + if (parentDev is IDmSwitch) + { // Get the Crestron chassis and link stuff up - var switchDev = (parentDev as IDmSwitch); - var chassis = switchDev.Chassis; + var switchDev = (parentDev as IDmSwitch); + var chassis = switchDev.Chassis; - var num = props.ParentInputNumber; - if (num <= 0 || num > chassis.NumberOfInputs) - { - Debug.Console(0, "Cannot create DM device '{0}'. Input number '{1}' is out of range", - key, num); - return null; - } - else + //Check that the input is within range of this chassis' possible inputs + var num = props.ParentInputNumber; + if (num <= 0 || num > chassis.NumberOfInputs) + { + Debug.Console(0, "Cannot create DM device '{0}'. Input number '{1}' is out of range", + key, num); + return null; + } + + switchDev.TxDictionary.Add(num, key); + dmInput = chassis.Inputs[num]; + + //Determine if IpId is needed for this chassis type + if (chassis is DmMd8x8Cpu3 || chassis is DmMd16x16Cpu3 || + chassis is DmMd32x32Cpu3 || chassis is DmMd8x8Cpu3rps || + chassis is DmMd16x16Cpu3rps || chassis is DmMd32x32Cpu3rps || + chassis is DmMd128x128 || chassis is DmMd64x64) { - var controller = (parentDev as IDmSwitch); - controller.TxDictionary.Add(num, key); + isCpu3 = true; } - // Catch constructor failures, mainly dues to IPID - try - { - // Must use different constructor for CPU3 chassis types. No IPID - if (chassis is DmMd8x8Cpu3 || chassis is DmMd16x16Cpu3 || - chassis is DmMd32x32Cpu3 || chassis is DmMd8x8Cpu3rps || - chassis is DmMd16x16Cpu3rps || chassis is DmMd32x32Cpu3rps|| - chassis is DmMd128x128 || chassis is DmMd64x64) - { - if (typeName.StartsWith("dmtx200")) - return new DmTx200Controller(key, name, new DmTx200C2G(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx201c")) - return new DmTx201CController(key, name, new DmTx201C(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx201s")) - return new DmTx201SController(key, name, new DmTx201S(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4k100")) - return new DmTx4k100Controller(key, name, new DmTx4K100C1G(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4kz100")) - return new DmTx4kz100Controller(key, name, new DmTx4kz100C1G(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4k202")) - return new DmTx4k202CController(key, name, new DmTx4k202C(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4kz202")) - return new DmTx4kz202CController(key, name, new DmTx4kz202C(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4k302")) - return new DmTx4k302CController(key, name, new DmTx4k302C(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4kz302")) - return new DmTx4kz302CController(key, name, new DmTx4kz302C(chassis.Inputs[num])); - if (typeName.StartsWith("dmtx401")) - return new DmTx401CController(key, name, new DmTx401C(chassis.Inputs[num])); - if (typeName.StartsWith("hdbasettx")) - return new HDBaseTTxController(key, name, new HDTx3CB(chassis.Inputs[num])); - } - else - { - if (typeName.StartsWith("dmtx200")) - return new DmTx200Controller(key, name, new DmTx200C2G(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx201c")) - return new DmTx201CController(key, name, new DmTx201C(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx201s")) - return new DmTx201SController(key, name, new DmTx201S(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4k100")) - return new DmTx4k100Controller(key, name, new DmTx4K100C1G(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4kz100")) - return new DmTx4kz100Controller(key, name, new DmTx4kz100C1G(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4k202")) - return new DmTx4k202CController(key, name, new DmTx4k202C(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4kz202")) - return new DmTx4kz202CController(key, name, new DmTx4kz202C(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4k302")) - return new DmTx4k302CController(key, name, new DmTx4k302C(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx4kz302")) - return new DmTx4kz302CController(key, name, new DmTx4kz302C(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("dmtx401")) - return new DmTx401CController(key, name, new DmTx401C(ipid, chassis.Inputs[num])); - if (typeName.StartsWith("hdbasettx")) - return new HDBaseTTxController(key, name, new HDTx3CB(ipid, chassis.Inputs[num])); - } - } - catch (Exception e) - { - Debug.Console(0, "[{0}] WARNING: Cannot create DM-TX device: {1}", key, e); - } + } + else if(parentDev is DmpsRoutingController) + { + // Get the DMPS chassis and link stuff up + var dmpsDev = (parentDev as DmpsRoutingController); + var chassis = dmpsDev.Dmps; + + //Check that the input is within range of this chassis' possible inputs + var num = props.ParentInputNumber; + if (num <= 0 || num > chassis.SwitcherInputs.Count) + { + Debug.Console(0, "Cannot create DMPS device '{0}'. Input number '{1}' is out of range", + key, num); + return null; + } + + dmpsDev.TxDictionary.Add(num, key); + + try + { + dmInput = chassis.SwitcherInputs[num] as DMInput; + } + catch + { + Debug.Console(0, "Cannot create DMPS device '{0}'. Input number '{1}' is not a DM input", key, num); + return null; + } + } + + else + { + Debug.Console(0, "Cannot create DM device '{0}'. '{1}' is not a processor, DM Chassis or DMPS.", key, pKey); + return null; } - - return null; + + try + { + // Must use different constructor for CPU3 or DMPS3-4K types. No IPID + if (isCpu3 || Global.ControlSystemIsDmps4kType) + { + if (typeName.StartsWith("dmtx200")) + return new DmTx200Controller(key, name, new DmTx200C2G(dmInput)); + if (typeName.StartsWith("dmtx201c")) + return new DmTx201CController(key, name, new DmTx201C(dmInput)); + if (typeName.StartsWith("dmtx201s")) + return new DmTx201SController(key, name, new DmTx201S(dmInput)); + if (typeName.StartsWith("dmtx4k100")) + return new DmTx4k100Controller(key, name, new DmTx4K100C1G(dmInput)); + if (typeName.StartsWith("dmtx4kz100")) + return new DmTx4kz100Controller(key, name, new DmTx4kz100C1G(dmInput)); + if (typeName.StartsWith("dmtx4k202")) + return new DmTx4k202CController(key, name, new DmTx4k202C(dmInput)); + if (typeName.StartsWith("dmtx4kz202")) + return new DmTx4kz202CController(key, name, new DmTx4kz202C(dmInput)); + if (typeName.StartsWith("dmtx4k302")) + return new DmTx4k302CController(key, name, new DmTx4k302C(dmInput)); + if (typeName.StartsWith("dmtx4kz302")) + return new DmTx4kz302CController(key, name, new DmTx4kz302C(dmInput)); + if (typeName.StartsWith("dmtx401")) + return new DmTx401CController(key, name, new DmTx401C(dmInput)); + if (typeName.StartsWith("hdbasettx")) + return new HDBaseTTxController(key, name, new HDTx3CB(dmInput)); + } + else + { + if (typeName.StartsWith("dmtx200")) + return new DmTx200Controller(key, name, new DmTx200C2G(ipid, dmInput)); + if (typeName.StartsWith("dmtx201c")) + return new DmTx201CController(key, name, new DmTx201C(ipid, dmInput)); + if (typeName.StartsWith("dmtx201s")) + return new DmTx201SController(key, name, new DmTx201S(ipid, dmInput)); + if (typeName.StartsWith("dmtx4k100")) + return new DmTx4k100Controller(key, name, new DmTx4K100C1G(ipid, dmInput)); + if (typeName.StartsWith("dmtx4kz100")) + return new DmTx4kz100Controller(key, name, new DmTx4kz100C1G(ipid, dmInput)); + if (typeName.StartsWith("dmtx4k202")) + return new DmTx4k202CController(key, name, new DmTx4k202C(ipid, dmInput)); + if (typeName.StartsWith("dmtx4kz202")) + return new DmTx4kz202CController(key, name, new DmTx4kz202C(ipid, dmInput)); + if (typeName.StartsWith("dmtx4k302")) + return new DmTx4k302CController(key, name, new DmTx4k302C(ipid, dmInput)); + if (typeName.StartsWith("dmtx4kz302")) + return new DmTx4kz302CController(key, name, new DmTx4kz302C(ipid, dmInput)); + if (typeName.StartsWith("dmtx401")) + return new DmTx401CController(key, name, new DmTx401C(ipid, dmInput)); + if (typeName.StartsWith("hdbasettx")) + return new HDBaseTTxController(key, name, new HDTx3CB(ipid, dmInput)); + } + } + catch (Exception e) + { + Debug.Console(0, "[{0}] WARNING: Cannot create DM-TX device: {1}", key, e); + } + + return null; } } @@ -185,11 +221,12 @@ namespace PepperDash.Essentials.DM protected DmTxControllerBase(string key, string name, EndpointTransmitterBase hardware) : base(key, name, hardware) { - // if wired to a chassis, skip registration step in base class - if (hardware.DMInput != null) - { - this.PreventRegistration = true; - } + // if wired to a chassis or DMPS, skip registration step in base class + if (hardware.DMInput != null || (Global.ControlSystemIsDmpsType && hardware.DMInput != null)) + { + this.PreventRegistration = true; + } + AddToFeedbackList(ActiveVideoInputFeedback); } @@ -226,6 +263,7 @@ namespace PepperDash.Essentials.DM tx.AnyVideoInput.VideoStatus.VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VideoSyncStatus.JoinNumber]); tx.AnyVideoInput.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentInputResolution.JoinNumber]); trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber].UShortValue = (ushort)tx.HdcpSupportCapability; + trilist.StringInput[joinMap.Name.JoinNumber].StringValue = tx.Name; bool hdcpTypeSimple; 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 4f674d9d..cef61b1a 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -93,6 +93,7 @@ + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs index def22069..1ef679ba 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs @@ -29,6 +29,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras public abstract class CameraBase : ReconfigurableDevice, IRoutingOutputs { + [JsonProperty("controlMode", NullValueHandling = NullValueHandling.Ignore)] public eCameraControlMode ControlMode { get; protected set; } #region IRoutingOutputs Members @@ -37,6 +38,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras #endregion + [JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)] public bool CanPan { get @@ -44,7 +46,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras return (Capabilities & eCameraCapabilities.Pan) == eCameraCapabilities.Pan; } } - + [JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)] public bool CanTilt { get @@ -52,7 +54,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras return (Capabilities & eCameraCapabilities.Tilt) == eCameraCapabilities.Tilt; } } - + [JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)] public bool CanZoom { get @@ -60,7 +62,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras return (Capabilities & eCameraCapabilities.Zoom) == eCameraCapabilities.Zoom; } } - + [JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)] public bool CanFocus { get @@ -216,21 +218,11 @@ namespace PepperDash.Essentials.Devices.Common.Cameras var presetsCamera = cameraDevice as IHasCameraPresets; presetsCamera.PresetsListHasChanged += new EventHandler((o, a) => { - for (int i = 1; i <= joinMap.NumberOfPresets.JoinNumber; i++) - { - int tempNum = i - 1; - - string label = ""; - - var preset = presetsCamera.Presets.FirstOrDefault(p => p.ID.Equals(i)); - - if (preset != null) - label = preset.Description; - - trilist.SetString((ushort) (joinMap.PresetLabelStart.JoinNumber + tempNum), label); - } + SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist); }); + SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist); + for (int i = 0; i < joinMap.NumberOfPresets.JoinNumber; i++) { int tempNum = i; @@ -246,10 +238,35 @@ namespace PepperDash.Essentials.Devices.Common.Cameras presetsCamera.PresetStore(tempNum, label); }); } + trilist.OnlineStatusChange += (sender, args) => + { + if (!args.DeviceOnLine) + { return; } + + SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist); + }; + + } + } + private void SendCameraPresetNamesToApi(IHasCameraPresets presetsCamera, CameraControllerJoinMap joinMap, BasicTriList trilist) + { + for (int i = 1; i <= joinMap.NumberOfPresets.JoinNumber; i++) + { + int tempNum = i - 1; + + string label = ""; + + var preset = presetsCamera.Presets.FirstOrDefault(p => p.ID.Equals(i)); + + if (preset != null) + label = preset.Description; + + trilist.SetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum), label); } } } + public class CameraPreset : PresetBase { public CameraPreset(int id, string description, bool isDefined, bool isDefinable) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraControl.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraControl.cs index 208b38de..cbf53476 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraControl.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraControl.cs @@ -57,6 +57,11 @@ namespace PepperDash.Essentials.Devices.Common.Cameras void CameraMuteToggle(); } + public interface IHasCameraMuteWithUnmuteReqeust : IHasCameraMute + { + event EventHandler VideoUnmuteRequested; + } + public class CameraSelectedEventArgs : EventArgs { public CameraBase SelectedCamera { get; private set; } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs index fb8ae79c..6acf5850 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs @@ -525,6 +525,15 @@ namespace PepperDash.Essentials.Devices.Common.Cameras public event EventHandler PresetsListHasChanged; + protected void OnPresetsListHasChanged() + { + var handler = PresetsListHasChanged; + if (handler == null) + return; + + handler.Invoke(this, EventArgs.Empty); + } + public List Presets { get; private set; } public void PresetSelect(int preset) @@ -537,6 +546,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras SavePreset(preset); } + #endregion #region IHasCameraFocusControl Members diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs index 75c4fc1a..d1dbdf5f 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs @@ -12,34 +12,40 @@ namespace PepperDash.Essentials.Devices.Common.Codec { public class CodecActiveCallItem { - [JsonProperty("name")] + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } - [JsonProperty("number")] + [JsonProperty("number", NullValueHandling = NullValueHandling.Ignore)] public string Number { get; set; } - [JsonProperty("type")] + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] [JsonConverter(typeof(StringEnumConverter))] public eCodecCallType Type { get; set; } - [JsonProperty("status")] + [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)] [JsonConverter(typeof(StringEnumConverter))] public eCodecCallStatus Status { get; set; } - [JsonProperty("direction")] + [JsonProperty("direction", NullValueHandling = NullValueHandling.Ignore)] [JsonConverter(typeof(StringEnumConverter))] public eCodecCallDirection Direction { get; set; } - [JsonProperty("id")] + [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] public string Id { get; set; } + [JsonProperty("isOnHold", NullValueHandling = NullValueHandling.Ignore)] + public bool IsOnHold { get; set; } + + [JsonProperty("duration", NullValueHandling = NullValueHandling.Ignore)] + public TimeSpan Duration { get; set; } + //public object CallMetaData { get; set; } /// /// Returns true when this call is any status other than /// Unknown, Disconnected, Disconnecting /// - [JsonProperty("isActiveCall")] + [JsonProperty("isActiveCall", NullValueHandling = NullValueHandling.Ignore)] public bool IsActiveCall { get diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasCallHold.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasCallHold.cs new file mode 100644 index 00000000..78211841 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasCallHold.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public interface IHasCallHold + { + /// + /// Put the specified call on hold + /// + /// + void HoldCall(CodecActiveCallItem activeCall); + + /// + /// Resume the specified call + /// + /// + void ResumeCall(CodecActiveCallItem activeCall); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs new file mode 100644 index 00000000..7bb9b924 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + /// + /// Describes a device that has Do Not Disturb mode capability + /// + public interface IHasDoNotDisturbMode + { + /// + /// Indictes whether Do Not Disturb mode is on (Activated) + /// + BoolFeedback DoNotDisturbModeIsOnFeedback { get; } + + /// + /// Activates Do Not Disturb mode + /// + void ActivateDoNotDisturbMode(); + + /// + /// Deactivates Do Not Disturb mode + /// + void DeactivateDoNotDisturbMode(); + + /// + /// Toggles Do Not Disturb mode + /// + void ToggleDoNotDisturbMode(); + } + + public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode + { + /// + /// Activates Do Not Disturb mode with a timeout + /// + /// + void ActivateDoNotDisturbMode(int timeout); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs index 256938d1..83a74022 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs @@ -23,9 +23,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec public enum eCodecOccurrenceType { Unknown = 0, - Placed, - Received, - NoAnswer + Placed = 1, + Received = 2, + NoAnswer = 3, } /// diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs index 642f03d0..57dbbd6b 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs @@ -65,8 +65,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec { /// /// Represents the contents of the directory + /// We don't want to serialize this for messages to MobileControl. MC can combine Contacts and Folders to get the same data /// - [JsonProperty("directoryResults")] + [JsonIgnore] public List CurrentDirectoryResults { get; private set; } [JsonProperty("contacts")] diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/SamsungMDCDisplay.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/SamsungMDCDisplay.cs index 65e38408..9fcb7295 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/SamsungMDCDisplay.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/SamsungMDCDisplay.cs @@ -99,7 +99,7 @@ namespace PepperDash.Essentials.Devices.Displays WarmupTime = 10000; CooldownTime = 8000; - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 2000, 120000, 300000, StatusGet); + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 2000, 120000, 300000, StatusGet, true); DeviceManager.AddDevice(CommunicationMonitor); VolumeIncrementer = new ActionIncrementer(655, 0, 65535, 800, 80, 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 5a1a501b..fe4d5f61 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 @@ -108,6 +108,8 @@ + + @@ -117,6 +119,7 @@ + @@ -127,7 +130,9 @@ + + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs index 6f68b369..9e8b0554 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs @@ -41,12 +41,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public void PanLeft() { - ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Left CallId: {0}", CallId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: Left CallId: {0}", CallId)); } public void PanRight() { - ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Right CallId: {0}", CallId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: Right CallId: {0}", CallId)); } public void PanStop() @@ -60,12 +60,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public void TiltDown() { - ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Down CallId: {0}", CallId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: Down CallId: {0}", CallId)); } public void TiltUp() { - ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Up CallId: {0}", CallId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: Up CallId: {0}", CallId)); } public void TiltStop() @@ -79,12 +79,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public void ZoomIn() { - ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomIn CallId: {0}", CallId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomIn CallId: {0}", CallId)); } public void ZoomOut() { - ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomOut CallId: {0}", CallId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomOut CallId: {0}", CallId)); } public void ZoomStop() @@ -97,7 +97,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco void Stop() { - ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Stop CallId: {0}", CallId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Call FarEndControl Camera Stop CallId: {0}", CallId)); } public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) @@ -116,7 +116,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco /// /// The ID of the camera on the codec /// - protected uint CameraId { get; private set; } + public uint CameraId { get; private set; } /// /// Valid range 1-15 @@ -202,7 +202,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (!isMoving) { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Left PanSpeed: {1}", CameraId, PanSpeed)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Left PanSpeed: {1}", CameraId, PanSpeed)); isPanning = true; } } @@ -211,14 +211,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (!isMoving) { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Right PanSpeed: {1}", CameraId, PanSpeed)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Right PanSpeed: {1}", CameraId, PanSpeed)); isPanning = true; } } public void PanStop() { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Stop", CameraId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Stop", CameraId)); isPanning = false; } @@ -232,7 +232,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (!isMoving) { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Down TiltSpeed: {1}", CameraId, TiltSpeed)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Down TiltSpeed: {1}", CameraId, TiltSpeed)); isTilting = true; } } @@ -241,14 +241,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (!isMoving) { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Up TiltSpeed: {1}", CameraId, TiltSpeed)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Up TiltSpeed: {1}", CameraId, TiltSpeed)); isTilting = true; } } public void TiltStop() { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Stop", CameraId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Stop", CameraId)); isTilting = false; } @@ -260,7 +260,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (!isMoving) { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: In ZoomSpeed: {1}", CameraId, ZoomSpeed)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: In ZoomSpeed: {1}", CameraId, ZoomSpeed)); isZooming = true; } } @@ -269,14 +269,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (!isMoving) { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Out ZoomSpeed: {1}", CameraId, ZoomSpeed)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Out ZoomSpeed: {1}", CameraId, ZoomSpeed)); isZooming = true; } } public void ZoomStop() { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Stop", CameraId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Stop", CameraId)); isZooming = false; } @@ -288,7 +288,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (!isMoving) { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Near", CameraId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Near", CameraId)); isFocusing = true; } } @@ -297,20 +297,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { if (!isMoving) { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Far", CameraId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Far", CameraId)); isFocusing = true; } } public void FocusStop() { - ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Stop", CameraId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Stop", CameraId)); isFocusing = false; } public void TriggerAutoFocus() { - ParentCodec.SendText(string.Format("xCommand Camera TriggerAutofocus CameraId: {0}", CameraId)); + ParentCodec.EnqueueCommand(string.Format("xCommand Camera TriggerAutofocus CameraId: {0}", CameraId)); } #endregion diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs new file mode 100644 index 00000000..2396afdc --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs @@ -0,0 +1,205 @@ +using System; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges.JoinMaps; + + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + public class CiscoCodecJoinMap : VideoCodecControllerJoinMap + { + #region Digital + + [JoinName("PresentationLocalOnly")] + public JoinDataComplete PresentationLocalOnly = new JoinDataComplete( + new JoinData + { + JoinNumber = 205, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Presentation Local Only Feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("PresentationLocalRemote")] + public JoinDataComplete PresentationLocalRemote = new JoinDataComplete( + new JoinData + { + JoinNumber = 206, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Presentation Local and Remote Feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ActivateDoNotDisturbMode")] + public JoinDataComplete ActivateDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 241, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Do Not Disturb Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DeactivateDoNotDisturbMode")] + public JoinDataComplete DeactivateDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 242, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Deactivates Do Not Disturb Mode. FB High if deactivated.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ToggleDoNotDisturbMode")] + public JoinDataComplete ToggleDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 243, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Toggles Do Not Disturb Mode.", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ActivateStandby")] + public JoinDataComplete ActivateStandby = new JoinDataComplete( + new JoinData + { + JoinNumber = 246, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Standby Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DeactivateStandby")] + public JoinDataComplete DeactivateStandby = new JoinDataComplete( + new JoinData + { + JoinNumber = 247, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Deactivates Standby Mode. FB High if deactivated.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ActivateHalfWakeMode")] + public JoinDataComplete ActivateHalfWakeMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 248, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Half Wake Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("EnteringStandbyMode")] + public JoinDataComplete EnteringStandbyMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 249, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "High to indicate that the codec is entering standby mode", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + #endregion + + + #region Analog + + [JoinName("RingtoneVolume")] + public JoinDataComplete RingtoneVolume = new JoinDataComplete( + new JoinData + { + JoinNumber = 21, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Ringtone volume set/FB. Valid values are 0 - 100 in increments of 5 (5, 10, 15, 20, etc.)", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("PresentationSource")] + public JoinDataComplete PresentationSource = new JoinDataComplete( + new JoinData + { + JoinNumber = 201, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Presentation set/FB. Valid values are 0 - 6 depending on the codec model.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); + + + #endregion + + + #region Serials + + [JoinName("CommandToDevice")] + public JoinDataComplete CommandToDevice = new JoinDataComplete( + new JoinData + { + JoinNumber = 5, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Sends a serial command to the device. Do not include the delimiter, it will be added automatically.", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Serial + }); + + + + #endregion + + public CiscoCodecJoinMap(uint joinStart) + : base(joinStart, typeof(CiscoCodecJoinMap)) + { + } + + public CiscoCodecJoinMap(uint joinStart, Type type) + : base(joinStart, type) + { + } + } +} \ No newline at end of file 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 8dac777c..c9e584d3 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 @@ -1,2199 +1,2624 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.CrestronThread; -using Crestron.SimplSharpPro.DeviceSupport; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using PepperDash.Essentials.Core.Routing; -using PepperDash.Essentials.Devices.Common.Cameras; -using PepperDash.Essentials.Devices.Common.Codec; -using PepperDash.Essentials.Devices.Common.VideoCodec; -using PepperDash.Essentials.Core.Queues; - -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, IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute - { - private bool _externalSourceChangeRequested; - - public event EventHandler DirectoryResultReturned; - - private CTimer _brandingTimer; - - public CommunicationGather PortGather { get; private set; } - - public StatusMonitorBase CommunicationMonitor { get; private set; } - - private GenericQueue ReceiveQueue; - - public BoolFeedback PresentationViewMaximizedFeedback { get; private set; } - - string CurrentPresentationView; - - public BoolFeedback RoomIsOccupiedFeedback { get; private set; } - - public IntFeedback PeopleCountFeedback { get; private set; } - - public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; } - - public BoolFeedback SelfviewIsOnFeedback { get; private set; } - - public StringFeedback SelfviewPipPositionFeedback { get; private set; } - - public StringFeedback LocalLayoutFeedback { get; private set; } - - public BoolFeedback LocalLayoutIsProminentFeedback { get; private set; } - - public BoolFeedback FarEndIsSharingContentFeedback { get; private set; } - - private CodecCommandWithLabel CurrentSelfviewPipPosition; - - private CodecCommandWithLabel CurrentLocalLayout; - - /// - /// List the available positions for the selfview PIP window - /// - public List SelfviewPipPositions = new List() - { - new CodecCommandWithLabel("CenterLeft", "Center Left"), - new CodecCommandWithLabel("CenterRight", "Center Right"), - new CodecCommandWithLabel("LowerLeft", "Lower Left"), - new CodecCommandWithLabel("LowerRight", "Lower Right"), - new CodecCommandWithLabel("UpperCenter", "Upper Center"), - new CodecCommandWithLabel("UpperLeft", "Upper Left"), - new CodecCommandWithLabel("UpperRight", "Upper Right"), - }; - - /// - /// Lists the available options for local layout - /// - public List LocalLayouts = new List() - { - //new CodecCommandWithLabel("auto", "Auto"), - //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now - new CodecCommandWithLabel("equal","Equal"), - new CodecCommandWithLabel("overlay","Overlay"), - new CodecCommandWithLabel("prominent","Prominent"), - new CodecCommandWithLabel("single","Single") - }; - - private CiscoCodecConfiguration.RootObject CodecConfiguration = new CiscoCodecConfiguration.RootObject(); - - private CiscoCodecStatus.RootObject CodecStatus = new CiscoCodecStatus.RootObject(); - - public CodecCallHistory CallHistory { get; private set; } - - public CodecCallFavorites CallFavorites { get; private set; } - - /// - /// The root level of the directory - /// - public CodecDirectory DirectoryRoot { get; private set; } - - /// - /// Represents the current state of the directory and is computed on get - /// - public CodecDirectory CurrentDirectoryResult - { - get - { - if (DirectoryBrowseHistory.Count > 0) - return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1]; - else - return DirectoryRoot; - } - } - - public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } - - /// - /// Tracks the directory browse history when browsing beyond the root directory - /// - public List DirectoryBrowseHistory { get; private set; } - - public CodecScheduleAwareness CodecSchedule { get; private set; } - - /// - /// Gets and returns the scaled volume of the codec - /// - protected override Func VolumeLevelFeedbackFunc - { - get - { - return () => CrestronEnvironment.ScaleWithLimits(CodecStatus.Status.Audio.Volume.IntValue, 100, 0, 65535, 0); - } - } - - protected override Func PrivacyModeIsOnFeedbackFunc - { - get - { - return () => CodecStatus.Status.Audio.Microphones.Mute.BoolValue; - } - } - - protected override Func StandbyIsOnFeedbackFunc - { - get - { - return () => CodecStatus.Status.Standby.State.BoolValue; - } - } - - /// - /// Gets the value of the currently shared source, or returns null - /// - protected override Func SharingSourceFeedbackFunc - { - get - { - return () => PresentationSourceKey; - } - } - - protected override Func SharingContentIsOnFeedbackFunc - { - get - { - return () => CodecStatus.Status.Conference.Presentation.Mode.BoolValue; - } - } - - protected Func FarEndIsSharingContentFeedbackFunc - { - get - { - return () => CodecStatus.Status.Conference.Presentation.Mode.Value == "Receiving"; - } - } - - protected override Func MuteFeedbackFunc - { - get - { - return () => CodecStatus.Status.Audio.VolumeMute.BoolValue; - } - } - - protected Func RoomIsOccupiedFeedbackFunc - { - get - { - return () => CodecStatus.Status.RoomAnalytics.PeoplePresence.BoolValue; - } - } - - protected Func PeopleCountFeedbackFunc - { - get - { - return () => CodecStatus.Status.RoomAnalytics.PeopleCount.Current.IntValue; - } - } - - protected Func SpeakerTrackIsOnFeedbackFunc - { - get - { - return () => CodecStatus.Status.Cameras.SpeakerTrack.Status.BoolValue; - } - } - - protected Func SelfViewIsOnFeedbackFunc - { - get - { - return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue; - } - } - - protected Func SelfviewPipPositionFeedbackFunc - { - get - { - return () => CurrentSelfviewPipPosition.Label; - } - } - - protected Func LocalLayoutFeedbackFunc - { - get - { - return () => CurrentLocalLayout.Label; - } - } - - protected Func LocalLayoutIsProminentFeedbackFunc - { - get - { - return () => CurrentLocalLayout.Label == "Prominent"; - } - } - - - private string CliFeedbackRegistrationExpression; - - private CodecSyncState SyncState; - - public CodecPhonebookSyncState PhonebookSyncState { get; private set; } - - private StringBuilder JsonMessage; - - private bool JsonFeedbackMessageIsIncoming; - - public bool CommDebuggingIsOn; - - string Delimiter = "\r\n"; - - /// - /// Used to track the current connector used for the presentation source - /// - int PresentationSource; - - string PresentationSourceKey; - - string PhonebookMode = "Local"; // Default to Local - - uint PhonebookResultsLimit = 255; // Could be set later by config. - - CTimer LoginMessageReceivedTimer; - CTimer RetryConnectionTimer; - - // **___________________________________________________________________** - // Timers to be moved to the global system timer at a later point.... - CTimer BookingsRefreshTimer; - CTimer PhonebookRefreshTimer; - // **___________________________________________________________________** - - public RoutingInputPort CodecOsdIn { get; private set; } - public RoutingInputPort HdmiIn2 { get; private set; } - public RoutingInputPort HdmiIn3 { get; private set; } - public RoutingOutputPort HdmiOut1 { get; private set; } - public RoutingOutputPort HdmiOut2 { get; private set; } - - - // Constructor for IBasicCommunication - public CiscoSparkCodec(DeviceConfig config, IBasicCommunication comm) - : base(config) - { - var props = JsonConvert.DeserializeObject(config.Properties.ToString()); - - // Use the configured phonebook results limit if present - if (props.PhonebookResultsLimit > 0) - { - PhonebookResultsLimit = props.PhonebookResultsLimit; - } - - // The queue that will collect the repsonses in the order they are received - ReceiveQueue = new GenericQueue(this.Key + "-rxQueue", 25); - - RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); - PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc); - CameraAutoModeIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc); - SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); - SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); - LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc); - LocalLayoutIsProminentFeedback = new BoolFeedback(LocalLayoutIsProminentFeedbackFunc); - FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); - CameraIsOffFeedback = new BoolFeedback(() => CodecStatus.Status.Video.Input.MainVideoMute.BoolValue); - CameraIsMutedFeedback = CameraIsOffFeedback; - SupportsCameraOff = true; - - PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized"); - - Communication = comm; - - if (props.CommunicationMonitorProperties != null) - { - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); - } - else - { - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xStatus SystemUnit Software Version\r"); - } - - if (props.Sharing != null) - AutoShareContentWhileInCall = props.Sharing.AutoShareContentWhileInCall; - - ShowSelfViewByDefault = props.ShowSelfViewByDefault; - - DeviceManager.AddDevice(CommunicationMonitor); - - PhonebookMode = props.PhonebookMode; - - SyncState = new CodecSyncState(Key + "--Sync"); - - PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); - - SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted); - - PortGather = new CommunicationGather(Communication, Delimiter); - PortGather.IncludeDelimiter = true; - PortGather.LineReceived += this.Port_LineReceived; - - CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration); - - CallHistory = new CodecCallHistory(); - - - if (props.Favorites != null) - { - CallFavorites = new CodecCallFavorites(); - CallFavorites.Favorites = props.Favorites; - } - - DirectoryRoot = new CodecDirectory(); - - DirectoryBrowseHistory = new List(); - - CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0); - - CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); - - CodecSchedule = new CodecScheduleAwareness(); - - //Set Feedback Actions - SetFeedbackActions(); - - CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); - HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this); - HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this); - - HdmiOut1 = new RoutingOutputPort(RoutingPortNames.HdmiOut1, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this); - HdmiOut2 = new RoutingOutputPort(RoutingPortNames.HdmiOut2, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this); - - InputPorts.Add(CodecOsdIn); - InputPorts.Add(HdmiIn2); - InputPorts.Add(HdmiIn3); - OutputPorts.Add(HdmiOut1); - - SetUpCameras(); - - CreateOsdSource(); - - ExternalSourceListEnabled = props.ExternalSourceListEnabled; - ExternalSourceInputPort = props.ExternalSourceInputPort; - - if (props.UiBranding == null) - { - return; - } - Debug.Console(2, this, "Setting branding properties enable: {0} _brandingUrl {1}", props.UiBranding.Enable, - props.UiBranding.BrandingUrl); - - BrandingEnabled = props.UiBranding.Enable; - - _brandingUrl = props.UiBranding.BrandingUrl; - } - - private void SetFeedbackActions() - { - CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate; - CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate; - CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate; - CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate; - CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate; - CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate; - CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate; - CodecStatus.Status.Cameras.SpeakerTrack.Availability.ValueChangedAction = () => { SupportsCameraAutoMode = CodecStatus.Status.Cameras.SpeakerTrack.Availability.BoolValue; }; - CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate; - CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus; - CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; - CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = () => - { - SharingContentIsOnFeedback.FireUpdate(); - FarEndIsSharingContentFeedback.FireUpdate(); - }; - - try - { - CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate; - } - catch (Exception ex) - { - Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex); - - if (ex.InnerException != null) - { - Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex); - } - } - } - - /// - /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input - /// to enable routing - /// - void CreateOsdSource() - { - OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); - DeviceManager.AddDevice(OsdSource); - var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); - TieLineCollection.Default.Add(tl); - } - - public void InitializeBranding(string roomKey) - { - Debug.Console(1, this, "Initializing Branding for room {0}", roomKey); - - if (!BrandingEnabled) - { - return; - } - - var mcBridgeKey = String.Format("mobileControlBridge-{0}", roomKey); - - var mcBridge = DeviceManager.GetDeviceForKey(mcBridgeKey) as IMobileControlRoomBridge; - - if (!String.IsNullOrEmpty(_brandingUrl)) - { - Debug.Console(1, this, "Branding URL found: {0}", _brandingUrl); - if (_brandingTimer != null) - { - _brandingTimer.Stop(); - _brandingTimer.Dispose(); - } - - _brandingTimer = new CTimer((o) => - { - if (_sendMcUrl) - { - SendMcBrandingUrl(mcBridge); - _sendMcUrl = false; - } - else - { - SendBrandingUrl(); - _sendMcUrl = true; - } - }, 0, 15000); - } else if (String.IsNullOrEmpty(_brandingUrl)) - { - Debug.Console(1, this, "No Branding URL found"); - if (mcBridge == null) return; - - Debug.Console(2, this, "Setting QR code URL: {0}", mcBridge.QrCodeUrl); - - mcBridge.UserCodeChanged += (o, a) => SendMcBrandingUrl(mcBridge); - mcBridge.UserPromptedForCode += (o, a) => DisplayUserCode(mcBridge.UserCode); - - SendMcBrandingUrl(mcBridge); - } - } - - /// - /// Displays the code for the specified duration - /// - /// Mobile Control user code - private void DisplayUserCode(string code) - { - SendText(string.Format("xcommand userinterface message alert display title:\"Mobile Control User Code:\" text:\"{0}\" duration: 30", code)); - } - - private void SendMcBrandingUrl(IMobileControlRoomBridge mcBridge) - { - if (mcBridge == null) - { - return; - } - - Debug.Console(1, this, "Sending url: {0}", mcBridge.QrCodeUrl); - - SendText("xconfiguration userinterface custommessage: \"Scan the QR code with a mobile phone to get started\""); - SendText("xconfiguration userinterface osd halfwakemessage: \"Tap the touch panel or scan the QR code with a mobile phone to get started\""); - - var checksum = !String.IsNullOrEmpty(mcBridge.QrCodeChecksum) - ? String.Format("checksum: {0} ", mcBridge.QrCodeChecksum) - : String.Empty; - - SendText(String.Format( - "xcommand userinterface branding fetch {1}type: branding url: {0}", - mcBridge.QrCodeUrl, checksum)); - SendText(String.Format( - "xcommand userinterface branding fetch {1}type: halfwakebranding url: {0}", - mcBridge.QrCodeUrl, checksum)); - } - - private void SendBrandingUrl() - { - Debug.Console(1, this, "Sending url: {0}", _brandingUrl); - - SendText(String.Format("xcommand userinterface branding fetch type: branding url: {0}", - _brandingUrl)); - SendText(String.Format("xcommand userinterface branding fetch type: halfwakebranding url: {0}", - _brandingUrl)); - } - /// - /// Starts the HTTP feedback server and syncronizes state of codec - /// - /// - public override bool CustomActivate() - { - CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); - - return base.CustomActivate(); - } - - #region Overrides of Device - - public override void Initialize() - { - var socket = Communication as ISocketStatus; - if (socket != null) - { - socket.ConnectionChange += new EventHandler(socket_ConnectionChange); - } - - Communication.Connect(); - - CommunicationMonitor.Start(); - - const string prefix = "xFeedback register "; - - CliFeedbackRegistrationExpression = - prefix + "/Configuration" + Delimiter + - prefix + "/Status/Audio" + Delimiter + - prefix + "/Status/Call" + Delimiter + - prefix + "/Status/Conference/Presentation" + Delimiter + - prefix + "/Status/Cameras/SpeakerTrack" + Delimiter + - prefix + "/Status/RoomAnalytics" + Delimiter + - prefix + "/Status/RoomPreset" + Delimiter + - prefix + "/Status/Standby" + Delimiter + - prefix + "/Status/Video/Selfview" + Delimiter + - prefix + "/Status/Video/Layout" + Delimiter + - prefix + "/Status/Video/Input/MainVideoMute" + Delimiter + - prefix + "/Bookings" + Delimiter + - prefix + "/Event/CallDisconnect" + Delimiter + - prefix + "/Event/Bookings" + Delimiter + - prefix + "/Event/CameraPresetListUpdated" + Delimiter + - prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter; - } - - #endregion - - /// - /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc. - /// - /// - /// - void SyncState_InitialSyncCompleted(object sender, EventArgs e) - { - // Fire the ready event - SetIsReady(); - //CommDebuggingIsOn = false; - - GetCallHistory(); - - PhonebookRefreshTimer = new CTimer(CheckCurrentHour, 3600000, 3600000); // check each hour to see if the phonebook should be downloaded - GetPhonebook(null); - - BookingsRefreshTimer = new CTimer(GetBookings, 900000, 900000); // 15 minute timer to check for new booking info - GetBookings(null); - } - - public void SetCommDebug(string s) - { - if (s == "1") - { - CommDebuggingIsOn = true; - Debug.Console(0, this, "Comm Debug Enabled."); - } - else - { - CommDebuggingIsOn = false; - Debug.Console(0, this, "Comm Debug Disabled."); - } - } - - void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) - { - Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); - if (e.Client.IsConnected) - { - if(!SyncState.LoginMessageWasReceived) - LoginMessageReceivedTimer = new CTimer(o => DisconnectClientAndReconnect(), 5000); - } - else - { - SyncState.CodecDisconnected(); - PhonebookSyncState.CodecDisconnected(); - - if (PhonebookRefreshTimer != null) - { - PhonebookRefreshTimer.Stop(); - PhonebookRefreshTimer = null; - } - - if (BookingsRefreshTimer != null) - { - BookingsRefreshTimer.Stop(); - BookingsRefreshTimer = null; - } - } - } - - void DisconnectClientAndReconnect() - { - Debug.Console(1, this, "Retrying connection to codec."); - - Communication.Disconnect(); - - RetryConnectionTimer = new CTimer(o => Communication.Connect(), 2000); - - //CrestronEnvironment.Sleep(2000); - - //Communication.Connect(); - } - - /// - /// Gathers responses from the codec (including the delimiter. Responses are checked to see if they contain JSON data and if so, the data is collected until a complete JSON - /// message is received before forwarding the message to be deserialized. - /// - /// - /// - void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) - { - if (CommDebuggingIsOn) - { - if (!JsonFeedbackMessageIsIncoming) - Debug.Console(1, this, "RX: '{0}'", args.Text); - } - - if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message - { - JsonFeedbackMessageIsIncoming = true; - - if (CommDebuggingIsOn) - Debug.Console(1, this, "Incoming JSON message..."); - - JsonMessage = new StringBuilder(); - } - else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message - { - JsonFeedbackMessageIsIncoming = false; - - JsonMessage.Append(args.Text); - - if (CommDebuggingIsOn) - Debug.Console(1, this, "Complete JSON Received:\n{0}", JsonMessage.ToString()); - - // Enqueue the complete message to be deserialized - - ReceiveQueue.Enqueue(new ProcessStringMessage(JsonMessage.ToString(), DeserializeResponse)); - - return; - } - - if(JsonFeedbackMessageIsIncoming) - { - JsonMessage.Append(args.Text); - - //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); - return; - } - - if (!SyncState.InitialSyncComplete) - { - switch (args.Text.Trim().ToLower()) // remove the whitespace - { - case "*r login successful": - { - SyncState.LoginMessageReceived(); - - if(LoginMessageReceivedTimer != null) - LoginMessageReceivedTimer.Stop(); - - SendText("xPreferences outputmode json"); - break; - } - case "xpreferences outputmode json": - { - if (!SyncState.InitialStatusMessageWasReceived) - SendText("xStatus"); - break; - } - case "xfeedback register /event/calldisconnect": - { - SyncState.FeedbackRegistered(); - break; - } - } - } - - - } - - /// - /// Appends the delimiter and send the command to the codec - /// - /// - public void SendText(string command) - { - if (CommDebuggingIsOn) - Debug.Console(1, this, "Sending: '{0}'", command); - - Communication.SendText(command + Delimiter); - } - - void DeserializeResponse(string response) - { - try - { - //// Serializer settings. We want to ignore null values and missing members - //JsonSerializerSettings settings = new JsonSerializerSettings(); - //settings.NullValueHandling = NullValueHandling.Ignore; - //settings.MissingMemberHandling = MissingMemberHandling.Ignore; - //settings.ObjectCreationHandling = ObjectCreationHandling.Auto; - - if (response.IndexOf("\"Status\":{") > -1 || response.IndexOf("\"Status\": {") > -1) - { - // Status Message - - // Temp object so we can inpsect for call data before simply deserializing - CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject(); - - JsonConvert.PopulateObject(response, tempCodecStatus); - - // Check to see if the message contains /Status/Conference/Presentation/LocalInstance and extract source value - var conference = tempCodecStatus.Status.Conference; - - if (conference.Presentation.LocalInstance.Count > 0) - { - if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost)) - PresentationSource = 0; - else if (conference.Presentation.LocalInstance[0].Source != null) - { - PresentationSource = conference.Presentation.LocalInstance[0].Source.IntValue; - } - } - - // Check to see if this is a call status message received after the initial status message - if (tempCodecStatus.Status.Call.Count > 0) - { - // Iterate through the call objects in the response - foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call) - { - var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id)); - - if (tempActiveCall != null) - { - bool changeDetected = false; - - eCodecCallStatus newStatus = eCodecCallStatus.Unknown; - - // Update properties of ActiveCallItem - if(call.Status != null) - if (!string.IsNullOrEmpty(call.Status.Value)) - { - tempActiveCall.Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value); - - if (newStatus == eCodecCallStatus.Connected) - GetCallHistory(); - - changeDetected = true; - } - if (call.CallType != null) - if (!string.IsNullOrEmpty(call.CallType.Value)) - { - tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value); - changeDetected = true; - } - if (call.DisplayName != null) - if (!string.IsNullOrEmpty(call.DisplayName.Value)) - { - tempActiveCall.Name = call.DisplayName.Value; - changeDetected = true; - } - if (call.Direction != null) - { - if (!string.IsNullOrEmpty(call.Direction.Value)) - { - tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value); - changeDetected = true; - } - } - - if (changeDetected) - { - SetSelfViewMode(); - OnCallStatusChange(tempActiveCall); - ListCalls(); - } - } - else if( call.ghost == null ) // if the ghost value is present the call has ended already - { - // Create a new call item - var newCallItem = new CodecActiveCallItem() - { - Id = call.id, - Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value), - Name = call.DisplayName.Value, - Number = call.RemoteNumber.Value, - Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value), - Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value) - }; - - // Add it to the ActiveCalls List - ActiveCalls.Add(newCallItem); - - ListCalls(); - - SetSelfViewMode(); - OnCallStatusChange(newCallItem); - } - - } - - } - - // Check for Room Preset data (comes in partial, so we need to handle these responses differently to prevent appending duplicate items - var tempPresets = tempCodecStatus.Status.RoomPreset; - - if (tempPresets.Count > 0) - { - // Create temporary list to store the existing items from the CiscoCodecStatus.RoomPreset collection - List existingRoomPresets = new List(); - // Add the existing items to the temporary list - existingRoomPresets.AddRange(CodecStatus.Status.RoomPreset); - // Populate the CodecStatus object (this will append new values to the RoomPreset collection - JsonConvert.PopulateObject(response, CodecStatus); - - JObject jResponse = JObject.Parse(response); - - IList roomPresets = jResponse["Status"]["RoomPreset"].Children().ToList(); - // Iterate the new items in this response agains the temporary list. Overwrite any existing items and add new ones. - foreach (var preset in tempPresets) - { - // First fine the existing preset that matches the id - var existingPreset = existingRoomPresets.FirstOrDefault(p => p.id.Equals(preset.id)); - if (existingPreset != null) - { - Debug.Console(1, this, "Existing Room Preset with ID: {0} found. Updating.", existingPreset.id); - - JToken updatedPreset = null; - - // Find the JToken from the response with the matching id - foreach (var jPreset in roomPresets) - { - if (jPreset["id"].Value() == existingPreset.id) - updatedPreset = jPreset; - } - - if (updatedPreset != null) - { - // use PopulateObject to overlay the partial data onto the existing object - JsonConvert.PopulateObject(updatedPreset.ToString(), existingPreset); - } - - } - else - { - Debug.Console(1, this, "New Room Preset with ID: {0}. Adding.", preset.id); - existingRoomPresets.Add(preset); - } - } - - // Replace the list in the CodecStatus object with the processed list - CodecStatus.Status.RoomPreset = existingRoomPresets; - - // Generecise the list - NearEndPresets = RoomPresets.GetGenericPresets(CodecStatus.Status.RoomPreset); - - var handler = CodecRoomPresetsListHasChanged; - if (handler != null) - { - handler(this, new EventArgs()); - } - } - else - { - JsonConvert.PopulateObject(response, CodecStatus); - } - - if (!SyncState.InitialStatusMessageWasReceived) - { - SyncState.InitialStatusMessageReceived(); - - if (!SyncState.InitialConfigurationMessageWasReceived) - SendText("xConfiguration"); - } - } - else if (response.IndexOf("\"Configuration\":{") > -1 || response.IndexOf("\"Configuration\": {") > -1) - { - // Configuration Message - - JsonConvert.PopulateObject(response, CodecConfiguration); - - if (!SyncState.InitialConfigurationMessageWasReceived) - { - SyncState.InitialConfigurationMessageReceived(); - if (!SyncState.FeedbackWasRegistered) - { - SendText(CliFeedbackRegistrationExpression); - } - } - - } - else if (response.IndexOf("\"Event\":{") > -1 || response.IndexOf("\"Event\": {") > -1) - { - if (response.IndexOf("\"CallDisconnect\":{") > -1 || response.IndexOf("\"CallDisconnect\": {") > -1) - { - CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); - - JsonConvert.PopulateObject(response, eventReceived); - - EvalutateDisconnectEvent(eventReceived); - } - else if (response.IndexOf("\"Bookings\":{") > -1 || response.IndexOf("\"Bookings\": {") > -1) // The list has changed, reload it - { - 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 && !_externalSourceChangeRequested) - { - RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null); - } - - _externalSourceChangeRequested = false; - } - } - else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1) - { - // CommandResponse Message - - if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1 || response.IndexOf("\"CallHistoryRecentsResult\": {") > -1) - { - var codecCallHistory = new CiscoCallHistory.RootObject(); - - JsonConvert.PopulateObject(response, codecCallHistory); - - CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry); - } - else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1 || response.IndexOf("\"CallHistoryDeleteEntryResult\": {") > -1) - { - GetCallHistory(); - } - else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1 || response.IndexOf("\"PhonebookSearchResult\": {") > -1) - { - var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject(); - - JsonConvert.PopulateObject(response, codecPhonebookResponse); - - if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived) - { - // Check if the phonebook has any folders - PhonebookSyncState.InitialPhonebookFoldersReceived(); - - PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0); - - if (PhonebookSyncState.PhonebookHasFolders) - { - DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); - } - - // Get the number of contacts in the phonebook - GetPhonebookContacts(); - } - else if (!PhonebookSyncState.NumberOfContactsWasReceived) - { - // Store the total number of contacts in the phonebook - PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value)); - - DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); - - PhonebookSyncState.PhonebookRootEntriesReceived(); - - PrintDirectory(DirectoryRoot); - } - else if (PhonebookSyncState.InitialSyncComplete) - { - var directoryResults = new CodecDirectory(); - - if(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value != "0") - directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult); - - PrintDirectory(directoryResults); - - DirectoryBrowseHistory.Add(directoryResults); - - OnDirectoryResultReturned(directoryResults); - - } - } - else if (response.IndexOf("\"BookingsListResult\":{") > -1) - { - var codecBookings = new CiscoCodecBookings.RootObject(); - - JsonConvert.PopulateObject(response, codecBookings); - - if(codecBookings.CommandResponse.BookingsListResult.ResultInfo.TotalRows.Value != "0") - CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking); - - BookingsRefreshTimer.Reset(900000, 900000); - } - - } - - } - catch (Exception ex) - { - Debug.Console(1, this, "Error Deserializing feedback from codec: {0}", ex); - } - } - - /// - /// Call when directory results are updated - /// - /// - void OnDirectoryResultReturned(CodecDirectory result) - { - CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); - - // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology - var handler = DirectoryResultReturned; - if (handler != null) - { - handler(this, new DirectoryEventArgs() - { - Directory = result, - DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue - }); - } - - PrintDirectory(result); - } - - /// - /// Evaluates an event received from the codec - /// - /// - void EvalutateDisconnectEvent(CiscoCodecEvents.RootObject eventReceived) - { - if (eventReceived.Event.CallDisconnect != null) - { - var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value)); - - // Remove the call from the Active calls list - if (tempActiveCall != null) - { - ActiveCalls.Remove(tempActiveCall); - - ListCalls(); - - SetSelfViewMode(); - // Notify of the call disconnection - SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall); - - GetCallHistory(); - } - } - } - - /// - /// - /// - /// - public override void ExecuteSwitch(object selector) - { - (selector as Action)(); - PresentationSourceKey = selector.ToString(); - } - - /// - /// This is necessary for devices that are "routers" in the middle of the path, even though it only has one output and - /// may only have one input. - /// - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - ExecuteSwitch(inputSelector); - PresentationSourceKey = inputSelector.ToString(); - } - - - /// - /// Gets the ID of the last connected call - /// - /// - public string GetCallId() - { - string callId = null; - - if (ActiveCalls.Count > 1) - { - var lastCallIndex = ActiveCalls.Count - 1; - callId = ActiveCalls[lastCallIndex].Id; - } - else if (ActiveCalls.Count == 1) - callId = ActiveCalls[0].Id; - - return callId; - - } - - public void GetCallHistory() - { - SendText("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime"); - } - - /// - /// Required for IHasScheduleAwareness - /// - public void GetSchedule() - { - GetBookings(null); - } - - /// - /// Gets the bookings for today - /// - /// - public void GetBookings(object command) - { - Debug.Console(1, this, "Retrieving Booking Info from Codec. Current Time: {0}", DateTime.Now.ToLocalTime()); - - SendText("xCommand Bookings List Days: 1 DayOffset: 0"); - } - - /// - /// Checks to see if it is 2am (or within that hour) and triggers a download of the phonebook - /// - /// - public void CheckCurrentHour(object o) - { - if (DateTime.Now.Hour == 2) - { - Debug.Console(1, this, "Checking hour to see if phonebook should be downloaded. Current hour is {0}", DateTime.Now.Hour); - - GetPhonebook(null); - PhonebookRefreshTimer.Reset(3600000, 3600000); - } - } - - /// - /// Triggers a refresh of the codec phonebook - /// - /// Just to allow this method to be called from a console command - public void GetPhonebook(string command) - { - PhonebookSyncState.CodecDisconnected(); - - DirectoryRoot = new CodecDirectory(); - - GetPhonebookFolders(); - } - - private void GetPhonebookFolders() - { - // Get Phonebook Folders (determine local/corporate from config, and set results limit) - SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode)); - } - - private void GetPhonebookContacts() - { - // Get Phonebook Folders (determine local/corporate from config, and set results limit) - SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", PhonebookMode, PhonebookResultsLimit)); - } - - /// - /// Searches the codec phonebook for all contacts matching the search string - /// - /// - public void SearchDirectory(string searchString) - { - SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit)); - } - - /// - /// // Get contents of a specific folder in the phonebook - /// - /// - public void GetDirectoryFolderContents(string folderId) - { - SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit)); - } - - /// - /// Sets the parent folder contents or the directory root as teh current directory and fires the event. Used to browse up a level - /// - /// - public void GetDirectoryParentFolderContents() - { - var currentDirectory = new CodecDirectory(); - - if (DirectoryBrowseHistory.Count > 0) - { - var lastItemIndex = DirectoryBrowseHistory.Count - 1; - var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex]; - - DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]); - - currentDirectory = parentDirectoryContents; - - } - else - { - currentDirectory = DirectoryRoot; - } - - OnDirectoryResultReturned(currentDirectory); - } - - /// - /// Clears the session browse history and fires the event with the directory root - /// - public void SetCurrentDirectoryToRoot() - { - DirectoryBrowseHistory.Clear(); - - OnDirectoryResultReturned(DirectoryRoot); - } - - /// - /// Prints the directory to console - /// - /// - void PrintDirectory(CodecDirectory directory) - { - if (Debug.Level > 0) - { - Debug.Console(1, this, "Directory Results:\n"); - - foreach (DirectoryItem item in directory.CurrentDirectoryResults) - { - if (item is DirectoryFolder) - { - Debug.Console(1, this, "[+] {0}", item.Name); - } - else if (item is DirectoryContact) - { - Debug.Console(1, this, "{0}", item.Name); - } - } - Debug.Console(1, this, "Directory is on Root Level: {0}", !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); - } - - } - - /// - /// Simple dial method - /// - /// - public override void Dial(string number) - { - SendText(string.Format("xCommand Dial Number: \"{0}\"", number)); - } - - /// - /// Dials a specific meeting - /// - /// - public override void Dial(Meeting meeting) - { - foreach (Call c in meeting.Calls) - { - Dial(c.Number, c.Protocol, c.CallRate, c.CallType, meeting.Id); - } - } - - /// - /// Detailed dial method - /// - /// - /// - /// - /// - /// - public void Dial(string number, string protocol, string callRate, string callType, string meetingId) - { - SendText(string.Format("xCommand Dial Number: \"{0}\" Protocol: {1} CallRate: {2} CallType: {3} BookingId: {4}", number, protocol, callRate, callType, meetingId)); - } - - public override void EndCall(CodecActiveCallItem activeCall) - { - SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); - } - - public override void EndAllCalls() - { - foreach (CodecActiveCallItem activeCall in ActiveCalls) - { - SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); - } - } - - public override void AcceptCall(CodecActiveCallItem item) - { - SendText("xCommand Call Accept"); - } - - public override void RejectCall(CodecActiveCallItem item) - { - SendText("xCommand Call Reject"); - } - - public override void SendDtmf(string s) - { - SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); - } - - public void SelectPresentationSource(int source) - { - PresentationSource = source; - - StartSharing(); - } - - /// - /// Select source 1 as the presetnation source - /// - public void SelectPresentationSource1() - { - SelectPresentationSource(2); - } - - /// - /// Select source 2 as the presetnation source - /// - public void SelectPresentationSource2() - { - SelectPresentationSource(3); - } - - /// - /// Starts presentation sharing - /// - public override void StartSharing() - { - string sendingMode = string.Empty; - - if (IsInCall) - sendingMode = "LocalRemote"; - else - sendingMode = "LocalOnly"; - - if(PresentationSource > 0) - SendText(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", PresentationSource, sendingMode)); - } - - /// - /// Stops sharing the current presentation - /// - public override void StopSharing() - { - PresentationSource = 0; - - SendText("xCommand Presentation Stop"); - } - - public override void PrivacyModeOn() - { - SendText("xCommand Audio Microphones Mute"); - } - - public override void PrivacyModeOff() - { - SendText("xCommand Audio Microphones Unmute"); - } - - public override void PrivacyModeToggle() - { - SendText("xCommand Audio Microphones ToggleMute"); - } - - public override void MuteOff() - { - SendText("xCommand Audio Volume Unmute"); - } - - public override void MuteOn() - { - SendText("xCommand Audio Volume Mute"); - } - - public override void MuteToggle() - { - SendText("xCommand Audio Volume ToggleMute"); - } - - /// - /// Increments the voluem - /// - /// - public override void VolumeUp(bool pressRelease) - { - SendText("xCommand Audio Volume Increase"); - } - - /// - /// Decrements the volume - /// - /// - public override void VolumeDown(bool pressRelease) - { - SendText("xCommand Audio Volume Decrease"); - } - - /// - /// Scales the level and sets the codec to the specified level within its range - /// - /// level from slider (0-65535 range) - public override void SetVolume(ushort level) - { - var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); - SendText(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel)); - } - - /// - /// Recalls the default volume on the codec - /// - public void VolumeSetToDefault() - { - SendText("xCommand Audio Volume SetToDefault"); - } - - /// - /// Puts the codec in standby mode - /// - public override void StandbyActivate() - { - SendText("xCommand Standby Activate"); - } - - /// - /// Wakes the codec from standby - /// - public override void StandbyDeactivate() - { - SendText("xCommand Standby Deactivate"); - } - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); - } - - /// - /// Reboots the codec - /// - public void Reboot() - { - SendText("xCommand SystemUnit Boot Action: Restart"); - } - - /// - /// Sets SelfView Mode based on config - /// - void SetSelfViewMode() - { - if (!IsInCall) - { - SelfViewModeOff(); - } - else - { - if (ShowSelfViewByDefault) - SelfViewModeOn(); - else - SelfViewModeOff(); - } - } - - /// - /// Turns on Selfview Mode - /// - public void SelfViewModeOn() - { - SendText("xCommand Video Selfview Set Mode: On"); - } - - /// - /// Turns off Selfview Mode - /// - public void SelfViewModeOff() - { - SendText("xCommand Video Selfview Set Mode: Off"); - } - - /// - /// Toggles Selfview mode on/off - /// - public void SelfViewModeToggle() - { - string mode = string.Empty; - - if (CodecStatus.Status.Video.Selfview.Mode.BoolValue) - mode = "Off"; - else - mode = "On"; - - SendText(string.Format("xCommand Video Selfview Set Mode: {0}", mode)); - } - - /// - /// Sets a specified position for the selfview PIP window - /// - /// - public void SelfviewPipPositionSet(CodecCommandWithLabel position) - { - SendText(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command)); - } - - /// - /// Toggles to the next selfview PIP position - /// - public void SelfviewPipPositionToggle() - { - if (CurrentSelfviewPipPosition != null) - { - var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1; - - if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list - nextPipPositionIndex = 0; - - SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); - } - } - - /// - /// Sets a specific local layout - /// - /// - public void LocalLayoutSet(CodecCommandWithLabel layout) - { - SendText(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command)); - } - - /// - /// Toggles to the next local layout - /// - public void LocalLayoutToggle() - { - if(CurrentLocalLayout != null) - { - var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1; - - if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list - nextLocalLayoutIndex = 0; - - LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]); - } - } - - /// - /// Toggles between single/prominent layouts - /// - public void LocalLayoutToggleSingleProminent() - { - if (CurrentLocalLayout != null) - { - if (CurrentLocalLayout.Label != "Prominent") - LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent"))); - else - LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single"))); - } - - } - - /// - /// - /// - public void MinMaxLayoutToggle() - { - if (PresentationViewMaximizedFeedback.BoolValue) - CurrentPresentationView = "Minimized"; - else - CurrentPresentationView = "Maximized"; - - SendText(string.Format("xCommand Video PresentationView Set View: {0}", CurrentPresentationView)); - PresentationViewMaximizedFeedback.FireUpdate(); - } - - /// - /// Calculates the current selfview PIP position - /// - void ComputeSelfviewPipStatus() - { - CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower())); - - if(CurrentSelfviewPipPosition != null) - SelfviewIsOnFeedback.FireUpdate(); - } - - /// - /// Calculates the current local Layout - /// - void ComputeLocalLayout() - { - CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower())); - - if (CurrentLocalLayout != null) - LocalLayoutFeedback.FireUpdate(); - } - - public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry) - { - SendText(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId)); - } - - #region IHasCameraSpeakerTrack - - public void CameraAutoModeToggle() - { - if (!CameraAutoModeIsOnFeedback.BoolValue) - { - SendText("xCommand Cameras SpeakerTrack Activate"); - } - else - { - SendText("xCommand Cameras SpeakerTrack Deactivate"); - } - } - - public void CameraAutoModeOn() - { - if (CameraIsOffFeedback.BoolValue) - { - CameraMuteOff(); - } - - SendText("xCommand Cameras SpeakerTrack Activate"); - } - - public void CameraAutoModeOff() - { - if (CameraIsOffFeedback.BoolValue) - { - CameraMuteOff(); - } - - SendText("xCommand Cameras SpeakerTrack Deactivate"); - } - - #endregion - - /// - /// Builds the cameras List. Could later be modified to build from config data - /// - void SetUpCameras() - { - // Add the internal camera - Cameras = new List(); - - var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1); - - if(CodecStatus.Status.Cameras.Camera.Count > 0) - internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value); - else - // Somehow subscribe to the event on the Options.Value property and update when it changes. - - Cameras.Add(internalCamera); - - // Add the far end camera - var farEndCamera = new CiscoFarEndCamera(Key + "-cameraFar", "Far End", this); - Cameras.Add(farEndCamera); - - SelectedCameraFeedback = new StringFeedback(() => SelectedCamera.Key); - - ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); - - DeviceManager.AddDevice(internalCamera); - DeviceManager.AddDevice(farEndCamera); - - NearEndPresets = new List(15); - - FarEndRoomPresets = new List(15); - - // Add the far end presets - for (int i = 1; i <= FarEndRoomPresets.Capacity; i++) - { - var label = string.Format("Far End Preset {0}", i); - FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false)); - } - - SelectedCamera = internalCamera; ; // call the method to select the camera and ensure the feedbacks get updated. - } - - #region IHasCodecCameras Members - - public event EventHandler CameraSelected; - - public List Cameras { get; private set; } - - public StringFeedback SelectedCameraFeedback { get; private set; } - - private CameraBase _selectedCamera; - - /// - /// Returns the selected camera - /// - public CameraBase SelectedCamera - { - get - { - return _selectedCamera; - } - private set - { - _selectedCamera = value; - SelectedCameraFeedback.FireUpdate(); - ControllingFarEndCameraFeedback.FireUpdate(); - if (CameraIsOffFeedback.BoolValue) - CameraMuteOff(); - - var handler = CameraSelected; - if (handler != null) - { - handler(this, new CameraSelectedEventArgs(SelectedCamera)); - } - } - } - - public void SelectCamera(string key) - { - var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); - if (camera != null) - { - Debug.Console(2, this, "Selected Camera with key: '{0}'", camera.Key); - SelectedCamera = camera; - } - else - Debug.Console(2, this, "Unable to select camera with key: '{0}'", key); - } - - public CameraBase FarEndCamera { get; private set; } - - public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } - - #endregion - - /// - /// - /// - public class CiscoCodecInfo : VideoCodecInfo - { - public CiscoCodecStatus.RootObject CodecStatus { get; private set; } - - public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; } - - public override bool MultiSiteOptionIsEnabled - { - get - { - if (!string.IsNullOrEmpty(CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value) && CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true") - return true; - else - return false; - } - - } - public override string IpAddress - { - get - { - if (CodecConfiguration.Configuration.Network != null) - { - if (CodecConfiguration.Configuration.Network.Count > 0) - return CodecConfiguration.Configuration.Network[0].IPv4.Address.Value; - } - return string.Empty; - } - } - public override string E164Alias - { - get - { - if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias.E164 != null) - { - return CodecConfiguration.Configuration.H323.H323Alias.E164.Value; - } - else - { - return string.Empty; - } - } - } - public override string H323Id - { - get - { - if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias != null - && CodecConfiguration.Configuration.H323.H323Alias.ID != null) - { - return CodecConfiguration.Configuration.H323.H323Alias.ID.Value; - } - else - { - return string.Empty; - } - } - } - public override string SipPhoneNumber - { - get - { - if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.Registration.Count > 0) - { - var match = Regex.Match(CodecStatus.Status.SIP.Registration[0].URI.Value, @"(\d+)"); // extract numbers only - if (match.Success) - { - Debug.Console(1, "Extracted phone number as '{0}' from string '{1}'", match.Groups[1].Value, CodecStatus.Status.SIP.Registration[0].URI.Value); - return match.Groups[1].Value; - } - else - { - Debug.Console(1, "Unable to extract phone number from string: '{0}'", CodecStatus.Status.SIP.Registration[0].URI.Value); - return string.Empty; - } - } - else - { - Debug.Console(1, "Unable to extract phone number. No SIP Registration items found"); - return string.Empty; - } - } - } - - public override string SipUri - { - get - { - if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value != null) - { - return CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value; - } - else if (CodecStatus.Status.UserInterface != null && - CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value != null) - { - return CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value; - } - else - return string.Empty; - } - } - - public override bool AutoAnswerEnabled - { - get - { - if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on") - return true; - else - return false; - } - } - - public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration) - { - CodecStatus = status; - CodecConfiguration = configuration; - } - } - - - #region IHasCameraPresets Members - - public event EventHandler CodecRoomPresetsListHasChanged; - - public List NearEndPresets { get; private set; } - - public List FarEndRoomPresets { get; private set; } - - public void CodecRoomPresetSelect(int preset) - { - Debug.Console(1, this, "Selecting Preset: {0}", preset); - if (SelectedCamera is IAmFarEndCamera) - SelectFarEndPreset(preset); - else - SendText(string.Format("xCommand RoomPreset Activate PresetId: {0}", preset)); - } - - public void CodecRoomPresetStore(int preset, string description) - { - SendText(string.Format("xCommand RoomPreset Store PresetId: {0} Description: \"{1}\" Type: All", preset, description)); - } - - #endregion - - public void SelectFarEndPreset(int preset) - { - SendText(string.Format("xCommand Call FarEndControl RoomPreset Activate CallId: {0} PresetId: {1}", GetCallId(), preset)); - } - - - #region IHasExternalSourceSwitching Members - - /// - /// Wheather the Cisco supports External Source Lists or not - /// - public bool ExternalSourceListEnabled - { - get; - private set; - } - - /// - /// The name of the RoutingInputPort to which the upstream external switcher is connected - /// - public string ExternalSourceInputPort { get; private set; } - - public bool BrandingEnabled { get; private set; } - private string _brandingUrl; - private bool _sendMcUrl; - - /// - /// 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"); - - } - - /// - /// Sets the selected source of the available external sources on teh Touch10 UI - /// - public void SetSelectedSource(string key) - { - SendText(string.Format("xCommand UserInterface Presentation ExternalSource Select SourceIdentifier: {0}", key)); - _externalSourceChangeRequested = true; - } - - /// - /// Action that will run when the External Source is selected. - /// - public Action RunRouteAction { private get; set; } - - - - - - - #endregion - #region ExternalDevices - - - - #endregion - - #region IHasCameraOff Members - - public BoolFeedback CameraIsOffFeedback { get; private set; } - - public void CameraOff() - { - CameraMuteOn(); - } - - #endregion - - public BoolFeedback CameraIsMutedFeedback { get; private set; } - - /// - /// Mutes the outgoing camera video - /// - public void CameraMuteOn() - { - SendText("xCommand Video Input MainVideo Mute"); - } - - /// - /// Unmutes the outgoing camera video - /// - public void CameraMuteOff() - { - SendText("xCommand Video Input MainVideo Unmute"); - } - - /// - /// Toggles the camera mute state - /// - public void CameraMuteToggle() - { - if (CameraIsMutedFeedback.BoolValue) - CameraMuteOff(); - else - CameraMuteOn(); - } - } - - - /// - /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes - /// - public class CodecCommandWithLabel - { - public string Command { get; set; } - public string Label { get; set; } - - public CodecCommandWithLabel(string command, string label) - { - Command = command; - Label = label; - } - } - - /// - /// Tracks the initial sycnronization state of the codec when making a connection - /// - public class CodecSyncState : IKeyed - { - bool _InitialSyncComplete; - - public event EventHandler InitialSyncCompleted; - - public string Key { get; private set; } - - public bool InitialSyncComplete - { - get { return _InitialSyncComplete; } - private set - { - if (value == true) - { - var handler = InitialSyncCompleted; - if (handler != null) - handler(this, new EventArgs()); - } - _InitialSyncComplete = value; - } - } - - public bool LoginMessageWasReceived { get; private set; } - - public bool InitialStatusMessageWasReceived { get; private set; } - - public bool InitialConfigurationMessageWasReceived { get; private set; } - - public bool FeedbackWasRegistered { get; private set; } - - public CodecSyncState(string key) - { - Key = key; - CodecDisconnected(); - } - - public void LoginMessageReceived() - { - LoginMessageWasReceived = true; - Debug.Console(1, this, "Login Message Received."); - CheckSyncStatus(); - } - - public void InitialStatusMessageReceived() - { - InitialStatusMessageWasReceived = true; - Debug.Console(1, this, "Initial Codec Status Message Received."); - CheckSyncStatus(); - } - - public void InitialConfigurationMessageReceived() - { - InitialConfigurationMessageWasReceived = true; - Debug.Console(1, this, "Initial Codec Configuration Message Received."); - CheckSyncStatus(); - } - - public void FeedbackRegistered() - { - FeedbackWasRegistered = true; - Debug.Console(1, this, "Initial Codec Feedback Registration Successful."); - CheckSyncStatus(); - } - - public void CodecDisconnected() - { - LoginMessageWasReceived = false; - InitialConfigurationMessageWasReceived = false; - InitialStatusMessageWasReceived = false; - FeedbackWasRegistered = false; - InitialSyncComplete = false; - } - - void CheckSyncStatus() - { - if (LoginMessageWasReceived && InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered) - { - InitialSyncComplete = true; - Debug.Console(1, this, "Initial Codec Sync Complete!"); - } - else - InitialSyncComplete = false; - } - } - - public class CiscoSparkCodecFactory : EssentialsDeviceFactory - { - public CiscoSparkCodecFactory() - { - TypeNames = new List() { "ciscospark", "ciscowebex", "ciscowebexpro", "ciscoroomkit", "ciscosparkpluscodec" }; - } - - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - Debug.Console(1, "Factory Attempting to create new Cisco Codec Device"); - - var comm = CommFactory.CreateCommForDevice(dc); - return new VideoCodec.Cisco.CiscoSparkCodec(dc, comm); - } - } - +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.CrestronThread; +using Crestron.SimplSharpPro.DeviceSupport; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Core.Queues; + +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, + IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute, IHasDoNotDisturbMode, + IHasHalfWakeMode, IHasCallHold, IJoinCalls + { + private CiscoSparkCodecPropertiesConfig _config; + + private bool _externalSourceChangeRequested; + + public event EventHandler DirectoryResultReturned; + + private CTimer _brandingTimer; + + public CommunicationGather PortGather { get; private set; } + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + private GenericQueue _receiveQueue; + + public BoolFeedback PresentationViewMaximizedFeedback { get; private set; } + + private string _currentPresentationView; + + public BoolFeedback RoomIsOccupiedFeedback { get; private set; } + + public IntFeedback PeopleCountFeedback { get; private set; } + + public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; } + + public BoolFeedback SelfviewIsOnFeedback { get; private set; } + + public StringFeedback SelfviewPipPositionFeedback { get; private set; } + + public StringFeedback LocalLayoutFeedback { get; private set; } + + public BoolFeedback LocalLayoutIsProminentFeedback { get; private set; } + + public BoolFeedback FarEndIsSharingContentFeedback { get; private set; } + + public IntFeedback RingtoneVolumeFeedback { get; private set; } + + private CodecCommandWithLabel _currentSelfviewPipPosition; + + private CodecCommandWithLabel _currentLocalLayout; + + /// + /// List the available positions for the selfview PIP window + /// + public List SelfviewPipPositions = new List() + { + new CodecCommandWithLabel("CenterLeft", "Center Left"), + new CodecCommandWithLabel("CenterRight", "Center Right"), + new CodecCommandWithLabel("LowerLeft", "Lower Left"), + new CodecCommandWithLabel("LowerRight", "Lower Right"), + new CodecCommandWithLabel("UpperCenter", "Upper Center"), + new CodecCommandWithLabel("UpperLeft", "Upper Left"), + new CodecCommandWithLabel("UpperRight", "Upper Right"), + }; + + /// + /// Lists the available options for local layout + /// + public List LocalLayouts = new List() + { + //new CodecCommandWithLabel("auto", "Auto"), + //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now + new CodecCommandWithLabel("equal","Equal"), + new CodecCommandWithLabel("overlay","Overlay"), + new CodecCommandWithLabel("prominent","Prominent"), + new CodecCommandWithLabel("single","Single") + }; + + private CiscoCodecConfiguration.RootObject CodecConfiguration = new CiscoCodecConfiguration.RootObject(); + + private CiscoCodecStatus.RootObject CodecStatus = new CiscoCodecStatus.RootObject(); + + public CodecCallHistory CallHistory { get; private set; } + + public CodecCallFavorites CallFavorites { get; private set; } + + /// + /// The root level of the directory + /// + public CodecDirectory DirectoryRoot { get; private set; } + + /// + /// Represents the current state of the directory and is computed on get + /// + public CodecDirectory CurrentDirectoryResult + { + get + { + if (DirectoryBrowseHistory.Count > 0) + return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1]; + else + return DirectoryRoot; + } + } + + public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } + + /// + /// Tracks the directory browse history when browsing beyond the root directory + /// + public List DirectoryBrowseHistory { get; private set; } + + public CodecScheduleAwareness CodecSchedule { get; private set; } + + /// + /// Gets and returns the scaled volume of the codec + /// + protected override Func VolumeLevelFeedbackFunc + { + get + { + return () => CrestronEnvironment.ScaleWithLimits(CodecStatus.Status.Audio.Volume.IntValue, 100, 0, 65535, 0); + } + } + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Audio.Microphones.Mute.BoolValue; + } + } + + protected override Func StandbyIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Standby.State.BoolValue; + } + } + + /// + /// Gets the value of the currently shared source, or returns null + /// + protected override Func SharingSourceFeedbackFunc + { + get + { + return () => _presentationSourceKey; + } + } + + protected override Func SharingContentIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Conference.Presentation.Mode.BoolValue; + } + } + + protected Func FarEndIsSharingContentFeedbackFunc + { + get + { + return () => CodecStatus.Status.Conference.Presentation.Mode.Value == "Receiving"; + } + } + + protected override Func MuteFeedbackFunc + { + get + { + return () => CodecStatus.Status.Audio.VolumeMute.BoolValue; + } + } + + protected Func RoomIsOccupiedFeedbackFunc + { + get + { + return () => CodecStatus.Status.RoomAnalytics.PeoplePresence.BoolValue; + } + } + + protected Func PeopleCountFeedbackFunc + { + get + { + return () => CodecStatus.Status.RoomAnalytics.PeopleCount.Current.IntValue; + } + } + + protected Func SpeakerTrackIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Cameras.SpeakerTrack.Status.BoolValue; + } + } + + protected Func SelfViewIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue; + } + } + + protected Func SelfviewPipPositionFeedbackFunc + { + get + { + return () => _currentSelfviewPipPosition.Label; + } + } + + protected Func LocalLayoutFeedbackFunc + { + get + { + return () => _currentLocalLayout.Label; + } + } + + protected Func LocalLayoutIsProminentFeedbackFunc + { + get + { + return () => _currentLocalLayout.Label == "Prominent"; + } + } + + + private string _cliFeedbackRegistrationExpression; + + private CodecSyncState _syncState; + + public CodecPhonebookSyncState PhonebookSyncState { get; private set; } + + private StringBuilder _jsonMessage; + + private bool _jsonFeedbackMessageIsIncoming; + + public bool CommDebuggingIsOn; + + string Delimiter = "\r\n"; + + public IntFeedback PresentationSourceFeedback { get; private set; } + + public BoolFeedback PresentationSendingLocalOnlyFeedback { get; private set; } + + public BoolFeedback PresentationSendingLocalRemoteFeedback { get; private set; } + + /// + /// Used to track the current connector used for the presentation source + /// + private int _presentationSource; + + /// + /// Used to track the connector that is desired to be the current presentation source (until the command is send) + /// + private int _desiredPresentationSource; + + private string _presentationSourceKey; + + private bool _presentationLocalOnly; + + private bool _presentationLocalRemote; + + private string _phonebookMode = "Local"; // Default to Local + + private uint _phonebookResultsLimit = 255; // Could be set later by config. + + private CTimer _loginMessageReceivedTimer; + private CTimer _retryConnectionTimer; + + // **___________________________________________________________________** + // Timers to be moved to the global system timer at a later point.... + private CTimer BookingsRefreshTimer; + private CTimer PhonebookRefreshTimer; + // **___________________________________________________________________** + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingInputPort HdmiIn2 { get; private set; } + public RoutingInputPort HdmiIn3 { get; private set; } + public RoutingOutputPort HdmiOut1 { get; private set; } + public RoutingOutputPort HdmiOut2 { get; private set; } + + + // Constructor for IBasicCommunication + public CiscoSparkCodec(DeviceConfig config, IBasicCommunication comm) + : base(config) + { + var props = JsonConvert.DeserializeObject(config.Properties.ToString()); + + _config = props; + + // Use the configured phonebook results limit if present + if (props.PhonebookResultsLimit > 0) + { + _phonebookResultsLimit = props.PhonebookResultsLimit; + } + + // The queue that will collect the repsonses in the order they are received + _receiveQueue = new GenericQueue(this.Key + "-rxQueue", 25); + + RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); + PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc); + CameraAutoModeIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc); + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); + LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc); + LocalLayoutIsProminentFeedback = new BoolFeedback(LocalLayoutIsProminentFeedbackFunc); + FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); + CameraIsOffFeedback = new BoolFeedback(() => CodecStatus.Status.Video.Input.MainVideoMute.BoolValue); + CameraIsMutedFeedback = CameraIsOffFeedback; + SupportsCameraOff = true; + + DoNotDisturbModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Conference.DoNotDisturb.BoolValue); + HalfWakeModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value.ToLower() == "halfwake"); + EnteringStandbyModeFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value.ToLower() == "enteringstandby"); + + PresentationViewMaximizedFeedback = new BoolFeedback(() => _currentPresentationView == "Maximized"); + + RingtoneVolumeFeedback = new IntFeedback(() => CodecConfiguration.Configuration.Audio.SoundsAndAlerts.RingVolume.Volume); + + PresentationSourceFeedback = new IntFeedback(() => _presentationSource); + PresentationSendingLocalOnlyFeedback = new BoolFeedback(() => _presentationLocalOnly); + PresentationSendingLocalRemoteFeedback = new BoolFeedback(() => _presentationLocalRemote); + + Communication = comm; + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + var command = string.Format("xCommand Peripherals HeartBeat ID: {0}{1}", CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, Delimiter); + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, command); + } + + if (props.Sharing != null) + AutoShareContentWhileInCall = props.Sharing.AutoShareContentWhileInCall; + + ShowSelfViewByDefault = props.ShowSelfViewByDefault; + + DeviceManager.AddDevice(CommunicationMonitor); + + _phonebookMode = props.PhonebookMode; + + _syncState = new CodecSyncState(Key + "--Sync", this); + + PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); + + _syncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted); + + PortGather = new CommunicationGather(Communication, Delimiter); + PortGather.IncludeDelimiter = true; + PortGather.LineReceived += this.Port_LineReceived; + + CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration); + + CallHistory = new CodecCallHistory(); + + + if (props.Favorites != null) + { + CallFavorites = new CodecCallFavorites(); + CallFavorites.Favorites = props.Favorites; + } + + DirectoryRoot = new CodecDirectory(); + + DirectoryBrowseHistory = new List(); + + CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0); + + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + CodecSchedule = new CodecScheduleAwareness(); + + //Set Feedback Actions + SetFeedbackActions(); + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this); + HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this); + + HdmiOut1 = new RoutingOutputPort(RoutingPortNames.HdmiOut1, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + HdmiOut2 = new RoutingOutputPort(RoutingPortNames.HdmiOut2, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + + InputPorts.Add(CodecOsdIn); + InputPorts.Add(HdmiIn2); + InputPorts.Add(HdmiIn3); + OutputPorts.Add(HdmiOut1); + CreateOsdSource(); + + ExternalSourceListEnabled = props.ExternalSourceListEnabled; + ExternalSourceInputPort = props.ExternalSourceInputPort; + + if (props.UiBranding == null) + { + return; + } + Debug.Console(2, this, "Setting branding properties enable: {0} _brandingUrl {1}", props.UiBranding.Enable, + props.UiBranding.BrandingUrl); + + BrandingEnabled = props.UiBranding.Enable; + + _brandingUrl = props.UiBranding.BrandingUrl; + } + + private void SetFeedbackActions() + { + CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate; + CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate; + CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate; + CodecStatus.Status.Standby.State.ValueChangedAction = new Action(() => + { + StandbyIsOnFeedback.FireUpdate(); + HalfWakeModeIsOnFeedback.FireUpdate(); + EnteringStandbyModeFeedback.FireUpdate(); + }); + CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate; + CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate; + CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate; + CodecStatus.Status.Cameras.SpeakerTrack.Availability.ValueChangedAction = () => { SupportsCameraAutoMode = CodecStatus.Status.Cameras.SpeakerTrack.Availability.BoolValue; }; + CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus; + CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; + CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = () => + { + SharingContentIsOnFeedback.FireUpdate(); + FarEndIsSharingContentFeedback.FireUpdate(); + }; + CodecStatus.Status.Conference.DoNotDisturb.ValueChangedAction = DoNotDisturbModeIsOnFeedback.FireUpdate; + + CodecConfiguration.Configuration.Audio.SoundsAndAlerts.RingVolume.ValueChangedAction = RingtoneVolumeFeedback.FireUpdate; + + try + { + CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate; + } + catch (Exception ex) + { + Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex); + + if (ex.InnerException != null) + { + Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex); + } + } + } + + /// + /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input + /// to enable routing + /// + void CreateOsdSource() + { + OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); + DeviceManager.AddDevice(OsdSource); + var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); + TieLineCollection.Default.Add(tl); + } + + public void InitializeBranding(string roomKey) + { + Debug.Console(1, this, "Initializing Branding for room {0}", roomKey); + + if (!BrandingEnabled) + { + return; + } + + var mcBridgeKey = String.Format("mobileControlBridge-{0}", roomKey); + + var mcBridge = DeviceManager.GetDeviceForKey(mcBridgeKey) as IMobileControlRoomBridge; + + if (!String.IsNullOrEmpty(_brandingUrl)) + { + Debug.Console(1, this, "Branding URL found: {0}", _brandingUrl); + if (_brandingTimer != null) + { + _brandingTimer.Stop(); + _brandingTimer.Dispose(); + } + + _brandingTimer = new CTimer((o) => + { + if (_sendMcUrl) + { + SendMcBrandingUrl(mcBridge); + _sendMcUrl = false; + } + else + { + SendBrandingUrl(); + _sendMcUrl = true; + } + }, 0, 15000); + } else if (String.IsNullOrEmpty(_brandingUrl)) + { + Debug.Console(1, this, "No Branding URL found"); + if (mcBridge == null) return; + + Debug.Console(2, this, "Setting QR code URL: {0}", mcBridge.QrCodeUrl); + + mcBridge.UserCodeChanged += (o, a) => SendMcBrandingUrl(mcBridge); + mcBridge.UserPromptedForCode += (o, a) => DisplayUserCode(mcBridge.UserCode); + + SendMcBrandingUrl(mcBridge); + } + } + + /// + /// Displays the code for the specified duration + /// + /// Mobile Control user code + private void DisplayUserCode(string code) + { + EnqueueCommand(string.Format("xcommand userinterface message alert display title:\"Mobile Control User Code:\" text:\"{0}\" duration: 30", code)); + } + + private void SendMcBrandingUrl(IMobileControlRoomBridge mcBridge) + { + if (mcBridge == null) + { + return; + } + + Debug.Console(1, this, "Sending url: {0}", mcBridge.QrCodeUrl); + + EnqueueCommand("xconfiguration userinterface custommessage: \"Scan the QR code with a mobile phone to get started\""); + EnqueueCommand("xconfiguration userinterface osd halfwakemessage: \"Tap the touch panel or scan the QR code with a mobile phone to get started\""); + + var checksum = !String.IsNullOrEmpty(mcBridge.QrCodeChecksum) + ? String.Format("checksum: {0} ", mcBridge.QrCodeChecksum) + : String.Empty; + + EnqueueCommand(String.Format( + "xcommand userinterface branding fetch {1}type: branding url: {0}", + mcBridge.QrCodeUrl, checksum)); + EnqueueCommand(String.Format( + "xcommand userinterface branding fetch {1}type: halfwakebranding url: {0}", + mcBridge.QrCodeUrl, checksum)); + } + + private void SendBrandingUrl() + { + Debug.Console(1, this, "Sending url: {0}", _brandingUrl); + + EnqueueCommand(String.Format("xcommand userinterface branding fetch type: branding url: {0}", + _brandingUrl)); + EnqueueCommand(String.Format("xcommand userinterface branding fetch type: halfwakebranding url: {0}", + _brandingUrl)); + } + /// + /// Starts the HTTP feedback server and syncronizes state of codec + /// + /// + public override bool CustomActivate() + { + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + + PhonebookSyncState.InitialSyncCompleted += new EventHandler(PhonebookSyncState_InitialSyncCompleted); + + return base.CustomActivate(); + } + + void PhonebookSyncState_InitialSyncCompleted(object sender, EventArgs e) + { + OnDirectoryResultReturned(DirectoryRoot); + } + + #region Overrides of Device + + public override void Initialize() + { + var socket = Communication as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + + Communication.Connect(); + + CommunicationMonitor.Start(); + + const string prefix = "xFeedback register "; + + _cliFeedbackRegistrationExpression = + prefix + "/Configuration" + Delimiter + + prefix + "/Status/Audio" + Delimiter + + prefix + "/Status/Call" + Delimiter + + prefix + "/Status/Conference/Presentation" + Delimiter + + prefix + "/Status/Conference/DoNotDisturb" + Delimiter + + prefix + "/Status/Cameras/SpeakerTrack" + Delimiter + + prefix + "/Status/RoomAnalytics" + Delimiter + + prefix + "/Status/RoomPreset" + Delimiter + + prefix + "/Status/Standby" + Delimiter + + prefix + "/Status/Video/Selfview" + Delimiter + + prefix + "/Status/Video/Layout" + Delimiter + + prefix + "/Status/Video/Input/MainVideoMute" + Delimiter + + prefix + "/Bookings" + Delimiter + + prefix + "/Event/Bookings" + Delimiter + + prefix + "/Event/CameraPresetListUpdated" + Delimiter + + prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter + + prefix + "/Event/CallDisconnect" + Delimiter; // Keep CallDisconnect last to detect when feedback registration completes correctly + + } + + #endregion + + /// + /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc. + /// + /// + /// + void SyncState_InitialSyncCompleted(object sender, EventArgs e) + { + // Check for camera config info first + if (_config.CameraInfo.Count > 0) + { + Debug.Console(0, this, "Reading codec cameraInfo from config properties."); + SetUpCameras(_config.CameraInfo); + } + else + { + Debug.Console(0, this, "No cameraInfo defined in video codec config. Attempting to get camera info from codec status data"); + try + { + var cameraInfo = new List(); + + Debug.Console(0, this, "Codec reports {0} cameras", CodecStatus.Status.Cameras.Camera.Count); + + foreach (var camera in CodecStatus.Status.Cameras.Camera) + { + Debug.Console(0, this, +@"Camera id: {0} +Name: {1} +ConnectorID: {2}" +, camera.id +, camera.Manufacturer.Value +, camera.Model.Value); + + var id = Convert.ToUInt16(camera.id); + var info = new CameraInfo() { CameraNumber = id, Name = string.Format("{0} {1}", camera.Manufacturer.Value, camera.Model.Value), SourceId = camera.DetectedConnector.ConnectorId }; + cameraInfo.Add(info); + } + + Debug.Console(0, this, "Successfully got cameraInfo for {0} cameras from codec.", cameraInfo.Count); + + SetUpCameras(cameraInfo); + } + catch (Exception ex) + { + Debug.Console(2, this, "Error generating camera info from codec status data: {0}", ex); + } + } + + //CommDebuggingIsOn = false; + + GetCallHistory(); + + PhonebookRefreshTimer = new CTimer(CheckCurrentHour, 3600000, 3600000); // check each hour to see if the phonebook should be downloaded + GetPhonebook(null); + + BookingsRefreshTimer = new CTimer(GetBookings, 900000, 900000); // 15 minute timer to check for new booking info + GetBookings(null); + + // Fire the ready event + SetIsReady(); + } + + public void SetCommDebug(string s) + { + if (s == "1") + { + CommDebuggingIsOn = true; + Debug.Console(0, this, "Comm Debug Enabled."); + } + else + { + CommDebuggingIsOn = false; + Debug.Console(0, this, "Comm Debug Disabled."); + } + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); + if (e.Client.IsConnected) + { + if(!_syncState.LoginMessageWasReceived) + _loginMessageReceivedTimer = new CTimer(o => DisconnectClientAndReconnect(), 5000); + } + else + { + _syncState.CodecDisconnected(); + PhonebookSyncState.CodecDisconnected(); + + if (PhonebookRefreshTimer != null) + { + PhonebookRefreshTimer.Stop(); + PhonebookRefreshTimer = null; + } + + if (BookingsRefreshTimer != null) + { + BookingsRefreshTimer.Stop(); + BookingsRefreshTimer = null; + } + } + } + + void DisconnectClientAndReconnect() + { + Debug.Console(1, this, "Retrying connection to codec."); + + Communication.Disconnect(); + + _retryConnectionTimer = new CTimer(o => Communication.Connect(), 2000); + + //CrestronEnvironment.Sleep(2000); + + //Communication.Connect(); + } + + /// + /// Gathers responses from the codec (including the delimiter. Responses are checked to see if they contain JSON data and if so, the data is collected until a complete JSON + /// message is received before forwarding the message to be deserialized. + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + if (CommDebuggingIsOn) + { + if (!_jsonFeedbackMessageIsIncoming) + Debug.Console(1, this, "RX: '{0}'", ComTextHelper.GetDebugText(args.Text)); + } + + if(args.Text.ToLower().Contains("xcommand")) + { + Debug.Console(1, this, "Received command echo response. Ignoring"); + return; + } + + if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message + { + _jsonFeedbackMessageIsIncoming = true; + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Incoming JSON message..."); + + _jsonMessage = new StringBuilder(); + } + else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message + { + _jsonFeedbackMessageIsIncoming = false; + + _jsonMessage.Append(args.Text); + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Complete JSON Received:\n{0}", _jsonMessage.ToString()); + + // Enqueue the complete message to be deserialized + + _receiveQueue.Enqueue(new ProcessStringMessage(_jsonMessage.ToString(), DeserializeResponse)); + + return; + } + + if(_jsonFeedbackMessageIsIncoming) + { + _jsonMessage.Append(args.Text); + + //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); + return; + } + + if (!_syncState.InitialSyncComplete) + { + switch (args.Text.Trim().ToLower()) // remove the whitespace + { + case "*r login successful": + { + _syncState.LoginMessageReceived(); + + if(_loginMessageReceivedTimer != null) + _loginMessageReceivedTimer.Stop(); + + //SendText("echo off"); + SendText("xPreferences outputmode json"); + break; + } + case "xpreferences outputmode json": + { + if (_syncState.JsonResponseModeSet) + return; + + _syncState.JsonResponseModeMessageReceived(); + + if (!_syncState.InitialStatusMessageWasReceived) + SendText("xStatus"); + break; + } + case "xfeedback register /event/calldisconnect": + { + _syncState.FeedbackRegistered(); + break; + } + } + } + } + + /// + /// Enqueues a command to be sent to the codec. + /// + /// + public void EnqueueCommand(string command) + { + _syncState.AddCommandToQueue(command); + } + + /// + /// Appends the delimiter and send the command to the codec. + /// Should not be used for sending general commands to the codec. Use EnqueueCommand instead. + /// Should be used to get initial Status and Configuration as well as set up Feedback Registration + /// + /// + public void SendText(string command) + { + if (CommDebuggingIsOn) + Debug.Console(1, this, "Sending: '{0}'", ComTextHelper.GetDebugText(command + Delimiter)); + + Communication.SendText(command + Delimiter); + } + + void DeserializeResponse(string response) + { + try + { + //// Serializer settings. We want to ignore null values and missing members + //JsonSerializerSettings settings = new JsonSerializerSettings(); + //settings.NullValueHandling = NullValueHandling.Ignore; + //settings.MissingMemberHandling = MissingMemberHandling.Ignore; + //settings.ObjectCreationHandling = ObjectCreationHandling.Auto; + + if (response.IndexOf("\"Status\":{") > -1 || response.IndexOf("\"Status\": {") > -1) + { + // Status Message + + // Temp object so we can inpsect for call data before simply deserializing + CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject(); + + JsonConvert.PopulateObject(response, tempCodecStatus); + + // Check to see if the message contains /Status/Conference/Presentation/LocalInstance and extract source value + var conference = tempCodecStatus.Status.Conference; + + if (conference.Presentation != null && conference.Presentation.LocalInstance == null) + { + // Handles an empty presentation object response + return; + } + + if (conference.Presentation.LocalInstance.Count > 0) + { + if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost)) + { + _presentationSource = 0; + _presentationLocalOnly = false; + _presentationLocalRemote = false; + } + else if (conference.Presentation.LocalInstance[0].Source != null) + { + _presentationSource = conference.Presentation.LocalInstance[0].Source.IntValue; + + // Check for any values in the SendingMode property + if (conference.Presentation.LocalInstance.Any((i) => !string.IsNullOrEmpty(i.SendingMode.Value))) + { + _presentationLocalOnly = conference.Presentation.LocalInstance.Any((i) => i.SendingMode.LocalOnly); + _presentationLocalRemote = conference.Presentation.LocalInstance.Any((i) => i.SendingMode.LocalRemote); + } + } + + PresentationSourceFeedback.FireUpdate(); + PresentationSendingLocalOnlyFeedback.FireUpdate(); + PresentationSendingLocalRemoteFeedback.FireUpdate(); + } + + // Check to see if this is a call status message received after the initial status message + if (tempCodecStatus.Status.Call.Count > 0) + { + // Iterate through the call objects in the response + foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call) + { + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id)); + + if (tempActiveCall != null) + { + bool changeDetected = false; + + eCodecCallStatus newStatus = eCodecCallStatus.Unknown; + + // Update properties of ActiveCallItem + if(call.Status != null) + if (!string.IsNullOrEmpty(call.Status.Value)) + { + tempActiveCall.Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value); + tempActiveCall.IsOnHold = tempActiveCall.Status == eCodecCallStatus.OnHold; + + if (newStatus == eCodecCallStatus.Connected) + GetCallHistory(); + + changeDetected = true; + } + if (call.CallType != null) + if (!string.IsNullOrEmpty(call.CallType.Value)) + { + tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value); + changeDetected = true; + } + if (call.DisplayName != null) + if (!string.IsNullOrEmpty(call.DisplayName.Value)) + { + tempActiveCall.Name = call.DisplayName.Value; + changeDetected = true; + } + if (call.Direction != null) + { + if (!string.IsNullOrEmpty(call.Direction.Value)) + { + tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value); + changeDetected = true; + } + } + if(call.Duration != null) + { + if(!string.IsNullOrEmpty(call.Duration.Value)) + { + tempActiveCall.Duration = call.Duration.DurationValue; + changeDetected = true; + } + } + if(call.PlacedOnHold != null) + { + tempActiveCall.IsOnHold = call.PlacedOnHold.BoolValue; + changeDetected = true; + } + + if (changeDetected) + { + SetSelfViewMode(); + OnCallStatusChange(tempActiveCall); + ListCalls(); + } + } + else if( call.ghost == null ) // if the ghost value is present the call has ended already + { + // Create a new call item + var newCallItem = new CodecActiveCallItem() + { + Id = call.id, + Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value), + Name = call.DisplayName.Value, + Number = call.RemoteNumber.Value, + Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value), + Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value), + Duration = call.Duration.DurationValue, + IsOnHold = call.PlacedOnHold.BoolValue, + }; + + // Add it to the ActiveCalls List + ActiveCalls.Add(newCallItem); + + ListCalls(); + + SetSelfViewMode(); + OnCallStatusChange(newCallItem); + } + + } + + } + + // Check for Room Preset data (comes in partial, so we need to handle these responses differently to prevent appending duplicate items + var tempPresets = tempCodecStatus.Status.RoomPreset; + + if (tempPresets.Count > 0) + { + // Create temporary list to store the existing items from the CiscoCodecStatus.RoomPreset collection + List existingRoomPresets = new List(); + // Add the existing items to the temporary list + existingRoomPresets.AddRange(CodecStatus.Status.RoomPreset); + // Populate the CodecStatus object (this will append new values to the RoomPreset collection + JsonConvert.PopulateObject(response, CodecStatus); + + JObject jResponse = JObject.Parse(response); + + IList roomPresets = jResponse["Status"]["RoomPreset"].Children().ToList(); + // Iterate the new items in this response agains the temporary list. Overwrite any existing items and add new ones. + foreach (var preset in tempPresets) + { + // First fine the existing preset that matches the id + var existingPreset = existingRoomPresets.FirstOrDefault(p => p.id.Equals(preset.id)); + if (existingPreset != null) + { + Debug.Console(1, this, "Existing Room Preset with ID: {0} found. Updating.", existingPreset.id); + + JToken updatedPreset = null; + + // Find the JToken from the response with the matching id + foreach (var jPreset in roomPresets) + { + if (jPreset["id"].Value() == existingPreset.id) + updatedPreset = jPreset; + } + + if (updatedPreset != null) + { + // use PopulateObject to overlay the partial data onto the existing object + JsonConvert.PopulateObject(updatedPreset.ToString(), existingPreset); + } + + } + else + { + Debug.Console(1, this, "New Room Preset with ID: {0}. Adding.", preset.id); + existingRoomPresets.Add(preset); + } + } + + // Replace the list in the CodecStatus object with the processed list + CodecStatus.Status.RoomPreset = existingRoomPresets; + + // Generecise the list + NearEndPresets = RoomPresets.GetGenericPresets(CodecStatus.Status.RoomPreset); + + var handler = CodecRoomPresetsListHasChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + else + { + JsonConvert.PopulateObject(response, CodecStatus); + } + + if (!_syncState.InitialStatusMessageWasReceived) + { + _syncState.InitialStatusMessageReceived(); + + if (!_syncState.InitialConfigurationMessageWasReceived) + { + SendText("xConfiguration"); + } + } + } + else if (response.IndexOf("\"Configuration\":{") > -1 || response.IndexOf("\"Configuration\": {") > -1) + { + // Configuration Message + + JsonConvert.PopulateObject(response, CodecConfiguration); + + if (!_syncState.InitialConfigurationMessageWasReceived) + { + _syncState.InitialConfigurationMessageReceived(); + if (!_syncState.FeedbackWasRegistered) + { + SendText(_cliFeedbackRegistrationExpression); + } + } + + } + else if (response.IndexOf("\"Event\":{") > -1 || response.IndexOf("\"Event\": {") > -1) + { + if (response.IndexOf("\"CallDisconnect\":{") > -1 || response.IndexOf("\"CallDisconnect\": {") > -1) + { + CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); + + JsonConvert.PopulateObject(response, eventReceived); + + EvalutateDisconnectEvent(eventReceived); + } + else if (response.IndexOf("\"Bookings\":{") > -1 || response.IndexOf("\"Bookings\": {") > -1) // The list has changed, reload it + { + 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 && !_externalSourceChangeRequested) + { + RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null); + } + + _externalSourceChangeRequested = false; + } + } + else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1) + { + // CommandResponse Message + + if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1 || response.IndexOf("\"CallHistoryRecentsResult\": {") > -1) + { + var codecCallHistory = new CiscoCallHistory.RootObject(); + + JsonConvert.PopulateObject(response, codecCallHistory); + + CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry); + } + else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1 || response.IndexOf("\"CallHistoryDeleteEntryResult\": {") > -1) + { + GetCallHistory(); + } + else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1 || response.IndexOf("\"PhonebookSearchResult\": {") > -1) + { + var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject(); + + JsonConvert.PopulateObject(response, codecPhonebookResponse); + + if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived) + { + // Check if the phonebook has any folders + PhonebookSyncState.InitialPhonebookFoldersReceived(); + + PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0); + + if (PhonebookSyncState.PhonebookHasFolders) + { + DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + } + + // Get the number of contacts in the phonebook + GetPhonebookContacts(); + } + else if (!PhonebookSyncState.NumberOfContactsWasReceived) + { + // Store the total number of contacts in the phonebook + PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value)); + + DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + + PhonebookSyncState.PhonebookRootEntriesReceived(); + + PrintDirectory(DirectoryRoot); + } + else if (PhonebookSyncState.InitialSyncComplete) + { + var directoryResults = new CodecDirectory(); + + if(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value != "0") + directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult); + + PrintDirectory(directoryResults); + + DirectoryBrowseHistory.Add(directoryResults); + + OnDirectoryResultReturned(directoryResults); + + } + } + else if (response.IndexOf("\"BookingsListResult\":{") > -1) + { + var codecBookings = new CiscoCodecBookings.RootObject(); + + JsonConvert.PopulateObject(response, codecBookings); + + if(codecBookings.CommandResponse.BookingsListResult.ResultInfo.TotalRows.Value != "0") + CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking); + + BookingsRefreshTimer.Reset(900000, 900000); + } + + } + + } + catch (Exception ex) + { + Debug.Console(1, this, "Error Deserializing feedback from codec: {0}", ex); + + if (ex is Newtonsoft.Json.JsonReaderException) + { + Debug.Console(1, this, "Received malformed response from codec."); + + //Communication.Disconnect(); + + //Initialize(); + } + + } + } + + /// + /// Call when directory results are updated + /// + /// + void OnDirectoryResultReturned(CodecDirectory result) + { + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + { + Debug.Console(2, this, "Directory result returned"); + handler(this, new DirectoryEventArgs() + { + Directory = result, + DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue + }); + } + + PrintDirectory(result); + } + + /// + /// Evaluates an event received from the codec + /// + /// + void EvalutateDisconnectEvent(CiscoCodecEvents.RootObject eventReceived) + { + if (eventReceived.Event.CallDisconnect != null) + { + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value)); + + // Remove the call from the Active calls list + if (tempActiveCall != null) + { + ActiveCalls.Remove(tempActiveCall); + + ListCalls(); + + SetSelfViewMode(); + // Notify of the call disconnection + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall); + + GetCallHistory(); + } + } + } + + /// + /// + /// + /// + public override void ExecuteSwitch(object selector) + { + (selector as Action)(); + _presentationSourceKey = selector.ToString(); + } + + /// + /// This is necessary for devices that are "routers" in the middle of the path, even though it only has one output and + /// may only have one input. + /// + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + ExecuteSwitch(inputSelector); + _presentationSourceKey = inputSelector.ToString(); + } + + + /// + /// Gets the ID of the last connected call + /// + /// + public string GetCallId() + { + string callId = null; + + if (ActiveCalls.Count > 1) + { + var lastCallIndex = ActiveCalls.Count - 1; + callId = ActiveCalls[lastCallIndex].Id; + } + else if (ActiveCalls.Count == 1) + callId = ActiveCalls[0].Id; + + return callId; + + } + + public void GetCallHistory() + { + EnqueueCommand("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime"); + } + + /// + /// Required for IHasScheduleAwareness + /// + public void GetSchedule() + { + GetBookings(null); + } + + /// + /// Gets the bookings for today + /// + /// + public void GetBookings(object command) + { + Debug.Console(1, this, "Retrieving Booking Info from Codec. Current Time: {0}", DateTime.Now.ToLocalTime()); + + EnqueueCommand("xCommand Bookings List Days: 1 DayOffset: 0"); + } + + /// + /// Checks to see if it is 2am (or within that hour) and triggers a download of the phonebook + /// + /// + public void CheckCurrentHour(object o) + { + if (DateTime.Now.Hour == 2) + { + Debug.Console(1, this, "Checking hour to see if phonebook should be downloaded. Current hour is {0}", DateTime.Now.Hour); + + GetPhonebook(null); + PhonebookRefreshTimer.Reset(3600000, 3600000); + } + } + + /// + /// Triggers a refresh of the codec phonebook + /// + /// Just to allow this method to be called from a console command + public void GetPhonebook(string command) + { + PhonebookSyncState.CodecDisconnected(); + + DirectoryRoot = new CodecDirectory(); + + GetPhonebookFolders(); + } + + private void GetPhonebookFolders() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + EnqueueCommand(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", _phonebookMode)); + } + + private void GetPhonebookContacts() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + EnqueueCommand(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", _phonebookMode, _phonebookResultsLimit)); + } + + /// + /// Searches the codec phonebook for all contacts matching the search string + /// + /// + public void SearchDirectory(string searchString) + { + EnqueueCommand(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, _phonebookMode, _phonebookResultsLimit)); + } + + /// + /// // Get contents of a specific folder in the phonebook + /// + /// + public void GetDirectoryFolderContents(string folderId) + { + EnqueueCommand(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, _phonebookMode, _phonebookResultsLimit)); + } + + /// + /// Sets the parent folder contents or the directory root as teh current directory and fires the event. Used to browse up a level + /// + /// + public void GetDirectoryParentFolderContents() + { + var currentDirectory = new CodecDirectory(); + + if (DirectoryBrowseHistory.Count > 0) + { + var lastItemIndex = DirectoryBrowseHistory.Count - 1; + var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex]; + + DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]); + + currentDirectory = parentDirectoryContents; + + } + else + { + currentDirectory = DirectoryRoot; + } + + OnDirectoryResultReturned(currentDirectory); + } + + /// + /// Clears the session browse history and fires the event with the directory root + /// + public void SetCurrentDirectoryToRoot() + { + DirectoryBrowseHistory.Clear(); + + OnDirectoryResultReturned(DirectoryRoot); + } + + /// + /// Prints the directory to console + /// + /// + void PrintDirectory(CodecDirectory directory) + { + if (Debug.Level > 0) + { + Debug.Console(1, this, "Directory Results:\n"); + + foreach (DirectoryItem item in directory.CurrentDirectoryResults) + { + if (item is DirectoryFolder) + { + Debug.Console(1, this, "[+] {0}", item.Name); + } + else if (item is DirectoryContact) + { + Debug.Console(1, this, "{0}", item.Name); + } + } + Debug.Console(1, this, "Directory is on Root Level: {0}", !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + } + + } + + /// + /// Simple dial method + /// + /// + public override void Dial(string number) + { + EnqueueCommand(string.Format("xCommand Dial Number: \"{0}\"", number)); + } + + /// + /// Dials a specific meeting + /// + /// + public override void Dial(Meeting meeting) + { + foreach (Call c in meeting.Calls) + { + Dial(c.Number, c.Protocol, c.CallRate, c.CallType, meeting.Id); + } + } + + /// + /// Detailed dial method + /// + /// + /// + /// + /// + /// + public void Dial(string number, string protocol, string callRate, string callType, string meetingId) + { + EnqueueCommand(string.Format("xCommand Dial Number: \"{0}\" Protocol: {1} CallRate: {2} CallType: {3} BookingId: {4}", number, protocol, callRate, callType, meetingId)); + } + + + public override void EndCall(CodecActiveCallItem activeCall) + { + EnqueueCommand(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); + } + + public override void EndAllCalls() + { + foreach (CodecActiveCallItem activeCall in ActiveCalls) + { + EnqueueCommand(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); + } + } + + public override void AcceptCall(CodecActiveCallItem item) + { + EnqueueCommand("xCommand Call Accept"); + } + + public override void RejectCall(CodecActiveCallItem item) + { + EnqueueCommand("xCommand Call Reject"); + } + + #region IHasCallHold Members + + public void HoldCall(CodecActiveCallItem activeCall) + { + EnqueueCommand(string.Format("xCommand Call Hold CallId: {0}", activeCall.Id)); + } + + public void ResumeCall(CodecActiveCallItem activeCall) + { + EnqueueCommand(string.Format("xCommand Call Resume CallId: {0}", activeCall.Id)); + } + + #endregion + + #region IJoinCalls + + public void JoinCall(CodecActiveCallItem activeCall) + { + EnqueueCommand(string.Format("xCommand Call Join CallId: {0}", activeCall.Id)); + } + + public void JoinAllCalls() + { + StringBuilder ids = new StringBuilder(); + + foreach (var call in ActiveCalls) + { + if (call.IsActiveCall) + { + ids.Append(string.Format(" CallId: {0}", call.Id)); + } + } + + if (ids.Length > 0) + { + EnqueueCommand(string.Format("xCommand Call Join {0}", ids.ToString())); + } + } + + #endregion + + /// + /// Sends tones to the last connected call + /// + /// + public override void SendDtmf(string s) + { + EnqueueCommand(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); + } + + /// + /// Sends tones to a specific call + /// + /// + /// + public override void SendDtmf(string s, CodecActiveCallItem activeCall) + { + EnqueueCommand(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", activeCall.Id, s)); + } + + public void SelectPresentationSource(int source) + { + _desiredPresentationSource = source; + + StartSharing(); + } + + /// + /// Sets the ringtone volume level + /// + /// level from 0 - 100 in increments of 5 + public void SetRingtoneVolume(int volume) + { + if (volume < 0 || volume > 100) + { + Debug.Console(0, this, "Cannot set ringtone volume to '{0}'. Value must be between 0 - 100", volume); + return; + } + + if (volume % 5 != 0) + { + Debug.Console(0, this, "Cannot set ringtone volume to '{0}'. Value must be between 0 - 100 and a multiple of 5", volume); + return; + } + + EnqueueCommand(string.Format("xConfiguration Audio SoundsAndAlerts RingVolume: {0}", volume)); + } + + /// + /// Select source 1 as the presetnation source + /// + public void SelectPresentationSource1() + { + SelectPresentationSource(2); + } + + /// + /// Select source 2 as the presetnation source + /// + public void SelectPresentationSource2() + { + SelectPresentationSource(3); + } + + + + /// + /// Starts presentation sharing + /// + public override void StartSharing() + { + string sendingMode = string.Empty; + + if (IsInCall) + sendingMode = "LocalRemote"; + else + sendingMode = "LocalOnly"; + + if (_desiredPresentationSource > 0) + EnqueueCommand(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", _desiredPresentationSource, sendingMode)); + } + + /// + /// Stops sharing the current presentation + /// + public override void StopSharing() + { + _desiredPresentationSource = 0; + + EnqueueCommand("xCommand Presentation Stop"); + } + + + + public override void PrivacyModeOn() + { + EnqueueCommand("xCommand Audio Microphones Mute"); + } + + public override void PrivacyModeOff() + { + EnqueueCommand("xCommand Audio Microphones Unmute"); + } + + public override void PrivacyModeToggle() + { + EnqueueCommand("xCommand Audio Microphones ToggleMute"); + } + + public override void MuteOff() + { + EnqueueCommand("xCommand Audio Volume Unmute"); + } + + public override void MuteOn() + { + EnqueueCommand("xCommand Audio Volume Mute"); + } + + public override void MuteToggle() + { + EnqueueCommand("xCommand Audio Volume ToggleMute"); + } + + /// + /// Increments the voluem + /// + /// + public override void VolumeUp(bool pressRelease) + { + EnqueueCommand("xCommand Audio Volume Increase"); + } + + /// + /// Decrements the volume + /// + /// + public override void VolumeDown(bool pressRelease) + { + EnqueueCommand("xCommand Audio Volume Decrease"); + } + + /// + /// Scales the level and sets the codec to the specified level within its range + /// + /// level from slider (0-65535 range) + public override void SetVolume(ushort level) + { + var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); + EnqueueCommand(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel)); + } + + /// + /// Recalls the default volume on the codec + /// + public void VolumeSetToDefault() + { + EnqueueCommand("xCommand Audio Volume SetToDefault"); + } + + /// + /// Puts the codec in standby mode + /// + public override void StandbyActivate() + { + EnqueueCommand("xCommand Standby Activate"); + } + + /// + /// Wakes the codec from standby + /// + public override void StandbyDeactivate() + { + EnqueueCommand("xCommand Standby Deactivate"); + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new CiscoCodecJoinMap(joinStart); + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); + } + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + + LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); + + LinkCiscoCodecToApi(trilist, joinMap); + } + + public void LinkCiscoCodecToApi(BasicTriList trilist, CiscoCodecJoinMap joinMap) + { + // Custom commands to codec + trilist.SetStringSigAction(joinMap.CommandToDevice.JoinNumber, (s) => this.EnqueueCommand(s)); + + + var dndCodec = this as IHasDoNotDisturbMode; + if (dndCodec != null) + { + dndCodec.DoNotDisturbModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateDoNotDisturbMode.JoinNumber]); + dndCodec.DoNotDisturbModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateDoNotDisturbMode.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.ActivateDoNotDisturbMode.JoinNumber, () => dndCodec.ActivateDoNotDisturbMode()); + trilist.SetSigFalseAction(joinMap.DeactivateDoNotDisturbMode.JoinNumber, () => dndCodec.DeactivateDoNotDisturbMode()); + trilist.SetSigFalseAction(joinMap.ToggleDoNotDisturbMode.JoinNumber, () => dndCodec.ToggleDoNotDisturbMode()); + } + + var halfwakeCodec = this as IHasHalfWakeMode; + if (halfwakeCodec != null) + { + halfwakeCodec.StandbyIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateStandby.JoinNumber]); + halfwakeCodec.StandbyIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateStandby.JoinNumber]); + halfwakeCodec.HalfWakeModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateHalfWakeMode.JoinNumber]); + halfwakeCodec.EnteringStandbyModeFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnteringStandbyMode.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.ActivateStandby.JoinNumber, () => halfwakeCodec.StandbyActivate()); + trilist.SetSigFalseAction(joinMap.DeactivateStandby.JoinNumber, () => halfwakeCodec.StandbyDeactivate()); + trilist.SetSigFalseAction(joinMap.ActivateHalfWakeMode.JoinNumber, () => halfwakeCodec.HalfwakeActivate()); + } + + // Ringtone volume + trilist.SetUShortSigAction(joinMap.RingtoneVolume.JoinNumber, (u) => SetRingtoneVolume(u)); + RingtoneVolumeFeedback.LinkInputSig(trilist.UShortInput[joinMap.RingtoneVolume.JoinNumber]); + + // Presentation Source + trilist.SetUShortSigAction(joinMap.PresentationSource.JoinNumber, (u) => SelectPresentationSource(u)); + PresentationSourceFeedback.LinkInputSig(trilist.UShortInput[joinMap.PresentationSource.JoinNumber]); + + PresentationSendingLocalOnlyFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PresentationLocalOnly.JoinNumber]); + PresentationSendingLocalRemoteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PresentationLocalRemote.JoinNumber]); + } + + /// + /// Reboots the codec + /// + public void Reboot() + { + EnqueueCommand("xCommand SystemUnit Boot Action: Restart"); + } + + /// + /// Sets SelfView Mode based on config + /// + void SetSelfViewMode() + { + if (!IsInCall) + { + SelfViewModeOff(); + } + else + { + if (ShowSelfViewByDefault) + SelfViewModeOn(); + else + SelfViewModeOff(); + } + } + + /// + /// Turns on Selfview Mode + /// + public void SelfViewModeOn() + { + EnqueueCommand("xCommand Video Selfview Set Mode: On"); + } + + /// + /// Turns off Selfview Mode + /// + public void SelfViewModeOff() + { + EnqueueCommand("xCommand Video Selfview Set Mode: Off"); + } + + /// + /// Toggles Selfview mode on/off + /// + public void SelfViewModeToggle() + { + string mode = string.Empty; + + if (CodecStatus.Status.Video.Selfview.Mode.BoolValue) + mode = "Off"; + else + mode = "On"; + + EnqueueCommand(string.Format("xCommand Video Selfview Set Mode: {0}", mode)); + } + + /// + /// Sets a specified position for the selfview PIP window + /// + /// + public void SelfviewPipPositionSet(CodecCommandWithLabel position) + { + EnqueueCommand(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command)); + } + + /// + /// Toggles to the next selfview PIP position + /// + public void SelfviewPipPositionToggle() + { + if (_currentSelfviewPipPosition != null) + { + var nextPipPositionIndex = SelfviewPipPositions.IndexOf(_currentSelfviewPipPosition) + 1; + + if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list + nextPipPositionIndex = 0; + + SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); + } + } + + /// + /// Sets a specific local layout + /// + /// + public void LocalLayoutSet(CodecCommandWithLabel layout) + { + EnqueueCommand(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command)); + } + + /// + /// Toggles to the next local layout + /// + public void LocalLayoutToggle() + { + if(_currentLocalLayout != null) + { + var nextLocalLayoutIndex = LocalLayouts.IndexOf(_currentLocalLayout) + 1; + + if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list + nextLocalLayoutIndex = 0; + + LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]); + } + } + + /// + /// Toggles between single/prominent layouts + /// + public void LocalLayoutToggleSingleProminent() + { + if (_currentLocalLayout != null) + { + if (_currentLocalLayout.Label != "Prominent") + LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent"))); + else + LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single"))); + } + + } + + /// + /// + /// + public void MinMaxLayoutToggle() + { + if (PresentationViewMaximizedFeedback.BoolValue) + _currentPresentationView = "Minimized"; + else + _currentPresentationView = "Maximized"; + + EnqueueCommand(string.Format("xCommand Video PresentationView Set View: {0}", _currentPresentationView)); + PresentationViewMaximizedFeedback.FireUpdate(); + } + + /// + /// Calculates the current selfview PIP position + /// + void ComputeSelfviewPipStatus() + { + _currentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower())); + + if(_currentSelfviewPipPosition != null) + SelfviewIsOnFeedback.FireUpdate(); + } + + /// + /// Calculates the current local Layout + /// + void ComputeLocalLayout() + { + _currentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower())); + + if (_currentLocalLayout != null) + LocalLayoutFeedback.FireUpdate(); + } + + public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry) + { + EnqueueCommand(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId)); + } + + #region IHasCameraSpeakerTrack + + public void CameraAutoModeToggle() + { + if (!CameraAutoModeIsOnFeedback.BoolValue) + { + EnqueueCommand("xCommand Cameras SpeakerTrack Activate"); + } + else + { + EnqueueCommand("xCommand Cameras SpeakerTrack Deactivate"); + } + } + + public void CameraAutoModeOn() + { + if (CameraIsOffFeedback.BoolValue) + { + CameraMuteOff(); + } + + EnqueueCommand("xCommand Cameras SpeakerTrack Activate"); + } + + public void CameraAutoModeOff() + { + if (CameraIsOffFeedback.BoolValue) + { + CameraMuteOff(); + } + + EnqueueCommand("xCommand Cameras SpeakerTrack Deactivate"); + } + + #endregion + + /// + /// Builds the cameras List. Could later be modified to build from config data + /// + void SetUpCameras(List cameraInfo) + { + // Add the internal camera + Cameras = new List(); + + var camCount = CodecStatus.Status.Cameras.Camera.Count; + + // Deal with the case of 1 or no reported cameras + if (camCount <= 1) + { + var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1); + + if (camCount > 0) + { + // Try to get the capabilities from the codec + if (CodecStatus.Status.Cameras.Camera[0] != null && CodecStatus.Status.Cameras.Camera[0].Capabilities != null) + { + internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value); + } + } + + Cameras.Add(internalCamera); + //DeviceManager.AddDevice(internalCamera); + } + else + { + // Setup all the cameras + for (int i = 0; i < camCount; i++) + { + var cam = CodecStatus.Status.Cameras.Camera[i]; + + var id = (uint)i; + var name = string.Format("Camera {0}", id); + + // Check for a config object that matches the camera number + var camInfo = cameraInfo.FirstOrDefault(c => c.CameraNumber == i + 1); + if (camInfo != null) + { + id = (uint)camInfo.SourceId; + name = camInfo.Name; + } + + var key = string.Format("{0}-camera{1}", Key, id); + var camera = new CiscoSparkCamera(key, name, this, id); + + if (cam.Capabilities != null) + { + camera.SetCapabilites(cam.Capabilities.Options.Value); + } + + Cameras.Add(camera); + } + } + + // Add the far end camera + var farEndCamera = new CiscoFarEndCamera(Key + "-cameraFar", "Far End", this); + Cameras.Add(farEndCamera); + + SelectedCameraFeedback = new StringFeedback(() => SelectedCamera.Key); + + ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); + + NearEndPresets = new List(15); + + FarEndRoomPresets = new List(15); + + // Add the far end presets + for (int i = 1; i <= FarEndRoomPresets.Capacity; i++) + { + var label = string.Format("Far End Preset {0}", i); + FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false)); + } + + SelectedCamera = Cameras[0]; ; // call the method to select the camera and ensure the feedbacks get updated. + + } + + #region IHasCodecCameras Members + + public event EventHandler CameraSelected; + + public List Cameras { get; private set; } + + public StringFeedback SelectedCameraFeedback { get; private set; } + + private CameraBase _selectedCamera; + + /// + /// Returns the selected camera + /// + public CameraBase SelectedCamera + { + get + { + return _selectedCamera; + } + private set + { + _selectedCamera = value; + SelectedCameraFeedback.FireUpdate(); + ControllingFarEndCameraFeedback.FireUpdate(); + if (CameraIsOffFeedback.BoolValue) + CameraMuteOff(); + + var handler = CameraSelected; + if (handler != null) + { + handler(this, new CameraSelectedEventArgs(SelectedCamera)); + } + } + } + + public void SelectCamera(string key) + { + var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); + if (camera != null) + { + Debug.Console(2, this, "Selected Camera with key: '{0}'", camera.Key); + SelectedCamera = camera; + } + else + Debug.Console(2, this, "Unable to select camera with key: '{0}'", key); + + var ciscoCam = camera as CiscoSparkCamera; + if (ciscoCam != null) + { + EnqueueCommand(string.Format("xCommand Video Input SetMainVideoSource SourceId: {0}", ciscoCam.CameraId)); + } + } + + public CameraBase FarEndCamera { get; private set; } + + public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } + + #endregion + + /// + /// + /// + public class CiscoCodecInfo : VideoCodecInfo + { + public CiscoCodecStatus.RootObject CodecStatus { get; private set; } + + public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; } + + public override bool MultiSiteOptionIsEnabled + { + get + { + if (!string.IsNullOrEmpty(CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value) && CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true") + return true; + else + return false; + } + + } + public override string IpAddress + { + get + { + var address = string.Empty; + if (CodecConfiguration.Configuration.Network.Count > 0) + { + if(!string.IsNullOrEmpty(CodecConfiguration.Configuration.Network[0].IPv4.Address.Value)) + address = CodecConfiguration.Configuration.Network[0].IPv4.Address.Value; + } + + if (string.IsNullOrEmpty(address) && CodecStatus.Status.Network.Count > 0) + { + if(!string.IsNullOrEmpty(CodecStatus.Status.Network[0].IPv4.Address.Value)) + address = CodecStatus.Status.Network[0].IPv4.Address.Value; + } + return address; + } + } + public override string E164Alias + { + get + { + if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias.E164 != null) + { + return CodecConfiguration.Configuration.H323.H323Alias.E164.Value; + } + else + { + return string.Empty; + } + } + } + public override string H323Id + { + get + { + if (CodecConfiguration.Configuration.H323 != null && CodecConfiguration.Configuration.H323.H323Alias != null + && CodecConfiguration.Configuration.H323.H323Alias.ID != null) + { + return CodecConfiguration.Configuration.H323.H323Alias.ID.Value; + } + else + { + return string.Empty; + } + } + } + public override string SipPhoneNumber + { + get + { + if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.Registration.Count > 0) + { + var match = Regex.Match(CodecStatus.Status.SIP.Registration[0].URI.Value, @"(\d+)"); // extract numbers only + if (match.Success) + { + Debug.Console(1, "Extracted phone number as '{0}' from string '{1}'", match.Groups[1].Value, CodecStatus.Status.SIP.Registration[0].URI.Value); + return match.Groups[1].Value; + } + else + { + Debug.Console(1, "Unable to extract phone number from string: '{0}'", CodecStatus.Status.SIP.Registration[0].URI.Value); + return string.Empty; + } + } + else + { + Debug.Console(1, "Unable to extract phone number. No SIP Registration items found"); + return string.Empty; + } + } + } + + public override string SipUri + { + get + { + if (CodecStatus.Status.SIP != null && CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value != null) + { + return CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value; + } + else if (CodecStatus.Status.UserInterface != null && + CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value != null) + { + return CodecStatus.Status.UserInterface.ContactInfo.ContactMethod[0].Number.Value; + } + else + return string.Empty; + } + } + + public override bool AutoAnswerEnabled + { + get + { + if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value == null) return false; + return CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on"; + } + } + + public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration) + { + CodecStatus = status; + CodecConfiguration = configuration; + } + } + + + #region IHasCameraPresets Members + + public event EventHandler CodecRoomPresetsListHasChanged; + + public List NearEndPresets { get; private set; } + + public List FarEndRoomPresets { get; private set; } + + public void CodecRoomPresetSelect(int preset) + { + Debug.Console(1, this, "Selecting Preset: {0}", preset); + if (SelectedCamera is IAmFarEndCamera) + SelectFarEndPreset(preset); + else + EnqueueCommand(string.Format("xCommand RoomPreset Activate PresetId: {0}", preset)); + } + + public void CodecRoomPresetStore(int preset, string description) + { + EnqueueCommand(string.Format("xCommand RoomPreset Store PresetId: {0} Description: \"{1}\" Type: All", preset, description)); + } + + #endregion + + public void SelectFarEndPreset(int preset) + { + EnqueueCommand(string.Format("xCommand Call FarEndControl RoomPreset Activate CallId: {0} PresetId: {1}", GetCallId(), preset)); + } + + + #region IHasExternalSourceSwitching Members + + /// + /// Wheather the Cisco supports External Source Lists or not + /// + public bool ExternalSourceListEnabled + { + get; + private set; + } + + /// + /// The name of the RoutingInputPort to which the upstream external switcher is connected + /// + public string ExternalSourceInputPort { get; private set; } + + public bool BrandingEnabled { get; private set; } + private string _brandingUrl; + private bool _sendMcUrl; + + /// + /// 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; + } + EnqueueCommand(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) + { + EnqueueCommand(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() + { + EnqueueCommand("xCommand UserInterface Presentation ExternalSource RemoveAll"); + + } + + /// + /// Sets the selected source of the available external sources on teh Touch10 UI + /// + public void SetSelectedSource(string key) + { + EnqueueCommand(string.Format("xCommand UserInterface Presentation ExternalSource Select SourceIdentifier: {0}", key)); + _externalSourceChangeRequested = true; + } + + /// + /// Action that will run when the External Source is selected. + /// + public Action RunRouteAction { private get; set; } + + + + + + + #endregion + #region ExternalDevices + + + + #endregion + + #region IHasCameraOff Members + + public BoolFeedback CameraIsOffFeedback { get; private set; } + + public void CameraOff() + { + CameraMuteOn(); + } + + #endregion + + public BoolFeedback CameraIsMutedFeedback { get; private set; } + + /// + /// Mutes the outgoing camera video + /// + public void CameraMuteOn() + { + EnqueueCommand("xCommand Video Input MainVideo Mute"); + } + + /// + /// Unmutes the outgoing camera video + /// + public void CameraMuteOff() + { + EnqueueCommand("xCommand Video Input MainVideo Unmute"); + } + + /// + /// Toggles the camera mute state + /// + public void CameraMuteToggle() + { + if (CameraIsMutedFeedback.BoolValue) + CameraMuteOff(); + else + CameraMuteOn(); + } + + #region IHasDoNotDisturbMode Members + + public BoolFeedback DoNotDisturbModeIsOnFeedback { get; private set; } + + public void ActivateDoNotDisturbMode() + { + EnqueueCommand("xCommand Conference DoNotDisturb Activate"); + } + + public void DeactivateDoNotDisturbMode() + { + EnqueueCommand("xCommand Conference DoNotDisturb Deactivate"); + } + + public void ToggleDoNotDisturbMode() + { + if (DoNotDisturbModeIsOnFeedback.BoolValue) + { + DeactivateDoNotDisturbMode(); + } + else + { + ActivateDoNotDisturbMode(); + } + } + + #endregion + + #region IHasHalfWakeMode Members + + public BoolFeedback HalfWakeModeIsOnFeedback { get; private set; } + + public BoolFeedback EnteringStandbyModeFeedback { get; private set; } + + public void HalfwakeActivate() + { + EnqueueCommand("xCommand Standby Halfwake"); + } + + #endregion + } + + + /// + /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes + /// + public class CodecCommandWithLabel + { + public string Command { get; set; } + public string Label { get; set; } + + public CodecCommandWithLabel(string command, string label) + { + Command = command; + Label = label; + } + } + + /// + /// Tracks the initial sycnronization state of the codec when making a connection + /// + public class CodecSyncState : IKeyed + { + bool _InitialSyncComplete; + private readonly CiscoSparkCodec _parent; + + public event EventHandler InitialSyncCompleted; + private readonly CrestronQueue _commandQueue; + + public string Key { get; private set; } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool LoginMessageWasReceived { get; private set; } + + public bool JsonResponseModeSet { get; private set; } + + public bool InitialStatusMessageWasReceived { get; private set; } + + public bool InitialConfigurationMessageWasReceived { get; private set; } + + public bool FeedbackWasRegistered { get; private set; } + + public CodecSyncState(string key, CiscoSparkCodec parent) + { + Key = key; + _parent = parent; + _commandQueue = new CrestronQueue(50); + CodecDisconnected(); + } + + private void ProcessQueuedCommands() + { + while (InitialSyncComplete) + { + var query = _commandQueue.Dequeue(); + + _parent.SendText(query); + } + } + + public void AddCommandToQueue(string query) + { + _commandQueue.Enqueue(query); + } + + public void LoginMessageReceived() + { + LoginMessageWasReceived = true; + Debug.Console(1, this, "Login Message Received."); + CheckSyncStatus(); + } + + public void JsonResponseModeMessageReceived() + { + JsonResponseModeSet = true; + Debug.Console(1, this, "Json Response Mode Message Received."); + CheckSyncStatus(); + } + + public void InitialStatusMessageReceived() + { + InitialStatusMessageWasReceived = true; + Debug.Console(1, this, "Initial Codec Status Message Received."); + CheckSyncStatus(); + } + + public void InitialConfigurationMessageReceived() + { + InitialConfigurationMessageWasReceived = true; + Debug.Console(1, this, "Initial Codec Configuration Message Received."); + CheckSyncStatus(); + } + + public void FeedbackRegistered() + { + FeedbackWasRegistered = true; + Debug.Console(1, this, "Initial Codec Feedback Registration Successful."); + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + _commandQueue.Clear(); + LoginMessageWasReceived = false; + JsonResponseModeSet = false; + InitialConfigurationMessageWasReceived = false; + InitialStatusMessageWasReceived = false; + FeedbackWasRegistered = false; + InitialSyncComplete = false; + } + + void CheckSyncStatus() + { + if (LoginMessageWasReceived && JsonResponseModeSet && InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Codec Sync Complete!"); + Debug.Console(1, this, "{0} Command queued. Processing now...", _commandQueue.Count); + + // Invoke a thread for the queue + CrestronInvoke.BeginInvoke((o) => { + ProcessQueuedCommands(); + }); + } + else + InitialSyncComplete = false; + } + } + + public class CiscoSparkCodecFactory : EssentialsDeviceFactory + { + public CiscoSparkCodecFactory() + { + TypeNames = new List() { "ciscospark", "ciscowebex", "ciscowebexpro", "ciscoroomkit", "ciscosparkpluscodec" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new Cisco Codec Device"); + + var comm = CommFactory.CreateCommForDevice(dc); + return new VideoCodec.Cisco.CiscoSparkCodec(dc, comm); + } + } + } \ No newline at end of file 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 1836bafb..7f36b0bf 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 @@ -50,8 +50,16 @@ namespace PepperDash.Essentials.Devices.Common.Codec public uint PhonebookResultsLimit { get; set; } [JsonProperty("UiBranding")] - public BrandingLogoProperties UiBranding { get; set; } + public BrandingLogoProperties UiBranding { get; set; } + [JsonProperty("cameraInfo")] + public List CameraInfo { get; set; } + + + public CiscoSparkCodecPropertiesConfig() + { + CameraInfo = new List(); + } } public class SharingProperties @@ -68,4 +76,14 @@ namespace PepperDash.Essentials.Devices.Common.Codec [JsonProperty("brandingUrl")] public string BrandingUrl { get; set; } } + + /// + /// Describes configuration information for the near end cameras + /// + public class CameraInfo + { + public int CameraNumber { get; set; } + public string Name { get; set; } + public int SourceId { get; set; } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs index 1b456774..b6327d52 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs @@ -26,6 +26,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec void CodecRoomPresetSelect(int preset); void CodecRoomPresetStore(int preset, string description); + + void SelectFarEndPreset(int preset); } public static class RoomPresets diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs index 8ae9e643..05c07378 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs @@ -112,16 +112,46 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public string Value { get; set; } } - public class RingVolume + public class RingVolume : ValueProperty { public string valueSpaceRef { get; set; } - public string Value { get; set; } + + string _Value; + + /// + /// Sets Value and triggers the action when set + /// + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + + public int Volume + { + get + { + return Int32.Parse(_Value); + } + } } public class SoundsAndAlerts { public RingTone RingTone { get; set; } public RingVolume RingVolume { get; set; } + + public SoundsAndAlerts() + { + RingVolume = new RingVolume(); + } } public class Audio @@ -131,6 +161,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Microphones Microphones { get; set; } public Output Output { get; set; } public SoundsAndAlerts SoundsAndAlerts { get; set; } + + + public Audio() + { + SoundsAndAlerts = new SoundsAndAlerts(); + } } public class DefaultMode @@ -340,6 +376,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Delay Delay { get; set; } public Mode9 Mode { get; set; } public Mute2 Mute { get; set; } + + public AutoAnswer() + { + Mode = new Mode9(); + Delay = new Delay(); + Mute = new Mute2(); + } } public class Protocol @@ -440,6 +483,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public MaxTotalTransmitCallRate MaxTotalTransmitCallRate { get; set; } public MaxTransmitCallRate MaxTransmitCallRate { get; set; } public MultiStream MultiStream { get; set; } + + public Conference() + { + AutoAnswer = new AutoAnswer(); + } } public class LoginName @@ -690,6 +738,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Assignment Assignment { get; set; } public Gateway Gateway { get; set; } public SubnetMask SubnetMask { get; set; } + + public IPv4() + { + Address = new Address4(); + } } public class Address5 @@ -841,6 +894,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public RemoteAccess RemoteAccess { get; set; } public Speed Speed { get; set; } public VLAN VLAN { get; set; } + + public Network() + { + IPv4 = new IPv4(); + } } public class Mode19 @@ -1797,11 +1855,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public UserInterface UserInterface { get; set; } public UserManagement UserManagement { get; set; } public Video2 Video { get; set; } + + public Configuration() + { + Audio = new Audio(); + Conference = new Conference(); + Network = new List(); + } } public class RootObject { public Configuration Configuration { get; set; } + + public RootObject() + { + Configuration = new Configuration(); + } } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs index c1cd92cf..5bde30ff 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs @@ -12,28 +12,28 @@ using PepperDash.Essentials.Devices.Common.VideoCodec.CiscoCodec; namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco { + // Helper Classes for Proerties + public abstract class ValueProperty + { + /// + /// Triggered when Value is set + /// + public Action ValueChangedAction { get; set; } + + protected void OnValueChanged() + { + var a = ValueChangedAction; + if (a != null) + a(); + } + + } + /// /// This class exists to capture serialized data sent back by a Cisco codec in JSON output mode /// public class CiscoCodecStatus { - // Helper Classes for Proerties - public abstract class ValueProperty - { - /// - /// Triggered when Value is set - /// - public Action ValueChangedAction { get; set; } - - protected void OnValueChanged() - { - var a = ValueChangedAction; - if (a != null) - a(); - } - - } - public class ConnectionStatus { @@ -262,11 +262,30 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public string Value { get; set; } } + public class DectectedConnector + { + public string Value { get; set; } + + public int ConnectorId + { + get + { + if(!string.IsNullOrEmpty(Value)) + { + return Convert.ToUInt16(Value); + } + else + return -1; + } + } + } + public class Camera { public string id { get; set; } public Capabilities Capabilities { get; set; } public Connected Connected { get; set; } + public DectectedConnector DetectedConnector { get; set; } public Flip Flip { get; set; } public HardwareID HardwareID { get; set; } public MacAddress MacAddress { get; set; } @@ -275,6 +294,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Position Position { get; set; } public SerialNumber SerialNumber { get; set; } public SoftwareID SoftwareID { get; set; } + + public Camera() + { + Manufacturer = new Manufacturer(); + Model = new Model(); + DetectedConnector = new DectectedConnector(); + } } public class Availability : ValueProperty @@ -298,11 +324,34 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } } + public class CallStatus : ValueProperty + { + string _Value; + public bool BoolValue { get; private set; } + + + public string Value + { + get + { + return _Value; + } + set + { + // If the incoming value is "Active" it sets the BoolValue true, otherwise sets it false + _Value = value; + BoolValue = value == "Connected"; + OnValueChanged(); + } + } + } + public class Status2 : ValueProperty { string _Value; public bool BoolValue { get; private set; } + public string Value { get @@ -440,9 +489,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public CallId CallId { get; set; } } - public class DoNotDisturb + public class DoNotDisturb : ValueProperty { - public string Value { get; set; } + string _Value; + + public bool BoolValue { get; private set; } + + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "On" || value == "Active"; + OnValueChanged(); + } + } } public class Mode @@ -541,9 +607,47 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco } } - public class SendingMode + public class SendingMode : ValueProperty { - public string Value { get; set; } + string _Value; + + /// + /// Sets Value and triggers the action when set + /// + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + + public bool LocalOnly + { + get + { + if(string.IsNullOrEmpty(_Value)) + return false; + + return _Value.ToLower() == "localonly"; + } + } + + public bool LocalRemote + { + get + { + if(string.IsNullOrEmpty(_Value)) + return false; + + return _Value.ToLower() == "localremote"; + } + } } public class LocalInstance @@ -556,6 +660,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public LocalInstance() { Source = new Source2(); + SendingMode = new SendingMode(); } } @@ -600,6 +705,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Conference2() { Presentation = new Presentation(); + DoNotDisturb = new DoNotDisturb(); } } @@ -863,6 +969,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Address4 Address { get; set; } public Gateway Gateway { get; set; } public SubnetMask SubnetMask { get; set; } + + public IPv4() + { + Address = new Address4(); + } } public class Address5 @@ -905,6 +1016,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public IPv4 IPv4 { get; set; } public IPv6 IPv6 { get; set; } public VLAN VLAN { get; set; } + + public Network() + { + IPv4 = new IPv4(); + } } public class CurrentAddress @@ -1380,12 +1496,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public class State : ValueProperty { + string _value; + public bool BoolValue { get; private set; } public string Value // Valid values are Standby/EnteringStandby/Halfwake/Off { + get { return _value; } set { + _value = value; // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false BoolValue = value == "On" || value == "Standby"; OnValueChanged(); @@ -1927,9 +2047,30 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public string Value { get; set; } } - public class Duration + public class Duration : ValueProperty { - public string Value { get; set; } + private string _Value; + + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + + public TimeSpan DurationValue + { + get + { + return new TimeSpan(0, 0, Int32.Parse(_Value)); + } + } } public class FacilityServiceId @@ -1942,9 +2083,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public string Value { get; set; } } - public class PlacedOnHold + public class PlacedOnHold : ValueProperty { - public string Value { get; set; } + public bool BoolValue { get; private set; } + + public string Value + { + set + { + // If the incoming value is "True" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "True"; + OnValueChanged(); + } + } } public class Protocol @@ -1985,13 +2136,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Protocol Protocol { get; set; } public ReceiveCallRate ReceiveCallRate { get; set; } public RemoteNumber RemoteNumber { get; set; } - public Status2 Status { get; set; } + public CallStatus Status { get; set; } public TransmitCallRate TransmitCallRate { get; set; } public Call() { CallType = new CallType(); - Status = new Status2(); + Status = new CallStatus(); + Duration = new Duration(); } } @@ -2091,6 +2243,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco Conference = new Conference2(); SystemUnit = new SystemUnit(); Video = new Video(); + Conference = new Conference2(); + Network = new List(); } } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs index 0d304028..cfe70e1b 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs @@ -42,9 +42,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces public Boolean WaitingForHost { get; private set; } [JsonProperty("isLocked", NullValueHandling = NullValueHandling.Ignore)] public Boolean IsLocked { get; private set; } + [JsonProperty("isRecording", NullValueHandling = NullValueHandling.Ignore)] + public Boolean IsRecording { get; private set; } - public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting, bool waitingForHost, bool isLocked) + public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting, bool waitingForHost, bool isLocked, bool isRecording) { Id = id; Name = name; @@ -55,6 +57,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces IsSharingMeeting = isSharingMeeting; WaitingForHost = waitingForHost; IsLocked = isLocked; + IsRecording = isRecording; } } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingRecording.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingRecording.cs index 771a0865..b05362c3 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingRecording.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingRecording.cs @@ -15,4 +15,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces void StopRecording(); void ToggleRecording(); } + + public interface IHasMeetingRecordingWithPrompt : IHasMeetingRecording + { + BoolFeedback RecordConsentPromptIsVisible { get; } + + /// + /// Used to agree or disagree to the meeting being recorded when prompted + /// + /// + void RecordingPromptAcknowledgement(bool agree); + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs index 11c5f147..98c94bdc 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs @@ -24,6 +24,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces /// /// void SetParticipantAsHost(int userId); + + /// + /// Admits a participant from the waiting room + /// + /// + void AdmitParticipantFromWaitingRoom(int userId); } /// diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs new file mode 100644 index 00000000..cc9dcd3d --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + /// + /// Describes a device that has Standby Mode capability + /// + public interface IHasStandbyMode + { + BoolFeedback StandbyIsOnFeedback { get; } + + void StandbyActivate(); + + void StandbyDeactivate(); + } + + /// + /// Describes a device that has Half Waek Mode capability + /// + public interface IHasHalfWakeMode : IHasStandbyMode + { + BoolFeedback HalfWakeModeIsOnFeedback { get; } + + BoolFeedback EnteringStandbyModeFeedback { get; } + + void HalfwakeActivate(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IJoinCalls.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IJoinCalls.cs new file mode 100644 index 00000000..b84db1e9 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IJoinCalls.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public interface IJoinCalls + { + void JoinCall(CodecActiveCallItem activeCall); + void JoinAllCalls(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs index 78302fcc..1f79d936 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs @@ -431,8 +431,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec m.MinutesBeforeMeeting = 5; m.Id = i.ToString(); m.Organizer = "Employee " + 1; - m.StartTime = DateTime.Now.AddMinutes(6).AddHours(i); - m.EndTime = DateTime.Now.AddHours(i).AddMinutes(16); + m.StartTime = DateTime.Now.AddMinutes(5).AddHours(i); + m.EndTime = DateTime.Now.AddHours(i).AddMinutes(50); m.Title = "Meeting " + i; m.Calls.Add(new Call() { Number = i + "meeting@fake.com"}); _CodecSchedule.Meetings.Add(m); @@ -602,6 +602,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec CameraAutoModeIsOnFeedback = new BoolFeedback(() => _CameraAutoModeIsOn); + SupportsCameraAutoMode = true; + CameraAutoModeIsOnFeedback.FireUpdate(); DeviceManager.AddDevice(internalCamera); @@ -609,7 +611,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec NearEndPresets = new List(15); // Fix the capacity to emulate Cisco - NearEndPresets = PropertiesConfig.Presets; + if (PropertiesConfig.Presets != null && PropertiesConfig.Presets.Count > 0) + { + NearEndPresets = PropertiesConfig.Presets; + } + else + { + for (int i = 1; i <= NearEndPresets.Capacity; i++) + { + var label = string.Format("Near End Preset {0}", i); + NearEndPresets.Add(new CodecRoomPreset(i, label, true, false)); + } + } FarEndRoomPresets = new List(15); // Fix the capacity to emulate Cisco @@ -751,6 +764,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec SetConfig(Config); } + public void SelectFarEndPreset(int i) + { + Debug.Console(1, this, "Selecting Far End Preset: {0}", i); + } + #endregion protected override void CustomSetConfig(DeviceConfig config) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVcPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVcPropertiesConfig.cs index 6a790af6..d294aff1 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVcPropertiesConfig.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVcPropertiesConfig.cs @@ -18,5 +18,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec [JsonProperty("presets")] public List Presets { get; set; } + + public MockVcPropertiesConfig() + { + Favorites = new List(); + Presets = new List(); + } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs index 7db58b1e..593472e9 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -25,7 +25,7 @@ using Feedback = PepperDash.Essentials.Core.Feedback; namespace PepperDash.Essentials.Devices.Common.VideoCodec { public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, - IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced + IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced, IHasStandbyMode { private const int XSigEncoding = 28591; protected const int MaxParticipants = 50; @@ -152,6 +152,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public abstract void AcceptCall(CodecActiveCallItem call); public abstract void RejectCall(CodecActiveCallItem call); public abstract void SendDtmf(string s); + public virtual void SendDtmf(string s, CodecActiveCallItem call) { } #endregion @@ -259,10 +260,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// public virtual void ListCalls() { + Debug.Console(1, this, "Active Calls:"); + var sb = new StringBuilder(); foreach (var c in ActiveCalls) { - sb.AppendFormat("{0} {1} -- {2} {3}\n", c.Id, c.Number, c.Name, c.Status); + sb.AppendFormat("id: {0} number: {1} -- name: {2} status: {3} onHold: {4}\r\n", c.Id, c.Number, c.Name, c.Status, c.IsOnHold); } Debug.Console(1, this, "\n{0}\n", sb.ToString()); } @@ -328,15 +331,22 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec LinkVideoCodecVolumeToApi(trilist, joinMap); + LinkVideoCodecInfoToApi(trilist, joinMap); + + // Register for this event to link any functions that require the codec to be ready first + codec.IsReadyChange += (o, a) => + { + if (codec is IHasCodecCameras) + { + LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap); + } + }; + if (codec is ICommunicationMonitor) { LinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap); } - if (codec is IHasCodecCameras) - { - LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap); - } if (codec is IHasCodecSelfView) { @@ -390,6 +400,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap); } + if (codec is IHasCallHistory) + { + LinkVideoCodecCallHistoryToApi(codec as IHasCallHistory, trilist, joinMap); + } + trilist.OnlineStatusChange += (device, args) => { if (!args.DeviceOnLine) return; @@ -411,7 +426,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec if (codec is IHasCameraAutoMode) { - trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, true); + trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); } @@ -436,6 +451,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec (codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate(); } + if (codec is IHasCallHistory) + { + UpdateCallHistory((codec as IHasCallHistory), trilist, joinMap); + } + SharingContentIsOnFeedback.FireUpdate(); trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); @@ -444,6 +464,31 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec }; } + private void LinkVideoCodecInfoToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled); + trilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled); + trilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress); + trilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber); + trilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias); + trilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id); + trilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri); + + trilist.OnlineStatusChange += (o, a) => + { + if (a.DeviceOnLine) + { + trilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled); + trilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled); + trilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress); + trilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber); + trilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias); + trilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id); + trilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri); + } + }; + } + private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) { codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]); @@ -645,37 +690,37 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec { if (meetingIndex >= maxParticipants * offset) break; - Debug.Console(2, this, -@"Updating Participant on xsig: -Name: {0} (s{9}) -AudioMute: {1} (d{10}) -VideoMute: {2} (d{11}) -CanMuteVideo: {3} (d{12}) -CanUMuteVideo: {4} (d{13}) -IsHost: {5} (d{14}) -HandIsRaised: {6} (d{15}) -IsPinned: {7} (d{16}) -ScreenIndexIsPinnedTo: {8} (a{17}) -", - participant.Name, - participant.AudioMuteFb, - participant.VideoMuteFb, - participant.CanMuteVideo, - participant.CanUnmuteVideo, - participant.IsHost, - participant.HandIsRaisedFb, - participant.IsPinnedFb, - participant.ScreenIndexIsPinnedToFb, - stringIndex + 1, - digitalIndex + 1, - digitalIndex + 2, - digitalIndex + 3, - digitalIndex + 4, - digitalIndex + 5, - digitalIndex + 6, - digitalIndex + 7, - analogIndex + 1 - ); +// Debug.Console(2, this, +//@"Updating Participant on xsig: +//Name: {0} (s{9}) +//AudioMute: {1} (d{10}) +//VideoMute: {2} (d{11}) +//CanMuteVideo: {3} (d{12}) +//CanUMuteVideo: {4} (d{13}) +//IsHost: {5} (d{14}) +//HandIsRaised: {6} (d{15}) +//IsPinned: {7} (d{16}) +//ScreenIndexIsPinnedTo: {8} (a{17}) +//", +// participant.Name, +// participant.AudioMuteFb, +// participant.VideoMuteFb, +// participant.CanMuteVideo, +// participant.CanUnmuteVideo, +// participant.IsHost, +// participant.HandIsRaisedFb, +// participant.IsPinnedFb, +// participant.ScreenIndexIsPinnedToFb, +// stringIndex + 1, +// digitalIndex + 1, +// digitalIndex + 2, +// digitalIndex + 3, +// digitalIndex + 4, +// digitalIndex + 5, +// digitalIndex + 6, +// digitalIndex + 7, +// analogIndex + 1 +// ); //digitals @@ -945,18 +990,33 @@ ScreenIndexIsPinnedTo: {8} (a{17}) private void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) { codec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig( - trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); + trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]); trilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory); - trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i)); + trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i, trilist, joinMap)); + + // Report feedback for number of contact methods for selected contact trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents); + if (codec.DirectoryRoot != null) + { + trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)codec.DirectoryRoot.CurrentDirectoryResults.Count); + + var clearBytes = XSigHelpers.ClearOutputs(); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var directoryXSig = UpdateDirectoryXSig(codec.DirectoryRoot, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + + Debug.Console(2, this, "Directory XSig Length: {0}", directoryXSig.Length); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); + } + codec.DirectoryResultReturned += (sender, args) => { trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)args.Directory.CurrentDirectoryResults.Count); @@ -967,6 +1027,8 @@ ScreenIndexIsPinnedTo: {8} (a{17}) Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); var directoryXSig = UpdateDirectoryXSig(args.Directory, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + Debug.Console(2, this, "Directory XSig Length: {0}", directoryXSig.Length); + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); }; @@ -981,40 +1043,154 @@ ScreenIndexIsPinnedTo: {8} (a{17}) }; } - private void SelectDirectoryEntry(IHasDirectory codec, ushort i) + + + private void SelectDirectoryEntry(IHasDirectory codec, ushort i, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) { - var entry = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; + if (i < 1 || i > codec.CurrentDirectoryResult.CurrentDirectoryResults.Count) return; - if (entry is DirectoryFolder) + _selectedDirectoryItem = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; + + + if (_selectedDirectoryItem is DirectoryFolder) { - codec.GetDirectoryFolderContents(entry.FolderId); - return; + codec.GetDirectoryFolderContents(_selectedDirectoryItem.FolderId); + trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0); + trilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, _selectedDirectoryItem.Name); + trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty); + trilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber); + return; } - var dialableEntry = entry as IInvitableContact; + // not a folder. Clear this value + trilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, string.Empty); - if (dialableEntry != null) - { - Dial(dialableEntry); - return; - } + var selectedContact = _selectedDirectoryItem as DirectoryContact; + if (selectedContact != null) + { + trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, selectedContact.Name); + + } - var entryToDial = entry as DirectoryContact; + // Allow auto dial of selected line. Always dials first contact method + if (!trilist.GetBool(joinMap.DirectoryDisableAutoDialSelectedLine.JoinNumber)) + { + var invitableEntry = _selectedDirectoryItem as IInvitableContact; - if (entryToDial == null) return; + if (invitableEntry != null) + { + Dial(invitableEntry); + return; + } - Dial(entryToDial.ContactMethods[0].Number); + var entryToDial = _selectedDirectoryItem as DirectoryContact; + + trilist.SetString(joinMap.DirectoryEntrySelectedNumber.JoinNumber, selectedContact.ContactMethods[0].Number); + + if (entryToDial == null) return; + + Dial(entryToDial.ContactMethods[0].Number); + return; + } + else + { + // If auto dial is disabled... + var entryToDial = _selectedDirectoryItem as DirectoryContact; + + if (entryToDial == null) + { + // Clear out values and actions from last selected item + trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0); + trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty); + trilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber); + return; + } + + trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, (ushort)entryToDial.ContactMethods.Count); + + // Update the action to dial the selected contact method + trilist.SetUShortSigAction(joinMap.SelectContactMethod.JoinNumber, (u) => + { + if (u < 1 || u > entryToDial.ContactMethods.Count) return; + + trilist.SetSigFalseAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber, () => Dial(entryToDial.ContactMethods[u - 1].Number)); + }); + + // Sets DirectoryDialSelectedLine join action to dial first contact method + trilist.SetSigFalseAction(joinMap.DirectoryDialSelectedLine.JoinNumber, () => Dial(entryToDial.ContactMethods[0].Number)); + + var clearBytes = XSigHelpers.ClearOutputs(); + + trilist.SetString(joinMap.ContactMethods.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var contactMethodsXSig = UpdateContactMethodsXSig(entryToDial); + + trilist.SetString(joinMap.ContactMethods.JoinNumber, contactMethodsXSig); + } } + /// + /// Generates the XSig data representing the available contact methods for the selected DirectoryContact + /// + /// + /// + private string UpdateContactMethodsXSig(DirectoryContact contact) + { + const int maxMethods = 10; + const int maxStrings = 3; + const int offset = maxStrings; + var stringIndex = 0; + var arrayIndex = 0; + // Create a new token array and set the size to the number of methods times the total number of signals + var tokenArray = new XSigToken[maxMethods * offset]; + + Debug.Console(2, this, "Creating XSIG token array with size {0}", maxMethods * offset); + + // TODO: Add code to generate XSig data + foreach (var method in contact.ContactMethods) + { + if (arrayIndex >= maxMethods * offset) + break; + + //serials + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, method.Number); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, method.ContactMethodId.ToString()); + tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, method.Device.ToString()); + + arrayIndex += offset; + stringIndex += maxStrings; + } + + while (arrayIndex < maxMethods) + { + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty); + + arrayIndex += offset; + stringIndex += maxStrings; + } + + return GetXSigString(tokenArray); + } + private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) { var contactIndex = 1; var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count]; + Debug.Console(2, this, "Is root {0} Directory Count: {1}", isRoot, directory.CurrentDirectoryResults.Count); + foreach (var entry in directory.CurrentDirectoryResults) { var arrayIndex = contactIndex - 1; + Debug.Console(2, this, "Entry Name: {0}, Folder ID: {1}", entry.Name, entry.FolderId); + if (entry is DirectoryFolder && entry.ParentFolderId == "root") { tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, String.Format("[+] {0}", entry.Name)); @@ -1024,8 +1200,6 @@ ScreenIndexIsPinnedTo: {8} (a{17}) continue; } - if (isRoot && String.IsNullOrEmpty(entry.FolderId)) continue; - tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, entry.Name); contactIndex++; @@ -1039,8 +1213,34 @@ ScreenIndexIsPinnedTo: {8} (a{17}) trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber, () => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue)); - //End All calls for now - trilist.SetSigFalseAction(joinMap.EndCall.JoinNumber, EndAllCalls); + //End All calls + trilist.SetSigFalseAction(joinMap.EndAllCalls.JoinNumber, EndAllCalls); + + //End a specific call, specified by index. Maximum 8 calls supported + for (int i = 0; i < joinMap.EndCallStart.JoinSpan; i++) + { + var callIndex = i; + + trilist.SetSigFalseAction((uint)(joinMap.EndCallStart.JoinNumber + i), () => + { + + if (callIndex < 0 || callIndex >= ActiveCalls.Count) + { + Debug.Console(2, this, "Cannot end call. No call found at index: {0}", callIndex); + return; + } + + var call = ActiveCalls[callIndex]; + if (call != null) + { + EndCall(call); + } + else + { + Debug.Console(0, this, "[End Call] Unable to find call at index '{0}'", i); + } + }); + } trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); @@ -1052,30 +1252,112 @@ ScreenIndexIsPinnedTo: {8} (a{17}) Debug.Console(1, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming); trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status == eCodecCallStatus.Ringing); - if (args.CallItem.Direction == eCodecCallDirection.Incoming) - { - trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem)); - trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem)); - } + if (args.CallItem.Direction == eCodecCallDirection.Incoming) + { + trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem)); + trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem)); + trilist.SetString(joinMap.IncomingCallName.JoinNumber, args.CallItem.Name); + trilist.SetString(joinMap.IncomingCallNumber.JoinNumber, args.CallItem.Number); + } + else + { + trilist.SetString(joinMap.IncomingCallName.JoinNumber, string.Empty); + trilist.SetString(joinMap.IncomingCallNumber.JoinNumber, string.Empty); + } + trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + + trilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count); }; - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; + var joinCodec = this as IJoinCalls; + if (joinCodec != null) + { + trilist.SetSigFalseAction(joinMap.JoinAllCalls.JoinNumber, () => joinCodec.JoinAllCalls()); - // TODO [ ] Issue #868 - trilist.SetString(joinMap.CurrentCallData.JoinNumber, "\xFC"); - UpdateCallStatusXSig(); - }; + for (int i = 0; i < joinMap.JoinCallStart.JoinSpan; i++) + { + trilist.SetSigFalseAction((uint)(joinMap.JoinCallStart.JoinNumber + i), () => + { + var call = ActiveCalls[i]; + if (call != null) + { + joinCodec.JoinCall(call); + } + else + { + Debug.Console(0, this, "[Join Call] Unable to find call at index '{0}'", i); + } + }); + } + } + + var holdCodec = this as IHasCallHold; + if (holdCodec != null) + { + trilist.SetSigFalseAction(joinMap.HoldAllCalls.JoinNumber, () => + { + foreach (var call in ActiveCalls) + { + holdCodec.HoldCall(call); + } + }); + + for (int i = 0; i < joinMap.HoldCallsStart.JoinSpan; i++) + { + var index = i; + + trilist.SetSigFalseAction((uint)(joinMap.HoldCallsStart.JoinNumber + index), () => + { + if (index < 0 || index >= ActiveCalls.Count) return; + + var call = ActiveCalls[index]; + if (call != null) + { + holdCodec.HoldCall(call); + } + else + { + Debug.Console(0, this, "[Hold Call] Unable to find call at index '{0}'", i); + } + }); + + trilist.SetSigFalseAction((uint)(joinMap.ResumeCallsStart.JoinNumber + index), () => + { + if (index < 0 || index >= ActiveCalls.Count) return; + + var call = ActiveCalls[index]; + if (call != null) + { + holdCodec.ResumeCall(call); + } + else + { + Debug.Console(0, this, "[Resume Call] Unable to find call at index '{0}'", i); + } + }); + } + } + + + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.CurrentCallData.JoinNumber, "\xFC"); + UpdateCallStatusXSig(); + }; } private string UpdateCallStatusXSig() { const int maxCalls = 8; - const int maxStrings = 5; - const int offset = 6; + const int maxStrings = 6; + const int maxDigitals = 2; + const int offset = maxStrings + maxDigitals; var stringIndex = 0; var digitalIndex = maxStrings * maxCalls; var arrayIndex = 0; @@ -1087,7 +1369,8 @@ ScreenIndexIsPinnedTo: {8} (a{17}) if (arrayIndex >= maxCalls * offset) break; //digitals - tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, call.IsOnHold); //serials tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty); @@ -1095,6 +1378,12 @@ ScreenIndexIsPinnedTo: {8} (a{17}) tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString()); tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, call.Type.ToString()); tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, call.Status.ToString()); + if(call.Duration != null) + { + // May need to verify correct string format here + var dur = string.Format("{0:c}", call.Duration); + tokenArray[arrayIndex + 6] = new XSigSerialToken(stringIndex + 6, dur); + } arrayIndex += offset; stringIndex += maxStrings; @@ -1103,14 +1392,17 @@ ScreenIndexIsPinnedTo: {8} (a{17}) while (digitalIndex < maxCalls) { //digitals - tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - //serials + + //serials tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty); tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty); tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty); tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, String.Empty); tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, String.Empty); + tokenArray[arrayIndex + 6] = new XSigSerialToken(stringIndex + 6, String.Empty); arrayIndex += offset; stringIndex += maxStrings; @@ -1122,25 +1414,61 @@ ScreenIndexIsPinnedTo: {8} (a{17}) private void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) { - trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmf("0")); - trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmf("1")); - trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmf("2")); - trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmf("3")); - trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmf("4")); - trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmf("5")); - trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmf("6")); - trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmf("7")); - trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmf("8")); - trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmf("9")); - trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmf("*")); - trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmf("#")); + trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmfAction("0", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmfAction("1", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmfAction("2", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmfAction("3", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmfAction("4", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmfAction("5", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmfAction("6", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmfAction("7", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmfAction("8", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmfAction("9", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmfAction("*", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmfAction("#", trilist, joinMap)); } + /// + /// Sends the specified string as a DTMF command. + /// Reads the value of the SendDtmfToSpecificCallInstance digital join and SelectCall analog join to determine + /// Whther to send to a specific call index or to the last connected call + /// + /// + /// + /// + private void SendDtmfAction(string s, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + if (!trilist.GetBool(joinMap.SendDtmfToSpecificCallIndex.JoinNumber)) + { + SendDtmf(s); + } + else + { + var callIndex = trilist.GetUshort(joinMap.SelectCall.JoinNumber); + if (callIndex > 0 && callIndex <= 8) + { + var call = ActiveCalls[callIndex - 1]; + if (call != null && call.IsActiveCall) + { + SendDtmf(s, call); + } + else + { + Debug.Console(0, this, "Warning: No call found at index {0} or call is not active.", callIndex); + } + } + else + { + Debug.Console(0, this, "Warning: Invalid call index specified. Please use a value of 1-8."); + } + } + } + private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) { trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle); - codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CameraLayoutStringFb.JoinNumber]); + codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentLayoutStringFb.JoinNumber]); } private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) @@ -1271,18 +1599,81 @@ ScreenIndexIsPinnedTo: {8} (a{17}) else camera.ZoomStop(); }); + + trilist.SetBoolSigAction(joinMap.CameraFocusNear.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraFocusControl; + + if (camera == null) return; + + if (b) camera.FocusNear(); + else camera.FocusStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraFocusFar.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraFocusControl; + + if (camera == null) return; + + if (b) camera.FocusFar(); + else camera.FocusStop(); + }); + + trilist.SetSigFalseAction(joinMap.CameraFocusAuto.JoinNumber, () => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraFocusControl; + + if (camera == null) return; + + camera.TriggerAutoFocus(); + }); + + // Camera count + trilist.SetUshort(joinMap.CameraCount.JoinNumber, (ushort)codec.Cameras.Count); + + // Camera names + for (uint i = 0; i < joinMap.CameraNamesFb.JoinSpan; i++) + { + //Check the count first + if (i < codec.Cameras.Count && codec.Cameras[(int)i] != null) + { + trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, codec.Cameras[(int)i].Name); + } + else + { + trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, ""); + } + } + //Camera Select trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => { - if (codec.SelectedCamera == null) return; - - codec.SelectCamera(codec.Cameras[i].Key); + if (i > 0 && i <= codec.Cameras.Count) + { + codec.SelectCamera(codec.Cameras[i - 1].Key); + } + else + { + Debug.Console(0, this, "Unable to select. No camera found at index {0}", i); + } }); + // Set initial selected camera feedback + if (codec.SelectedCamera != null) + { + trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)codec.Cameras.FindIndex((c) => c.Key == codec.SelectedCamera.Key)); + } + codec.CameraSelected += (sender, args) => { var i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); + trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)(i + 1)); + if (codec is IHasCodecRoomPresets) { return; @@ -1325,10 +1716,16 @@ ScreenIndexIsPinnedTo: {8} (a{17}) trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) => { presetCodec.CodecRoomPresetSelect(i); - - trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, i); }); + + // Far End Presets + trilist.SetUShortSigAction(joinMap.FarEndPresetSelect.JoinNumber, (i) => + { + presetCodec.SelectFarEndPreset(i); + }); + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, () => { @@ -1347,7 +1744,96 @@ ScreenIndexIsPinnedTo: {8} (a{17}) }; } - private string SetCameraPresetNames(IEnumerable presets) + // Following fields only used for Bridging + private int _selectedRecentCallItemIndex; + private CodecCallHistory.CallHistoryEntry _selectedRecentCallItem; + private DirectoryItem _selectedDirectoryItem; + + private void LinkVideoCodecCallHistoryToApi(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.CallHistory.RecentCallsListHasChanged += (o, a) => + { + UpdateCallHistory(codec, trilist, joinMap); + }; + + // Selected item action and feedback + trilist.SetUShortSigAction(joinMap.SelectRecentCallItem.JoinNumber, (u) => + { + if (u == 0 || u > codec.CallHistory.RecentCalls.Count) + { + Debug.Console(2, this, "Recent Call History index out of range"); + return; + } + + _selectedRecentCallItemIndex = (int)(u - 1); + trilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, u); + + var _selectedRecentCallItem = codec.CallHistory.RecentCalls[_selectedRecentCallItemIndex]; + + if (_selectedRecentCallItem != null) + { + trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, _selectedRecentCallItem.Name); + trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, _selectedRecentCallItem.Number); + trilist.SetSigFalseAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber, () => codec.RemoveCallHistoryEntry(_selectedRecentCallItem)); + trilist.SetSigFalseAction(joinMap.DialSelectedRecentCallItem.JoinNumber, () => this.Dial(_selectedRecentCallItem.Number)); + } + else + { + trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty); + trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty); + trilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DialSelectedRecentCallItem.JoinNumber); + } + }); + } + + + + private void UpdateCallHistory(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + // Clear out selected item + _selectedRecentCallItemIndex = 0; + _selectedRecentCallItem = null; + trilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, 0); + trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty); + trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty); + trilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber); + // + + trilist.SetUshort(joinMap.RecentCallCount.JoinNumber, (ushort)codec.CallHistory.RecentCalls.Count); + + // Update the call history joins + var maxItems = joinMap.RecentCallNamesStart.JoinSpan; + + // Create history + uint index = 0; + for (uint i = 0; i < maxItems && i < codec.CallHistory.RecentCalls.Count; i++) + { + trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].Name); + trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].StartTime.ToShortTimeString()); + trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)codec.CallHistory.RecentCalls[(int)i].OccurrenceType); + //i++; + index = i; + } + + //foreach(var item in codec.CallHistory.RecentCalls) + //{ + // trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, item.Name); + // trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, item.StartTime.ToShortTimeString()); + // trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)item.OccurrenceType); + // i++; + //} + + // Clears existing items + for (uint j = index; j < maxItems; j++) + { + trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + j, string.Empty); + trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + j, string.Empty); + trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + j, 0); + } + } + + private string SetCameraPresetNames(IEnumerable presets) { return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs index f6355d63..b5296410 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs @@ -31,10 +31,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom protected void NotifyPropertyChanged(string propertyName) { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } + var handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + else + { + Debug.Console(2, "PropertyChanged event is NULL"); + } } #endregion @@ -778,13 +783,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } set { + //Debug.Console(2, "************************************setting value of meetingIsBeingRecorded to: {0}", value); if (value != _meetingIsBeingRecorded) { _meetingIsBeingRecorded = value; + //Debug.Console(2, "********************************set value of meetingIsBeingRecorded to: {0}", _meetingIsBeingRecorded); NotifyPropertyChanged("meetingIsBeingRecorded"); } } } + + public CallRecordInfo() + { + Debug.Console(2, Debug.ErrorLogLevel.Notice, "********************************************* CallRecordInfo() ******************************************"); + } } } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs index 8fc40f29..73c1e8c5 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs @@ -24,16 +24,30 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectoryHistoryStack, ICommunicationMonitor, IRouting, - IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraMute, IHasCameraAutoMode, + IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraMuteWithUnmuteReqeust, IHasCameraAutoMode, IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing, IHasZoomRoomLayouts, IHasParticipantPinUnpin, IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo, IHasPresentationOnlyMeeting, - IHasMeetingLock, IHasMeetingRecording + IHasMeetingLock, IHasMeetingRecordingWithPrompt { + public event EventHandler VideoUnmuteRequested; + private const long MeetingRefreshTimer = 60000; public uint DefaultMeetingDurationMin { get; private set; } - private const string Delimiter = "\x0D\x0A"; + /// + /// CR LF CR LF Delimits an echoed response to a command + /// + private const string EchoDelimiter = "\x0D\x0A\x0D\x0A"; + private const string SendDelimiter = "\x0D"; + + /// + /// CR LF } CR LF Delimits a JSON response + /// + private const string JsonDelimiter = "\x0D\x0A\x7D\x0D\x0A"; + + private string[] Delimiters = new string[] { EchoDelimiter, JsonDelimiter, "OK\x0D\x0A", "end\x0D\x0A" }; + //"echo off\x0D\x0A\x0A\x0D\x0A" private readonly GenericQueue _receiveQueue; //private readonly CrestronQueue _receiveQueue; @@ -59,7 +73,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom _props = JsonConvert.DeserializeObject(config.Properties.ToString()); - _receiveQueue = new GenericQueue(Key + "-rxQueue", Thread.eThreadPriority.MediumPriority, 512); + _receiveQueue = new GenericQueue(Key + "-rxQueue", Thread.eThreadPriority.MediumPriority, 2048); Communication = comm; @@ -71,7 +85,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom else { CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, - "zStatus SystemUnit\r"); + "zStatus SystemUnit" + SendDelimiter); } DeviceManager.AddDevice(CommunicationMonitor); @@ -86,23 +100,31 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom _syncState.InitialSyncCompleted += SyncState_InitialSyncCompleted; + _syncState.FirstJsonResponseReceived += (o, a) => SetUpSyncQueries(); + PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); - PortGather = new CommunicationGather(Communication, "\x0A") {IncludeDelimiter = true}; + PhonebookSyncState.InitialSyncCompleted += (o, a) => ResubscribeForAddedContacts(); + + PortGather = new CommunicationGather(Communication, Delimiters) {IncludeDelimiter = true}; PortGather.LineReceived += Port_LineReceived; CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); - Output1 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, + Output1 = new RoutingOutputPort(RoutingPortNames.HdmiOut1, eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, null, this); - Output2 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, + Output2 = new RoutingOutputPort(RoutingPortNames.HdmiOut2, eRoutingSignalType.Video, eRoutingPortConnectionType.DisplayPort, null, this); + Output3 = new RoutingOutputPort(RoutingPortNames.HdmiOut3, + eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); CameraIsOffFeedback = new BoolFeedback(CameraIsOffFeedbackFunc); @@ -152,6 +174,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom MeetingIsLockedFeedback = new BoolFeedback(() => Configuration.Call.Lock.Enable ); MeetingIsRecordingFeedback = new BoolFeedback(() => Status.Call.CallRecordInfo.meetingIsBeingRecorded ); + + RecordConsentPromptIsVisible = new BoolFeedback(() => _recordConsentPromptIsVisible); + + SetUpRouting(); } public CommunicationGather PortGather { get; private set; } @@ -281,6 +307,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public RoutingInputPort CodecOsdIn { get; private set; } public RoutingOutputPort Output1 { get; private set; } public RoutingOutputPort Output2 { get; private set; } + public RoutingOutputPort Output3 { get; private set; } #region ICommunicationMonitor Members @@ -326,6 +353,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { Debug.Console(1, this, "Selected Camera with key: '{0}'", camera.Key); SelectedCamera = camera; + + if (CameraIsMutedFeedback.BoolValue) + { + CameraMuteOff(); + } } else { @@ -384,10 +416,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { _currentDirectoryResult = value; - Debug.Console(2, this, "CurrentDirectoryResult Updated. ResultsFolderId: {0}", - _currentDirectoryResult.ResultsFolderId); - - CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + Debug.Console(2, this, "CurrentDirectoryResult Updated. ResultsFolderId: {0} Contact Count: {1}", + _currentDirectoryResult.ResultsFolderId, _currentDirectoryResult.CurrentDirectoryResults.Count); OnDirectoryResultReturned(_currentDirectoryResult); } @@ -468,25 +498,63 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private void SyncState_InitialSyncCompleted(object sender, EventArgs e) { - SetUpRouting(); - SetIsReady(); } + /// + /// Handles subscriptions to Status.Call and sub objects. Needs to be called whenever Status.Call is constructed + /// private void SetUpCallFeedbackActions() { - Status.Call.Sharing.PropertyChanged += HandleSharingStateUpdate; + Status.Call.Sharing.PropertyChanged -= HandleSharingStateUpdate; + Status.Call.Sharing.PropertyChanged += HandleSharingStateUpdate; - Status.Call.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Info") - { - Debug.Console(1, this, "Updating Call Status"); - UpdateCallStatus(); - } - }; + Status.Call.PropertyChanged -= HandleCallStateUpdate; + Status.Call.PropertyChanged += HandleCallStateUpdate; + + Status.Call.CallRecordInfo.PropertyChanged -= HandleCallRecordInfoStateUpdate; + Status.Call.CallRecordInfo.PropertyChanged += HandleCallRecordInfoStateUpdate; } + private void HandleCallRecordInfoStateUpdate(object sender, PropertyChangedEventArgs a) + { + if (a.PropertyName == "meetingIsBeingRecorded") + { + MeetingIsRecordingFeedback.FireUpdate(); + + var meetingInfo = new MeetingInfo(MeetingInfo.Id, + MeetingInfo.Name, + MeetingInfo.Host, + MeetingInfo.Password, + GetSharingStatus(), + GetIsHostMyself(), + MeetingInfo.IsSharingMeeting, + MeetingInfo.WaitingForHost, + MeetingIsLockedFeedback.BoolValue, + MeetingIsRecordingFeedback.BoolValue); + MeetingInfo = meetingInfo; + } + } + + private void HandleCallStateUpdate(object sender, PropertyChangedEventArgs a) + { + switch (a.PropertyName) + { + case "Info": + { + Debug.Console(1, this, "Updating Call Status"); + UpdateCallStatus(); + break; + } + + case "Status": + { + UpdateCallStatus(); + break; + } + } + } + private void HandleSharingStateUpdate(object sender, PropertyChangedEventArgs a) { if (a.PropertyName != "State") @@ -504,19 +572,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { var sharingStatus = GetSharingStatus(); - MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true, false, MeetingIsLockedFeedback.BoolValue); + MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true, false, MeetingIsLockedFeedback.BoolValue, MeetingIsRecordingFeedback.BoolValue); return; } var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host != null ? Participants.Host.Name : "None", - MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost, MeetingIsLockedFeedback.BoolValue); + MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost, MeetingIsLockedFeedback.BoolValue, MeetingIsRecordingFeedback.BoolValue); MeetingInfo = meetingInfo; } catch (Exception e) { Debug.Console(1, this, "Error processing state property update. {0}", e.Message); Debug.Console(2, this, e.StackTrace); - MeetingInfo = new MeetingInfo("", "", "", "", "None", false, false, false, MeetingIsLockedFeedback.BoolValue); + MeetingInfo = new MeetingInfo("", "", "", "", "None", false, false, false, MeetingIsLockedFeedback.BoolValue, MeetingIsRecordingFeedback.BoolValue); } } @@ -525,6 +593,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private void SetUpFeedbackActions() { + // Set these up initially. + SetUpCallFeedbackActions(); + Configuration.Audio.Output.PropertyChanged += (o, a) => { if (a.PropertyName == "Volume") @@ -619,7 +690,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom MeetingInfo.IsHost, MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost, - MeetingIsLockedFeedback.BoolValue + MeetingIsLockedFeedback.BoolValue, + MeetingIsRecordingFeedback.BoolValue ); } }; @@ -652,41 +724,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } }; - Status.Call.Sharing.PropertyChanged += (o, a) => - { - if (a.PropertyName == "State") - { - SharingContentIsOnFeedback.FireUpdate(); - ReceivingContent.FireUpdate(); - } - }; - - Status.Call.PropertyChanged += (o, a) => - { - switch(a.PropertyName) - { - case "Info": - { - Debug.Console(1, this, "Updating Call Status"); - UpdateCallStatus(); - break; - } - - case "Status": - { - UpdateCallStatus(); - break; - } - } - }; - - Status.Call.CallRecordInfo.PropertyChanged += (o, a) => - { - if (a.PropertyName == "meetingIsBeingRecorded") - { - MeetingIsRecordingFeedback.FireUpdate(); - } - }; Status.Sharing.PropertyChanged += (o, a) => { @@ -718,7 +755,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost, - MeetingIsLockedFeedback.BoolValue); + MeetingIsLockedFeedback.BoolValue, + MeetingIsRecordingFeedback.BoolValue); MeetingInfo = meetingInfo; break; } @@ -904,7 +942,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Debug.Console(1, this, "Sending: '{0}'", command); } - Communication.SendText(command + Delimiter); + Communication.SendText(command + SendDelimiter); } /// @@ -914,13 +952,28 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) { - //if (CommDebuggingIsOn) - // Debug.Console(1, this, "Gathered: '{0}'", args.Text); + //Debug.Console(0, this, "Port_LineReceived"); - _receiveQueue.Enqueue(new ProcessStringMessage(args.Text, ProcessMessage)); + if (args.Delimiter != JsonDelimiter) + { +// Debug.Console(0, this, +//@"Non JSON response: +//Delimiter: {0} +//{1}", ComTextHelper.GetDebugText(args.Delimiter), args.Text); + ProcessNonJsonResponse(args.Text); + return; + } + else + { +// Debug.Console(0, this, +//@"JSON response: +//Delimiter: {0} +//{1}", ComTextHelper.GetDebugText(args.Delimiter), args.Text); + _receiveQueue.Enqueue(new ProcessStringMessage(args.Text, DeserializeResponse)); + //_receiveQueue.Enqueue(new ProcessStringMessage(args.Text, ProcessMessage)); + } } - /// /// Queues the initial queries to be sent upon connection /// @@ -979,6 +1032,93 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom _syncState.StartSync(); } + private void SetupSession() + { + // disable echo of commands + SendText("echo off"); + // switch to json format + // set feedback exclusions + // Currently the feedback exclusions don't work when using the API in JSON response mode + // But leave these here in case the API gets updated in the future + // These may work as of 5.9.4 + + // In 5.9.4 we're getting sent an AddedContact message for every contact in the phonebook on connect, which is redunant and way too much data + // We want to exclude these messages right away until after we've retrieved the entire phonebook and then we can re-enable them + SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); + + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/callin_country_list"); + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/callout_country_list"); + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/toll_free_callinLlist"); + + SendText("zStatus SystemUnit"); + } + + /// + /// Removes the feedback exclusion for added contacts + /// + private void ResubscribeForAddedContacts() + { + SendText("zFeedback Register Op: in Path: /Event/Phonebook/AddedContact"); + } + + /// + /// Processes non-JSON responses as their are received + /// + /// + private void ProcessNonJsonResponse(string response) + { + if (response.Contains("client_loop: send disconnect: Broken pipe")) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Error, + "Zoom Room Controller or App connected. Essentials will NOT control the Zoom Room until it is disconnected."); + + return; + } + + if (!_syncState.InitialSyncComplete) + { + if(response.ToLower().Contains("*r login successful")) + { + _syncState.LoginResponseReceived(); + + SendText("format json"); + + SetupSession(); + } + + //switch (response.Trim().ToLower()) // remove the whitespace + //{ + // case "*r login successful": + // { + // _syncState.LoginMessageReceived(); + + // //// Fire up a thread to send the intial commands. + // //CrestronInvoke.BeginInvoke(o => + // //{ + // // disable echo of commands + // SendText("echo off"); + // // switch to json format + // SendText("format json"); + // // set feedback exclusions + // // Currently the feedback exclusions don't work when using the API in JSON response mode + // // But leave these here in case the API gets updated in the future + // // These may work as of 5.9.4 + // if (_props.DisablePhonebookAutoDownload) + // { + // SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); + // } + // SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/callin_country_list"); + // SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/callout_country_list"); + // SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/toll_free_callinLlist"); + + // //}); + + // break; + // } + //} + } + } + /// /// Processes messages as they are dequeued /// @@ -1006,7 +1146,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom //Debug.Console(2, this, "JSON Curly Brace Count: {0}", _jsonCurlyBraceCounter); - if (!_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + Delimiter) + if (!_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + EchoDelimiter) // Check for the beginning of a new JSON message { _jsonFeedbackMessageIsIncoming = true; @@ -1023,7 +1163,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom return; } - if (_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) + if (_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + EchoDelimiter) // Check for the end of a JSON message { _jsonMessage.Append(message); @@ -1068,7 +1208,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { case "*r login successful": { - _syncState.LoginMessageReceived(); + _syncState.LoginResponseReceived(); // Fire up a thread to send the intial commands. @@ -1090,7 +1230,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom SendText("zFeedback Register Op: ex Path: /Event/InfoResult/Info/toll_free_callinLlist"); Thread.Sleep(100); - if (!_props.DisablePhonebookAutoDownload) + if (_props.DisablePhonebookAutoDownload) { SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); } @@ -1121,6 +1261,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom var message = JObject.Parse(trimmedResponse); + if (!_syncState.FirstJsonResponseWasReceived) + { + _syncState.ReceivedFirstJsonResponse(); + } + var eType = (eZoomRoomResponseType) Enum.Parse(typeof (eZoomRoomResponseType), message["type"].Value(), true); @@ -1129,7 +1274,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom var responseObj = message[topKey]; - Debug.Console(1, "{0} Response Received. topKey: '{1}'\n{2}", eType, topKey, responseObj.ToString()); + Debug.Console(1, this, "{0} Response Received. topKey: '{1}'\n{2}", eType, topKey, responseObj.ToString().Replace("\n", CrestronEnvironment.NewLine)); switch (eType) { @@ -1182,6 +1327,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom // This result will always be the complete contents of the directory and never // A subset of the results via a search + // Clear out any existing data + Status.Phonebook = new zStatus.Phonebook(); + JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); var directoryResults = @@ -1195,10 +1343,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); } - if (directoryResults.ResultsFolderId != "root") - { - directoryResults.ResultsFolderId = "root"; - } + directoryResults.ResultsFolderId = "root"; DirectoryRoot = directoryResults; @@ -1308,7 +1453,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost, - MeetingIsLockedFeedback.BoolValue); + MeetingIsLockedFeedback.BoolValue, + MeetingIsRecordingFeedback.BoolValue); MeetingInfo = meetingInfo; PrintCurrentCallParticipants(); @@ -1485,7 +1631,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } case "videounmuterequest": { - // TODO: notify room of a request to unmute video + var handler = VideoUnmuteRequested; + + if (handler != null) + { + handler(this, null); + } + break; } case "meetingneedspassword": @@ -1517,14 +1669,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (MeetingInfo == null) { MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", - GetSharingStatus(), false, false, true, MeetingIsLockedFeedback.BoolValue); + GetSharingStatus(), false, false, true, MeetingIsLockedFeedback.BoolValue, MeetingIsRecordingFeedback.BoolValue); UpdateCallStatus(); break; } MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", - GetSharingStatus(), false, false, true, MeetingIsLockedFeedback.BoolValue); + GetSharingStatus(), false, false, true, MeetingIsLockedFeedback.BoolValue, MeetingIsRecordingFeedback.BoolValue); UpdateCallStatus(); @@ -1534,12 +1686,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (MeetingInfo == null) { MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", - GetSharingStatus(), false, false, false, MeetingIsLockedFeedback.BoolValue); + GetSharingStatus(), false, false, false, MeetingIsLockedFeedback.BoolValue, MeetingIsRecordingFeedback.BoolValue); break; } MeetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, - GetSharingStatus(), GetIsHostMyself(), false, false, MeetingIsLockedFeedback.BoolValue); + GetSharingStatus(), GetIsHostMyself(), false, false, MeetingIsLockedFeedback.BoolValue, MeetingIsRecordingFeedback.BoolValue); break; } @@ -1548,12 +1700,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom // TODO: notify user that host has disabled unmuting video break; } - case "updatedcallrecordinfo": + case "updatecallrecordinfo": { JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); break; } + case "recordingconsent": + { + _recordConsentPromptIsVisible = responseObj["isShow"].Value(); + RecordConsentPromptIsVisible.FireUpdate(); + break; + } case "phonecallstatus": { JsonConvert.PopulateObject(responseObj.ToString(), Status.PhoneCall); @@ -1623,7 +1781,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (result.Success) { - MeetingInfo = new MeetingInfo("", "", "", "", "", true, true, MeetingInfo.WaitingForHost, MeetingIsLockedFeedback.BoolValue); + MeetingInfo = new MeetingInfo("", "", "", "", "", true, true, MeetingInfo.WaitingForHost, MeetingIsLockedFeedback.BoolValue, MeetingIsRecordingFeedback.BoolValue); break; } @@ -1642,19 +1800,17 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { case "login": { - _syncState.LoginMessageReceived(); + _syncState.LoginResponseReceived(); - if (!_syncState.InitialQueryMessagesWereSent) - { - SetUpSyncQueries(); - } + SetupSession(); JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); break; } case "systemunit": - { + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); break; @@ -1720,6 +1876,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } case "video camera line": { + Status.Cameras.Clear(); + JsonConvert.PopulateObject(responseObj.ToString(), Status.Cameras); if (!_syncState.CamerasHaveBeenSetUp) @@ -1824,12 +1982,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom // If not crrently in a meeting, intialize the call object if (callStatus != zStatus.eCallStatus.IN_MEETING && callStatus != zStatus.eCallStatus.CONNECTING_MEETING) { - //Debug.Console(1, this, "[UpdateCallStatus] Creating new Status.Call object"); Status.Call = new zStatus.Call {Status = callStatus}; + // Resubscribe to all property change events after Status.Call is reconstructed + SetUpCallFeedbackActions(); OnCallStatusChange(new CodecActiveCallItem() {Status = eCodecCallStatus.Disconnected}); - - SetUpCallFeedbackActions(); } if (ActiveCalls.Count == 0) @@ -1969,7 +2126,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom GetIsHostMyself(), !String.Equals(Status.Call.Info.meeting_type,"NORMAL"), false, - MeetingIsLockedFeedback.BoolValue + MeetingIsLockedFeedback.BoolValue, + MeetingIsRecordingFeedback.BoolValue ); } // TODO [ ] Issue #868 @@ -1984,6 +2142,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom false, false, false, + false, false ); } @@ -2209,6 +2368,21 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public void LinkZoomRoomToApi(BasicTriList trilist, ZoomRoomJoinMap joinMap) { + var recordingCodec = this as IHasMeetingRecordingWithPrompt; + if (recordingCodec != null) + { + trilist.SetSigFalseAction(joinMap.StartRecording.JoinNumber, () => recordingCodec.StartRecording()); + trilist.SetSigFalseAction(joinMap.StopRecording.JoinNumber, () => recordingCodec.StopRecording()); + + recordingCodec.MeetingIsRecordingFeedback.LinkInputSig(trilist.BooleanInput[joinMap.StartRecording.JoinNumber]); + recordingCodec.MeetingIsRecordingFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.StopRecording.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.RecordingPromptAgree.JoinNumber, () => recordingCodec.RecordingPromptAcknowledgement(true)); + trilist.SetSigFalseAction(joinMap.RecordingPromptDisagree.JoinNumber, () => recordingCodec.RecordingPromptAcknowledgement(false)); + + recordingCodec.RecordConsentPromptIsVisible.LinkInputSig(trilist.BooleanInput[joinMap.RecordConsentPromptIsVisible.JoinNumber]); + } + var layoutsCodec = this as IHasZoomRoomLayouts; if (layoutsCodec != null) { @@ -2295,29 +2469,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom }); layoutSizeCodec.SelfviewPipSizeFeedback.LinkInputSig(trilist.StringInput[joinMap.GetSetSelfviewPipSize.JoinNumber]); - } - - PasswordRequired += (device, args) => - { - if (args.LoginAttemptCancelled) - { - trilist.SetBool(joinMap.ShowPasswordPrompt.JoinNumber, false); - return; - } - - if (!string.IsNullOrEmpty(args.Message)) - { - trilist.SetString(joinMap.PasswordPromptMessage.JoinNumber, args.Message); - } - - if (args.LoginAttemptFailed) - { - trilist.SetBool(joinMap.PasswordLoginFailed.JoinNumber, true); - return; - } - - trilist.SetBool(joinMap.ShowPasswordPrompt.JoinNumber, true); - }; + } MeetingInfoChanged += (device, args) => { @@ -2575,27 +2727,27 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { try { - Debug.Console(2, this, "OnDirectoryResultReturned"); + Debug.Console(2, this, "OnDirectoryResultReturned. Result has {0} contacts", result.Contacts.Count); - var directoryResult = new CodecDirectory(); + var directoryResult = result; // If result is Root, create a copy and filter out contacts whose parent folder is not root - if (!CurrentDirectoryResultIsNotDirectoryRoot.BoolValue) - { - Debug.Console(2, this, "Filtering DirectoryRoot to remove contacts for display"); + //if (!CurrentDirectoryResultIsNotDirectoryRoot.BoolValue) + //{ + // Debug.Console(2, this, "Filtering DirectoryRoot to remove contacts for display"); - directoryResult.ResultsFolderId = result.ResultsFolderId; - directoryResult.AddFoldersToDirectory(result.Folders); - directoryResult.AddContactsToDirectory( - result.Contacts.Where((c) => c.ParentFolderId == result.ResultsFolderId).ToList()); - } - else - { - directoryResult = result; - } + // directoryResult.ResultsFolderId = result.ResultsFolderId; + // directoryResult.AddFoldersToDirectory(result.Folders); + // directoryResult.AddContactsToDirectory( + // result.Contacts.Where((c) => c.ParentFolderId == result.ResultsFolderId).ToList()); + //} + //else + //{ + // directoryResult = result; + //} - Debug.Console(2, this, "Updating directoryResult. IsOnRoot: {0}", - !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + Debug.Console(2, this, "Updating directoryResult. IsOnRoot: {0} Contact Count: {1}", + !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue, directoryResult.Contacts.Count); // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology var handler = DirectoryResultReturned; @@ -2607,6 +2759,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue }); } + + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); } catch (Exception e) { @@ -2637,14 +2791,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom continue; } - var camera = new ZoomRoomCamera(cam.id, cam.Name, this); + var existingCam = Cameras.FirstOrDefault((c) => c.Key.Equals(cam.id)); - Cameras.Add(camera); + if (existingCam == null) + { + var camera = new ZoomRoomCamera(cam.id, cam.Name, this); - if (cam.Selected) - { - SelectedCamera = camera; - } + Cameras.Add(camera); + + if (cam.Selected) + { + SelectedCamera = camera; + } + } } if (IsInCall) @@ -2677,6 +2836,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom SendText(string.Format("zCommand Call HostChange Id: {0}", userId)); } + public void AdmitParticipantFromWaitingRoom(int userId) + { + SendText(string.Format("zCommand Call Admit Participant Id: {0}", userId)); + } + #endregion #region IHasParticipantAudioMute Members @@ -3060,7 +3224,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom handler(this, new LayoutInfoChangedEventArgs() { AvailableLayouts = AvailableLayouts, - CurrentSelectedLayout = (zConfiguration.eLayoutStyle)Enum.Parse(typeof(zConfiguration.eLayoutStyle),LocalLayoutFeedback.StringValue, true), + CurrentSelectedLayout = (zConfiguration.eLayoutStyle)Enum.Parse(typeof(zConfiguration.eLayoutStyle),string.IsNullOrEmpty(LocalLayoutFeedback.StringValue) ? "None" : LocalLayoutFeedback.StringValue , true), LayoutViewIsOnFirstPage = LayoutViewIsOnFirstPageFeedback.BoolValue, LayoutViewIsOnLastPage = LayoutViewIsOnLastPageFeedback.BoolValue, CanSwapContentWithThumbnail = CanSwapContentWithThumbnailFeedback.BoolValue, @@ -3287,18 +3451,29 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom #endregion - #region IHasMeetingRecording Members + #region IHasMeetingRecordingWithPrompt Members public BoolFeedback MeetingIsRecordingFeedback { get; private set; } + bool _recordConsentPromptIsVisible; + + public BoolFeedback RecordConsentPromptIsVisible { get; private set; } + + public void RecordingPromptAcknowledgement(bool agree) + { + var command = string.Format("zCommand Agree Recording: {0}", agree ? "on" : "off"); + //Debug.Console(2, this, "Sending agree: {0} {1}", agree, command); + SendText(command); + } + public void StartRecording() { - SendText(string.Format("Command Call Record Enable: on")); + SendText(string.Format("zCommand Call Record Enable: on")); } public void StopRecording() { - SendText(string.Format("Command Call Record Enable: off")); + SendText(string.Format("zCommand Call Record Enable: off")); } public void ToggleRecording() @@ -3437,7 +3612,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } - public bool LoginMessageWasReceived { get; private set; } + public bool LoginResponseWasReceived { get; private set; } + + public bool FirstJsonResponseWasReceived { get; private set; } public bool InitialQueryMessagesWereSent { get; private set; } @@ -3453,6 +3630,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public event EventHandler InitialSyncCompleted; + public event EventHandler FirstJsonResponseReceived; + public void StartSync() { DequeueQueries(); @@ -3475,13 +3654,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom _syncQueries.Enqueue(query); } - public void LoginMessageReceived() + public void LoginResponseReceived() { - LoginMessageWasReceived = true; - Debug.Console(1, this, "Login Message Received."); + LoginResponseWasReceived = true; + Debug.Console(1, this, "Login Rsponse Received."); CheckSyncStatus(); } + public void ReceivedFirstJsonResponse() + { + FirstJsonResponseWasReceived = true; + Debug.Console(1, this, "First JSON Response Received."); + + var handler = FirstJsonResponseReceived; + if (handler != null) + { + handler(this, null); + } + CheckSyncStatus(); + } + public void InitialQueryMessagesSent() { InitialQueryMessagesWereSent = true; @@ -3506,7 +3698,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public void CodecDisconnected() { _syncQueries.Clear(); - LoginMessageWasReceived = false; + LoginResponseWasReceived = false; + FirstJsonResponseWasReceived = false; InitialQueryMessagesWereSent = false; LastQueryResponseWasReceived = false; CamerasHaveBeenSetUp = false; @@ -3515,7 +3708,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private void CheckSyncStatus() { - if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && + if (LoginResponseWasReceived && FirstJsonResponseWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && CamerasHaveBeenSetUp) { InitialSyncComplete = true; diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs index b0fc52fe..4b723ec8 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs @@ -7,6 +7,8 @@ using Crestron.SimplSharpPro.DeviceSupport; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Devices.Common.Cameras; +using Newtonsoft.Json; + namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { public enum eZoomRoomCameraState @@ -34,7 +36,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { protected ZoomRoom ParentCodec { get; private set; } - public int Id = 0; // ID of near end selected camara is always 0 + [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] + public int? Id = 0; // ID of near end selected camara is always 0 private int ContinueTime = 10; // number of milliseconds between issuing continue commands diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomJoinMap.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomJoinMap.cs index b977cfe3..48b2c039 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomJoinMap.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomJoinMap.cs @@ -295,7 +295,77 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Description = "Toggles the selfview pip size, (aka layout size)", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital - }); + }); + + [JoinName("StartRecording")] + public JoinDataComplete StartRecording = new JoinDataComplete( + new JoinData + { + JoinNumber = 241, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to start the Meeting Recording. FB high if meeting is currently recording", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("StopRecording")] + public JoinDataComplete StopRecording = new JoinDataComplete( + new JoinData + { + JoinNumber = 242, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to stop the Meeting Recording. FB high if meeting is currently NOT recording", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("RecordConsentPromptIsVisible")] + public JoinDataComplete RecordConsentPromptIsVisible = new JoinDataComplete( + new JoinData + { + JoinNumber = 243, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "When high, indicates that the recording consent prompt is visible on the ZoomRoom UI", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("RecordingPromptAgree")] + public JoinDataComplete RecordingPromptAgree = new JoinDataComplete( + new JoinData + { + JoinNumber = 244, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to agree to consent for meeting recording", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("RecordingPromptDisagree")] + public JoinDataComplete RecordingPromptDisagree = new JoinDataComplete( + new JoinData + { + JoinNumber = 245, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to disagree to consent for meeting recording", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); //[JoinName("ParticipantAudioMuteToggleStart")] //public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete( diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs index 4c9b08c2..3f752f43 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs @@ -2,30 +2,40 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Crestron.SimplSharp; - +using Crestron.SimplSharp; +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { public class ZoomRoomPropertiesConfig - { - public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } - - public bool DisablePhonebookAutoDownload { get; set; } - public bool SupportsCameraAutoMode { get; set; } + { + [JsonProperty("communicationMonitorProperties")] + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + [JsonProperty("disablePhonebookAutoDownload")] + public bool DisablePhonebookAutoDownload { get; set; } + + [JsonProperty("supportsCameraAutoMode")] + public bool SupportsCameraAutoMode { get; set; } + + [JsonProperty("supportsCameraOff")] public bool SupportsCameraOff { get; set; } - //if true, the layouts will be set automatically when sharing starts/ends or a call is joined + //if true, the layouts will be set automatically when sharing starts/ends or a call is joined + [JsonProperty("autoDefaultLayouts")] public bool AutoDefaultLayouts { get; set; } - /* This layout will be selected when Sharing starts (either from Far end or locally)*/ + /* This layout will be selected when Sharing starts (either from Far end or locally)*/ + [JsonProperty("defaultSharingLayout")] public string DefaultSharingLayout { get; set; } - //This layout will be selected when a call is connected and no content is being shared - public string DefaultCallLayout { get; set; } - + //This layout will be selected when a call is connected and no content is being shared + [JsonProperty("defaultCallLayout")] + public string DefaultCallLayout { get; set; } + + [JsonProperty("minutesBeforeMeetingStart")] public int MinutesBeforeMeetingStart { get; set; } } } \ No newline at end of file diff --git a/packages.config b/packages.config index 10124bbd..4c411add 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file