diff --git a/IR Drivers/Apple_AppleTV_4th_Gen_Essentials.ir b/IR Drivers/Apple_AppleTV_4th_Gen_Essentials.ir new file mode 100644 index 00000000..f112ee8b Binary files /dev/null and b/IR Drivers/Apple_AppleTV_4th_Gen_Essentials.ir differ diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index b454cec9..be78aaf9 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -506,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 with IP-ID {0:X2}", fusionIpId); - 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..."); diff --git a/PepperDashEssentials/Example Configuration/EssentialsHuddleSpaceRoom/configurationFile-HuddleSpace-2-Source.json b/PepperDashEssentials/Example Configuration/EssentialsHuddleSpaceRoom/configurationFile-HuddleSpace-2-Source.json index 3e43dac6..d3ce3275 100644 --- a/PepperDashEssentials/Example Configuration/EssentialsHuddleSpaceRoom/configurationFile-HuddleSpace-2-Source.json +++ b/PepperDashEssentials/Example Configuration/EssentialsHuddleSpaceRoom/configurationFile-HuddleSpace-2-Source.json @@ -88,7 +88,7 @@ { "name": "Wireless Video", "key": "wePresent-1", - "type": "wePresent", + "type": "genericSource", "group": "genericSource", "uid": 9, "properties": { diff --git a/PepperDashEssentials/Example Configuration/EssentialsHuddleVtc1Room/configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json b/PepperDashEssentials/Example Configuration/EssentialsHuddleVtc1Room/configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json index 7cc97c47..1e5b66d2 100644 --- a/PepperDashEssentials/Example Configuration/EssentialsHuddleVtc1Room/configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json +++ b/PepperDashEssentials/Example Configuration/EssentialsHuddleVtc1Room/configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json @@ -114,7 +114,7 @@ { "name": "Wireless Video", "key": "wePresent-1", - "type": "wePresent", + "type": "genericSource", "properties": {}, "group": "genericSource", "uid": 3 diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index d172076a..e4a0cff6 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -144,6 +144,7 @@ + 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 5ec0565d..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); } @@ -147,6 +151,24 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("helpMessage")] public string HelpMessage { get; set; } + /// + /// Read this value to get the help message. It checks for the old and new config format. + /// + public string HelpMessageForDisplay + { + get + { + if(Help != null && !string.IsNullOrEmpty(Help.Message)) + { + return Help.Message; + } + else + { + return HelpMessage; + } + } + } + [JsonProperty("environment")] public EssentialsEnvironmentPropertiesConfig Environment { get; set; } @@ -183,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/EssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs index 3123f20f..5dbe7600 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs @@ -202,11 +202,28 @@ namespace PepperDash.Essentials }; } + SetupEnvironmentalControlDevices(); + SetSourceListKey(); EnablePowerOnToLastSource = true; } + private void SetupEnvironmentalControlDevices() + { + if (PropertiesConfig.Environment != null) + { + if (PropertiesConfig.Environment.Enabled) + { + foreach (var d in PropertiesConfig.Environment.DeviceKeys) + { + var envDevice = DeviceManager.GetDeviceForKey(d) as EssentialsDevice; + EnvironmentalControlDevices.Add(envDevice); + } + } + } + } + private void SetSourceListKey() { if (!string.IsNullOrEmpty(PropertiesConfig.SourceListKey)) diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index f2b17526..ba4567dc 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -210,21 +210,29 @@ 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; - + if (VideoCodec == null) - throw new ArgumentNullException("codec cannot be null"); - + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "No Video Codec set. Please check 'videoCodecKey' property in room config"); + throw new ArgumentNullException("VideoCodec 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; + if (DefaultAudioDevice == null) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "No Default Audio Device set. Please check 'defaultAudioKey' property in room config"); + throw new ArgumentNullException("DefaultAudioDevice cannot be null"); + } InitializeRoom(); } @@ -326,6 +334,8 @@ namespace PepperDash.Essentials CallTypeFeedback = new IntFeedback(() => 0); + SetupEnvironmentalControlDevices(); + SetSourceListKey(); EnablePowerOnToLastSource = true; @@ -336,6 +346,21 @@ namespace PepperDash.Essentials } } + private void SetupEnvironmentalControlDevices() + { + if (PropertiesConfig.Environment != null) + { + if (PropertiesConfig.Environment.Enabled) + { + foreach (var d in PropertiesConfig.Environment.DeviceKeys) + { + var envDevice = DeviceManager.GetDeviceForKey(d) as EssentialsDevice; + EnvironmentalControlDevices.Add(envDevice); + } + } + } + } + private void SetSourceListKey() { @@ -419,6 +444,14 @@ namespace PepperDash.Essentials /// public bool RunDefaultCallRoute() { + Debug.Console(2, this, "RunDefaultCallRoute() Currently Sharing Content: {0}", VideoCodec.SharingContentIsOnFeedback.BoolValue); + + if (VideoCodec.SharingContentIsOnFeedback.BoolValue) + { + Debug.Console(2, this, "Currently sharing content. Ignoring request to run default call route."); + return false; + } + RunRouteAction(DefaultCodecRouteString); return true; } diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index d33c3707..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; } @@ -375,13 +375,13 @@ Params: {2}" { bridge.AddJoinMap(Key, joinMap); } - uint i; - 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/IEssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs index 5a6e9f59..03f7340b 100644 --- a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs @@ -12,6 +12,8 @@ namespace PepperDash.Essentials { EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; } + bool ExcludeFromGlobalFunctions { get; } + void RunRouteAction(string routeKey); IHasScheduleAwareness ScheduleSource { get; } diff --git a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs index 9a8006e5..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 @@ -213,7 +408,7 @@ namespace PepperDash.Essentials { public EssentialsTouchpanelControllerFactory() { - TypeNames = new List() { "tsw550", "tsw750", "tsw1050", "tsw560", "tsw760", "tsw1060", "tsw570", "tsw770", "ts770", "tsw1070", "ts1070", "xpanel" }; + TypeNames = new List() { "crestronapp", "tsw550", "tsw750", "tsw1050", "tsw560", "tsw760", "tsw1060", "tsw570", "tsw770", "ts770", "tsw1070", "ts1070", "xpanel" }; } public override EssentialsDevice BuildDevice(DeviceConfig dc) @@ -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/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 26bdaee9..44091362 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(); }); diff --git a/PepperDashEssentials/UIDrivers/ScreenSaverController.cs b/PepperDashEssentials/UIDrivers/ScreenSaverController.cs index e6d60d5b..0f75e2a2 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/README.md b/README.md index efb706c0..0fe4f075 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,10 @@ Utilization of Essentials Framework falls into the following categories: For detailed documentation, see the [Wiki](https://github.com/PepperDash/EssentialsFramework/wiki). +## Support + +* Check out our [Discord Server](https://discord.gg/rWyeRH3K) + ## How-To (Getting Started) See [Getting Started](https://github.com/PepperDash/Essentials/wiki/Get-started#how-to-get-started) 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/DmChassisControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs index 60973801..3582e6cf 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs @@ -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 80975338..44917899 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs @@ -4,6 +4,10 @@ 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 }); @@ -12,6 +16,14 @@ namespace PepperDash.Essentials.Core.Bridges 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 }); @@ -40,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 }); 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 8f7e8188..07c5c4bc 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs @@ -1145,7 +1145,22 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps - #region Analog + #region Analog + + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + [JoinName("MeetingsToDisplay")] + public JoinDataComplete MeetingsToDisplay = new JoinDataComplete( + new JoinData + { + JoinNumber = 40, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Set/FB the number of meetings to display via the bridge xsig; default: 3 meetings.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); [JoinName("SelectCall")] public JoinDataComplete SelectCall = new JoinDataComplete( 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 IO/Relay/GenericRelayDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/GenericRelayDevice.cs index 445ac338..69c588ae 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/GenericRelayDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/GenericRelayDevice.cs @@ -44,6 +44,12 @@ namespace PepperDash.Essentials.Core.CrestronIO { RelayOutput = postActivationFunc(config); + if (RelayOutput == null) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Unable to get parent relay device for device key {0} and port {1}", config.PortDeviceKey, config.PortNumber); + return; + } + RelayOutput.Register(); RelayOutput.StateChange += RelayOutput_StateChange; @@ -61,33 +67,36 @@ namespace PepperDash.Essentials.Core.CrestronIO { if (!Global.ControlSystem.SupportsRelay) { - Debug.Console(0, "GetRelayDevice: Processor does not support relays"); + Debug.Console(0, "Processor does not support relays"); return null; } relayDevice = Global.ControlSystem; + + return relayDevice.RelayPorts[dc.PortNumber]; } - else + + var essentialsDevice = DeviceManager.GetDeviceForKey(dc.PortDeviceKey); + if (essentialsDevice == null) { - var relayDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IRelayPorts; - if (relayDev == null) - { - Debug.Console(0, "GetRelayDevice: Device {0} is not a valid device", dc.PortDeviceKey); - return null; - } - relayDevice = relayDev; - } - if (relayDevice == null) - { - Debug.Console(0, "GetRelayDevice: Device '0' is not a valid IRelayPorts Device", dc.PortDeviceKey); + Debug.Console(0, "Device {0} was not found in Device Manager. Check configuration or for errors with device.", dc.PortDeviceKey); return null; } - if (dc.PortNumber > relayDevice.NumberOfRelayPorts) - { - Debug.Console(0, "GetRelayDevice: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); - } + relayDevice = essentialsDevice as IRelayPorts; - return relayDevice.RelayPorts[dc.PortNumber]; + if (relayDevice == null) + { + Debug.Console(0, "Device {0} is not a valid relay parent. Please check configuration.", dc.PortDeviceKey); + return null; + } + + if (dc.PortNumber <= relayDevice.NumberOfRelayPorts) + { + return relayDevice.RelayPorts[dc.PortNumber]; + } + + Debug.Console(0, "Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); + return null; } #endregion @@ -200,58 +209,6 @@ namespace PepperDash.Essentials.Core.CrestronIO var portDevice = new GenericRelayDevice(dc.Key, dc.Name, GetRelay, props); return portDevice; - - - /* - if (props.PortDeviceKey == "processor") - portDevice = Global.ControlSystem as IRelayPorts; - else - portDevice = DeviceManager.GetDeviceForKey(props.PortDeviceKey) as IRelayPorts; - - if (portDevice == null) - Debug.Console(0, "Unable to add relay device with key '{0}'. Port Device does not support relays", key); - else - { - var cs = (portDevice as CrestronControlSystem); - - if (cs != null) - { - // The relay is on a control system processor - if (!cs.SupportsRelay || props.PortNumber > cs.NumberOfRelayPorts) - { - Debug.Console(0, "Port Device: {0} does not support relays or does not have enough relays"); - return null; - } - } - else - { - // The relay is on another device type - - if (props.PortNumber > portDevice.NumberOfRelayPorts) - { - Debug.Console(0, "Port Device: {0} does not have enough relays"); - return null; - } - } - - Relay relay = portDevice.RelayPorts[props.PortNumber]; - - if (!relay.Registered) - { - if (relay.Register() == eDeviceRegistrationUnRegistrationResponse.Success) - return new GenericRelayDevice(key, relay); - else - Debug.Console(0, "Attempt to register relay {0} on device with key '{1}' failed.", props.PortNumber, props.PortDeviceKey); - } - else - { - return new GenericRelayDevice(key, relay); - } - - // Future: Check if portDevice is 3-series card or other non control system that supports versiports - } - */ - } } 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/Lighting/LightingBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/LightingBase.cs index 1bf8aee8..569d2005 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/LightingBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/LightingBase.cs @@ -1,83 +1,83 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.DeviceSupport; -using Newtonsoft.Json; -using PepperDash.Core; -using PepperDash.Essentials.Core.Bridges; - -namespace PepperDash.Essentials.Core.Lighting -{ - public abstract class LightingBase : EssentialsBridgeableDevice, ILightingScenes - { - #region ILightingScenes Members - - public event EventHandler LightingSceneChange; - - public List LightingScenes { get; protected set; } - - public LightingScene CurrentLightingScene { get; protected set; } - - public IntFeedback CurrentLightingSceneFeedback { get; protected set; } - - #endregion - - protected LightingBase(string key, string name) - : base(key, name) - { - LightingScenes = new List(); - - CurrentLightingScene = new LightingScene(); - //CurrentLightingSceneFeedback = new IntFeedback(() => { return int.Parse(this.CurrentLightingScene.ID); }); - } - - public abstract void SelectScene(LightingScene scene); - - public void SimulateSceneSelect(string sceneName) - { - Debug.Console(1, this, "Simulating selection of scene '{0}'", sceneName); - - var scene = LightingScenes.FirstOrDefault(s => s.Name.Equals(sceneName)); - - if (scene != null) - { - CurrentLightingScene = scene; - OnLightingSceneChange(); - } - } - - /// - /// Sets the IsActive property on each scene and fires the LightingSceneChange event - /// - protected void OnLightingSceneChange() - { - foreach (var scene in LightingScenes) - { - if (scene == CurrentLightingScene) - scene.IsActive = true; - - else - scene.IsActive = false; - } - - var handler = LightingSceneChange; - if (handler != null) - { - handler(this, new LightingSceneChangeEventArgs(CurrentLightingScene)); - } - } - - protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, uint joinStart, - string joinMapKey, EiscApiAdvanced bridge) +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDash.Essentials.Core.Lighting +{ + public abstract class LightingBase : EssentialsBridgeableDevice, ILightingScenes + { + #region ILightingScenes Members + + public event EventHandler LightingSceneChange; + + public List LightingScenes { get; protected set; } + + public LightingScene CurrentLightingScene { get; protected set; } + + public IntFeedback CurrentLightingSceneFeedback { get; protected set; } + + #endregion + + protected LightingBase(string key, string name) + : base(key, name) { - var joinMap = new GenericLightingJoinMap(joinStart); - - var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); - - if (!string.IsNullOrEmpty(joinMapSerialized)) + LightingScenes = new List(); + + CurrentLightingScene = new LightingScene(); + //CurrentLightingSceneFeedback = new IntFeedback(() => { return int.Parse(this.CurrentLightingScene.ID); }); + } + + public abstract void SelectScene(LightingScene scene); + + public void SimulateSceneSelect(string sceneName) + { + Debug.Console(1, this, "Simulating selection of scene '{0}'", sceneName); + + var scene = LightingScenes.FirstOrDefault(s => s.Name.Equals(sceneName)); + + if (scene != null) + { + CurrentLightingScene = scene; + OnLightingSceneChange(); + } + } + + /// + /// Sets the IsActive property on each scene and fires the LightingSceneChange event + /// + protected void OnLightingSceneChange() + { + foreach (var scene in LightingScenes) + { + if (scene == CurrentLightingScene) + scene.IsActive = true; + + else + scene.IsActive = false; + } + + var handler = LightingSceneChange; + if (handler != null) + { + handler(this, new LightingSceneChangeEventArgs(CurrentLightingScene)); + } + } + + protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, uint joinStart, + string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new GenericLightingJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) joinMap = JsonConvert.DeserializeObject(joinMapSerialized); if (bridge != null) @@ -87,52 +87,57 @@ namespace PepperDash.Essentials.Core.Lighting else { Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); - } - - Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - - Debug.Console(0, "Linking to Lighting Type {0}", lightingDevice.GetType().Name.ToString()); - + } + + Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + + Debug.Console(0, "Linking to Lighting Type {0}", lightingDevice.GetType().Name.ToString()); + // GenericLighitng Actions & FeedBack - trilist.SetUShortSigAction(joinMap.SelectScene.JoinNumber, u => lightingDevice.SelectScene(lightingDevice.LightingScenes[u])); - - var sceneIndex = 0; - foreach (var scene in lightingDevice.LightingScenes) + trilist.SetUShortSigAction(joinMap.SelectScene.JoinNumber, u => lightingDevice.SelectScene(lightingDevice.LightingScenes[u])); + + var sceneIndex = 0; + foreach (var scene in lightingDevice.LightingScenes) { - trilist.SetSigTrueAction((uint)(joinMap.SelectSceneDirect.JoinNumber + sceneIndex), () => lightingDevice.SelectScene(lightingDevice.LightingScenes[sceneIndex])); + trilist.SetSigTrueAction((uint)(joinMap.SelectSceneDirect.JoinNumber + sceneIndex), () => lightingDevice.SelectScene(lightingDevice.LightingScenes[sceneIndex])); scene.IsActiveFeedback.LinkInputSig(trilist.BooleanInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + sceneIndex)]); - trilist.StringInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + sceneIndex)].StringValue = scene.Name; - trilist.BooleanInput[(uint)(joinMap.ButtonVisibility.JoinNumber + sceneIndex)].BoolValue = true; - sceneIndex++; - } - - return joinMap; - } - - } - - public class LightingScene - { - public string Name { get; set; } - public string ID { get; set; } - bool _IsActive; - public bool IsActive - { - get - { - return _IsActive; - } - set - { - _IsActive = value; - IsActiveFeedback.FireUpdate(); - } - } - public BoolFeedback IsActiveFeedback { get; set; } - - public LightingScene() - { - IsActiveFeedback = new BoolFeedback(new Func(() => IsActive)); - } - } + trilist.StringInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + sceneIndex)].StringValue = scene.Name; + trilist.BooleanInput[(uint)(joinMap.ButtonVisibility.JoinNumber + sceneIndex)].BoolValue = true; + sceneIndex++; + } + + return joinMap; + } + + } + + public class LightingScene + { + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] + public string Name { get; set; } + [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] + public string ID { get; set; } + bool _IsActive; + [JsonProperty("isActive", NullValueHandling = NullValueHandling.Ignore)] + public bool IsActive + { + get + { + return _IsActive; + } + set + { + _IsActive = value; + IsActiveFeedback.FireUpdate(); + } + } + + [JsonIgnore] + public BoolFeedback IsActiveFeedback { get; set; } + + public LightingScene() + { + IsActiveFeedback = new BoolFeedback(new Func(() => IsActive)); + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs index 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/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 752466d4..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 @@ + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs index 8dca9803..9080435e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs @@ -139,7 +139,8 @@ namespace PepperDash.Essentials.Core.Queues _queue = new CrestronQueue(cap); _worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running) { - Priority = priority + Priority = priority, + Name = _key }; SetDelayValues(pacing); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs index 6d80913f..820d363f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs @@ -54,13 +54,20 @@ namespace PepperDash.Essentials.Core SetupPartitionStateProviders(); SetRooms(); + + if (isInAutoMode) + { + DetermineRoomCombinationScenario(); + } + else + { + SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey); + } }); } void CreateScenarios() { - RoomCombinationScenarios = new List(); - foreach (var scenarioConfig in _propertiesConfig.Scenarios) { var scenario = new RoomCombinationScenario(scenarioConfig); @@ -160,12 +167,26 @@ namespace PepperDash.Essentials.Core { return _currentScenario; } - set + private set { if (value != _currentScenario) { + // Deactivate the old scenario first + if (_currentScenario != null) + { + _currentScenario.Deactivate(); + } + _currentScenario = value; - Debug.Console(1, this, "Current Scenario: {0}", _currentScenario.Name); + + // Activate the new scenario + if (_currentScenario != null) + { + _currentScenario.Activate(); + + Debug.Console(1, this, "Current Scenario: {0}", _currentScenario.Name); + } + var handler = RoomCombinationScenarioChanged; if (handler != null) { @@ -220,9 +241,11 @@ namespace PepperDash.Essentials.Core // 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)); @@ -231,15 +254,25 @@ namespace PepperDash.Essentials.Core { 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 diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs index 05295f42..6fb15042 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs @@ -30,7 +30,7 @@ namespace PepperDash.Essentials.Core /// /// The list of rooms keys that can be combined /// - [JsonProperty("roomMap")] + [JsonProperty("roomKeys")] public List RoomKeys {get; set;} /// @@ -87,6 +87,9 @@ namespace PepperDash.Essentials.Core [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; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs index c0c8101b..e6bdd983 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs @@ -78,6 +78,11 @@ namespace PepperDash.Essentials.Core /// void Activate(); + /// + /// Deactivates this room combination scenario + /// + void Deactivate(); + /// /// The state of the partitions that would activate this scenario /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs index a5534edc..460241a0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; using Crestron.SimplSharp; +using PepperDash.Core; + using Newtonsoft.Json; namespace PepperDash.Essentials.Core @@ -27,9 +29,9 @@ namespace PepperDash.Essentials.Core public BoolFeedback IsActiveFeedback { get; private set; } - List activationActions; + private List activationActions; - List deactivationActions; + private List deactivationActions; public RoomCombinationScenario(RoomCombinationScenarioConfig config) { @@ -52,6 +54,8 @@ namespace PepperDash.Essentials.Core 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) @@ -66,6 +70,8 @@ namespace PepperDash.Essentials.Core 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) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs index 0d8e9803..352cbfcd 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs @@ -11,6 +11,8 @@ using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Devices; using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using Newtonsoft.Json; + namespace PepperDash.Essentials.Core { /// @@ -18,6 +20,8 @@ namespace PepperDash.Essentials.Core /// public abstract class EssentialsRoomBase : ReconfigurableDevice, IEssentialsRoom { + + /// /// /// @@ -35,6 +39,16 @@ namespace PepperDash.Essentials.Core public bool OccupancyStatusProviderIsRemote { get; private set; } + public List EnvironmentalControlDevices { get; protected set; } + + public bool HasEnvironmentalControlDevices + { + get + { + return EnvironmentalControlDevices != null && EnvironmentalControlDevices.Count > 0; + } + } + protected abstract Func IsWarmingFeedbackFunc { get; } protected abstract Func IsCoolingFeedbackFunc { get; } @@ -119,6 +133,8 @@ namespace PepperDash.Essentials.Core public EssentialsRoomBase(DeviceConfig config) : base(config) { + EnvironmentalControlDevices = new List(); + // Setup the ShutdownPromptTimer ShutdownPromptTimer = new SecondsCountdownTimer(Key + "-offTimer"); ShutdownPromptTimer.IsRunningFeedback.OutputChange += (o, a) => diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs index 5f94a5cd..2273690f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs @@ -15,7 +15,7 @@ namespace PepperDash.Essentials.Core /// /// Describes the basic functionality of an EssentialsRoom /// - public interface IEssentialsRoom : IKeyName, IReconfigurableDevice, IRunDefaultPresentRoute + public interface IEssentialsRoom : IKeyName, IReconfigurableDevice, IRunDefaultPresentRoute, IEnvironmentalControls { BoolFeedback OnFeedback { get; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs index 592b0bd9..b5121e9c 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; using Crestron.SimplSharp; +using PepperDash.Core; + namespace PepperDash.Essentials.Core { @@ -66,6 +68,14 @@ namespace PepperDash.Essentials.Core bool RunDefaultCallRoute(); } + /// + /// Describes environmental controls available on a room such as lighting, shades, temperature, etc. + /// + public interface IEnvironmentalControls + { + List EnvironmentalControlDevices { get; } + bool HasEnvironmentalControlDevices { get; } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/Shade Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/Shade Interfaces.cs index b4d7c88d..35492162 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/Shade Interfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/Shade Interfaces.cs @@ -31,6 +31,7 @@ namespace PepperDash.Essentials.Core.Shades public interface IShadesOpenCloseStop : IShadesOpenClose { void StopOrPreset(); + string StopOrPresetButtonLabel { get; } } /// 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 96cc8097..412dee6b 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmBladeChassisController.cs @@ -483,7 +483,7 @@ namespace PepperDash.Essentials.DM Debug.Console(2, this, "Adding output port '{0}'", portKey); OutputPorts.Add(new RoutingOutputPort(portKey, sigType, portType, selector, this) { - FeedbackMatchObject = Chassis.Outputs[(uint)selector] + FeedbackMatchObject = selector }); }*/ 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 9a8742a4..f6592699 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmpsRoutingController.cs @@ -25,6 +25,7 @@ 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; } @@ -32,9 +33,11 @@ namespace PepperDash.Essentials.DM //IroutingNumericEvent public event EventHandler NumericSwitchChange; - //Feedback for DMPS System Power + //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; } @@ -59,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(); @@ -122,26 +126,37 @@ namespace PepperDash.Essentials.DM { Dmps = Global.ControlSystem; - switch (name.Replace("-", "").Replace("c", "").Replace("C", "")) + switch (systemControl.SystemControlType) { - case "dmps34k50": - case "dmps34k100": - case "dmps34k150": + case eSystemControlType.Dmps34K150CSystemControl: SystemControl = systemControl as Dmps34K150CSystemControl; Dmps4kType = true; + SystemPowerOnFeedback = new BoolFeedback(() => { return true; }); + SystemPowerOffFeedback = new BoolFeedback(() => { return false; }); break; - case "dmps34k200": - case "dmps34k250": - case "dmps34k300": - case "dmps34k350": + 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(); @@ -149,27 +164,13 @@ namespace PepperDash.Essentials.DM TxDictionary = new Dictionary(); RxDictionary = new Dictionary(); - SystemPowerOnFeedback = new BoolFeedback(() => + FrontPanelLockOnFeedback = new BoolFeedback(() => { - if (SystemControl is Dmps3SystemControl) - { - return ((Dmps3SystemControl)SystemControl).SystemPowerOnFeedBack.BoolValue; - } - else - { - return false; - } + return SystemControl.FrontPanelLockOnFeedback.BoolValue; }); - SystemPowerOffFeedback = new BoolFeedback(() => + FrontPanelLockOffFeedback = new BoolFeedback(() => { - if (SystemControl is Dmps3SystemControl) - { - return ((Dmps3SystemControl)SystemControl).SystemPowerOffFeedBack.BoolValue; - } - else - { - return false; - } + return SystemControl.FrontPanelLockOffFeedback.BoolValue; }); VideoOutputFeedbacks = new Dictionary(); @@ -191,6 +192,8 @@ namespace PepperDash.Essentials.DM SetupOutputCards(); SetupInputCards(); + + Microphones = new DmpsMicrophoneController(Dmps); } public override bool CustomActivate() @@ -204,6 +207,41 @@ namespace PepperDash.Essentials.DM 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(); } @@ -241,20 +279,10 @@ namespace PepperDash.Essentials.DM } } - public void SetPowerOn(bool a) + public void SetRoutingEnable(bool enable) { - if (SystemControl is Dmps3SystemControl) - { - ((Dmps3SystemControl)SystemControl).SystemPowerOn(); - } - } - - public void SetPowerOff(bool a) - { - if (SystemControl is Dmps3SystemControl) - { - ((Dmps3SystemControl)SystemControl).SystemPowerOff(); - } + CrestronEnvironment.Sleep(1000); + EnableRouting = enable; } public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) @@ -277,20 +305,24 @@ namespace PepperDash.Essentials.DM Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - //Link up system - trilist.SetBoolSigAction(joinMap.SystemPowerOn.JoinNumber, SetPowerOn); - trilist.SetBoolSigAction(joinMap.SystemPowerOff.JoinNumber, SetPowerOff); - if (SystemPowerOnFeedback != null) + //Link up system power only for non-4k DMPS3 + if (SystemControl is Dmps3SystemControl) { - SystemPowerOnFeedback.LinkInputSig( - trilist.BooleanInput[joinMap.SystemPowerOn.JoinNumber]); - } - if (SystemPowerOffFeedback != null) - { - SystemPowerOffFeedback.LinkInputSig( - trilist.BooleanInput[joinMap.SystemPowerOff.JoinNumber]); + 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); @@ -351,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) { @@ -372,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); @@ -379,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 => @@ -412,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]); @@ -463,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; @@ -509,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; @@ -665,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); } @@ -677,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); } @@ -686,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); } @@ -834,12 +887,17 @@ namespace PepperDash.Essentials.DM { 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(); } @@ -904,15 +962,19 @@ namespace PepperDash.Essentials.DM switch (args.EventId) { case DMSystemEventIds.SystemPowerOnEventId: - { - SystemPowerOnFeedback.FireUpdate(); - break; - } case DMSystemEventIds.SystemPowerOffEventId: { + SystemPowerOnFeedback.FireUpdate(); SystemPowerOffFeedback.FireUpdate(); break; } + case DMSystemEventIds.FrontPanelLockOnEventId: + case DMSystemEventIds.FrontPanelLockOffEventId: + { + FrontPanelLockOnFeedback.FireUpdate(); + FrontPanelLockOffFeedback.FireUpdate(); + break; + } } } @@ -933,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); diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4kZScalerCController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4kZScalerCController.cs index 21663e78..73dd59b9 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4kZScalerCController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4kZScalerCController.cs @@ -14,7 +14,7 @@ namespace PepperDash.Essentials.DM { [Description("Wrapper Class for DM-RMC-4K-Z-SCALER-C")] public class DmRmc4kZScalerCController : DmRmcControllerBase, IRmcRoutingWithFeedback, - IIROutputPorts, IComPorts, ICec + IIROutputPorts, IComPorts, ICec, IRelayPorts { private readonly DmRmc4kzScalerC _rmc; @@ -168,5 +168,18 @@ namespace PepperDash.Essentials.DM } #endregion + #region Implementation of IRelayPorts + + public CrestronCollection RelayPorts + { + get { return _rmc.RelayPorts; } + } + + public int NumberOfRelayPorts + { + get { return _rmc.NumberOfRelayPorts; } + } + + #endregion } } \ No newline at end of file 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 9043d514..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) 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 fcec7fa4..5eb2d7b5 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs @@ -263,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/Codec/iHasDirectory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs index b2ccb29c..642f03d0 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 @@ -69,6 +69,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec [JsonProperty("directoryResults")] public List CurrentDirectoryResults { get; private set; } + [JsonProperty("contacts")] public List Contacts { get @@ -77,6 +78,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec } } + [JsonProperty("folders")] public List Folders { get 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 3689bf61..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 @@ -124,6 +124,8 @@ + + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/AppleTV.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/AppleTV.cs index 709afb72..07df3602 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/AppleTV.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/AppleTV.cs @@ -19,7 +19,7 @@ namespace PepperDash.Essentials.Devices.Common public class AppleTV : EssentialsBridgeableDevice, IDPad, ITransport, IUiDisplayInfo, IRoutingOutputs { public IrOutputPortController IrPort { get; private set; } - public const string StandardDriverName = "Apple AppleTV-v2.ir"; + public const string StandardDriverName = "Apple_AppleTV_4th_Gen_Essentials.ir"; public uint DisplayUiType { get { return DisplayUiConstants.TypeAppleTv; } } public AppleTV(string key, string name, IrOutputPortController portCont) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecLayouts.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecLayouts.cs index a8805a21..ef0bf2bd 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecLayouts.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecLayouts.cs @@ -6,6 +6,8 @@ using Crestron.SimplSharp; using PepperDash.Essentials.Core; +using Newtonsoft.Json; + namespace PepperDash.Essentials.Devices.Common.VideoCodec { /// @@ -25,7 +27,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// public interface IHasZoomRoomLayouts : IHasCodecLayouts { - event EventHandler AvailableLayoutsChanged; + event EventHandler LayoutInfoChanged; BoolFeedback LayoutViewIsOnFirstPageFeedback { get; } // TODO: #697 [*] Consider modifying to report button visibility in func BoolFeedback LayoutViewIsOnLastPageFeedback { get; } // TODO: #697 [*] Consider modifying to report button visibility in func @@ -45,6 +47,17 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public class LayoutInfoChangedEventArgs : EventArgs { + [JsonProperty("availableLayouts", NullValueHandling = NullValueHandling.Ignore)] public ZoomRoom.zConfiguration.eLayoutStyle AvailableLayouts { get; set; } + [JsonProperty("currentSelectedLayout", NullValueHandling = NullValueHandling.Ignore)] + public ZoomRoom.zConfiguration.eLayoutStyle CurrentSelectedLayout { get; set; } + [JsonProperty("canSwapContentWithThumbnail", NullValueHandling = NullValueHandling.Ignore)] + public bool CanSwapContentWithThumbnail { get; set; } + [JsonProperty("contentSwappedWithThumbnail", NullValueHandling = NullValueHandling.Ignore)] + public bool ContentSwappedWithThumbnail { get; set; } + [JsonProperty("layoutViewIsOnFirstPage", NullValueHandling = NullValueHandling.Ignore)] + public bool LayoutViewIsOnFirstPage { get; set; } + [JsonProperty("layoutViewIsOnLastPage", NullValueHandling = NullValueHandling.Ignore)] + public bool LayoutViewIsOnLastPage { get; set; } } } \ No newline at end of file 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 0720b319..0d304028 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 @@ -24,24 +24,27 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces /// public class MeetingInfo { - [JsonProperty("id")] + [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] public string Id { get; private set; } - [JsonProperty("name")] + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; private set; } - [JsonProperty("host")] + [JsonProperty("host", NullValueHandling = NullValueHandling.Ignore)] public string Host { get; private set; } - [JsonProperty("password")] + [JsonProperty("password", NullValueHandling = NullValueHandling.Ignore)] public string Password { get; private set; } - [JsonProperty("shareStatus")] + [JsonProperty("shareStatus", NullValueHandling = NullValueHandling.Ignore)] public string ShareStatus { get; private set; } - [JsonProperty("isHost")] + [JsonProperty("isHost", NullValueHandling = NullValueHandling.Ignore)] public Boolean IsHost { get; private set; } - [JsonProperty("isSharingMeeting")] + [JsonProperty("isSharingMeeting", NullValueHandling = NullValueHandling.Ignore)] public Boolean IsSharingMeeting { get; private set; } - [JsonProperty("waitingForHost")] + [JsonProperty("waitingForHost", NullValueHandling = NullValueHandling.Ignore)] public Boolean WaitingForHost { get; private set; } + [JsonProperty("isLocked", NullValueHandling = NullValueHandling.Ignore)] + public Boolean IsLocked { get; private set; } - public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting, bool waitingForHost) + + public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting, bool waitingForHost, bool isLocked) { Id = id; Name = name; @@ -51,6 +54,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces IsHost = isHost; IsSharingMeeting = isSharingMeeting; WaitingForHost = waitingForHost; + IsLocked = isLocked; } } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingLock.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingLock.cs new file mode 100644 index 00000000..97fcb725 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingLock.cs @@ -0,0 +1,18 @@ +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.Interfaces +{ + public interface IHasMeetingLock + { + BoolFeedback MeetingIsLockedFeedback { get; } + + void LockMeeting(); + void UnLockMeeting(); + void ToggleMeetingLock(); + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..771a0865 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingRecording.cs @@ -0,0 +1,18 @@ +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.Interfaces +{ + public interface IHasMeetingRecording + { + BoolFeedback MeetingIsRecordingFeedback { get; } + + void StartRecording(); + void StopRecording(); + void ToggleRecording(); + } +} \ 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 409ccd89..11c5f147 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 @@ -12,6 +12,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces public interface IHasParticipants { CodecParticipants Participants { get; } + + /// + /// Removes the participant from the meeting + /// + /// + void RemoveParticipant(int userId); + + /// + /// Sets the participant as the new host + /// + /// + void SetParticipantAsHost(int userId); } /// @@ -29,6 +41,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces /// public interface IHasParticipantAudioMute : IHasParticipantVideoMute { + /// + /// Mute audio of all participants + /// + void MuteAudioForAllParticipants(); + void MuteAudioForParticipant(int userId); void UnmuteAudioForParticipant(int userId); void ToggleAudioForParticipant(int userId); diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/iVideoCodecInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/iVideoCodecInfo.cs index 902dd68f..6eee454f 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/iVideoCodecInfo.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/iVideoCodecInfo.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; using Crestron.SimplSharp; +using Newtonsoft.Json; + namespace PepperDash.Essentials.Devices.Common.Codec { /// @@ -19,12 +21,19 @@ namespace PepperDash.Essentials.Devices.Common.Codec /// public abstract class VideoCodecInfo { + [JsonProperty("multiSiteOptionIsEnabled", NullValueHandling = NullValueHandling.Ignore)] public abstract bool MultiSiteOptionIsEnabled { get; } + [JsonProperty("ipAddress", NullValueHandling = NullValueHandling.Ignore)] public abstract string IpAddress { get; } + [JsonProperty("sipPhoneNumber", NullValueHandling = NullValueHandling.Ignore)] public abstract string SipPhoneNumber { get; } + [JsonProperty("e164Alias", NullValueHandling = NullValueHandling.Ignore)] public abstract string E164Alias { get; } + [JsonProperty("h323Id", NullValueHandling = NullValueHandling.Ignore)] public abstract string H323Id { get; } + [JsonProperty("sipUri", NullValueHandling = NullValueHandling.Ignore)] public abstract string SipUri { get; } + [JsonProperty("autoAnswerEnabled", NullValueHandling = NullValueHandling.Ignore)] public abstract bool AutoAnswerEnabled { get; } } } \ 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 c47870ab..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 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 73390b06..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 @@ -41,6 +41,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); SharingContentIsOnFeedback = new BoolFeedback(SharingContentIsOnFeedbackFunc); + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + MeetingsToDisplayFeedback = new IntFeedback(() => MeetingsToDisplay); + InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); @@ -305,8 +308,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec trilist.OnlineStatusChange += (device, args) => { if (!args.DeviceOnLine) return; - - trilist.SetString(joinMap.Schedule.JoinNumber, "\xFC"); }; } @@ -425,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(); } @@ -598,6 +599,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec UpdateParticipantsXSig(codec, trilist, joinMap); }; + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, "\xFC"); + UpdateParticipantsXSig(codec, trilist, joinMap); + }; } private void UpdateParticipantsXSig(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) @@ -829,7 +839,22 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec UpdateMeetingsList(codec, trilist, joinMap); } }; - } + + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + trilist.SetUShortSigAction(joinMap.MeetingsToDisplay.JoinNumber, m => MeetingsToDisplay = m); + MeetingsToDisplayFeedback.LinkInputSig(trilist.UShortInput[joinMap.MeetingsToDisplay.JoinNumber]); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.Schedule.JoinNumber, "\xFC"); + UpdateMeetingsList(codec, trilist, joinMap); + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + MeetingsToDisplayFeedback.LinkInputSig(trilist.UShortInput[joinMap.MeetingsToDisplay.JoinNumber]); + }; + } private void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) { @@ -850,19 +875,45 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec var meetingsData = UpdateMeetingsListXSig(_currentMeetings); trilist.SetString(joinMap.Schedule.JoinNumber, meetingsData); trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)_currentMeetings.Count); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.Schedule.JoinNumber, "\xFC"); + UpdateMeetingsListXSig(_currentMeetings); + }; } + + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + private int _meetingsToDisplay = 3; + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + protected int MeetingsToDisplay + { + get { return _meetingsToDisplay; } + set { + _meetingsToDisplay = (ushort) (value == 0 ? 3 : value); + MeetingsToDisplayFeedback.FireUpdate(); + } + } + + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + public IntFeedback MeetingsToDisplayFeedback { get; set; } + private string UpdateMeetingsListXSig(List meetings) { - const int maxMeetings = 3; + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + //const int _meetingsToDisplay = 3; const int maxDigitals = 2; const int maxStrings = 7; const int offset = maxDigitals + maxStrings; - var digitalIndex = maxStrings * maxMeetings; //15 + var digitalIndex = maxStrings * _meetingsToDisplay; //15 var stringIndex = 0; var meetingIndex = 0; - var tokenArray = new XSigToken[maxMeetings * offset]; + var tokenArray = new XSigToken[_meetingsToDisplay * offset]; /* * Digitals * IsJoinable - 1 @@ -885,7 +936,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec if (meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue; - if (meetingIndex >= maxMeetings * offset) + if (meetingIndex >= _meetingsToDisplay * offset) { Debug.Console(2, this, "Max Meetings reached"); break; @@ -910,10 +961,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec stringIndex += maxStrings; } - while (meetingIndex < maxMeetings * offset) + while (meetingIndex < _meetingsToDisplay * offset) { Debug.Console(2, this, "Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}", - meetingIndex, maxMeetings * offset); + meetingIndex, _meetingsToDisplay * offset); //digitals tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); @@ -980,6 +1031,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); }; + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, "\xFC"); + UpdateDirectoryXSig(codec.CurrentDirectoryResult, + !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + }; } @@ -1207,80 +1268,88 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); - trilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count); + trilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count); }; - var joinCodec = this as IJoinCalls; - if (joinCodec != null) - { - trilist.SetSigFalseAction(joinMap.JoinAllCalls.JoinNumber, () => joinCodec.JoinAllCalls()); + var joinCodec = this as IJoinCalls; + if (joinCodec != null) + { + trilist.SetSigFalseAction(joinMap.JoinAllCalls.JoinNumber, () => joinCodec.JoinAllCalls()); - 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); - } - }); - } - } + 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); - } - }); + 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; + 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; + 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); - } - }); + 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; + 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); - } - }); - } - } + 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() @@ -1664,6 +1733,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty); trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); }); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.CameraPresetNames.JoinNumber, "\xFC"); + SetCameraPresetNames(presetCodec.NearEndPresets); + }; } // Following fields only used for Bridging 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 e6efd063..f6355d63 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 @@ -618,13 +618,31 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private bool _can_Switch_Speaker_View; private bool _can_Switch_Wall_View; private bool _can_Switch_Share_On_All_Screens; + private bool _can_Switch_Floating_Share_Content; private bool _is_In_First_Page; private bool _is_In_Last_Page; private string _video_type; public bool can_Adjust_Floating_Video { get; set; } - public bool can_Switch_Floating_Share_Content { get; set; } + + + public bool can_Switch_Floating_Share_Content + { + get + { + return _can_Switch_Floating_Share_Content; + } + set + { + if (value != _can_Switch_Floating_Share_Content) + { + _can_Switch_Floating_Share_Content = value; + NotifyPropertyChanged("can_Switch_Floating_Share_Content"); + } + } + } + /// /// [on/off] // Set to On if it is possible to invoke zConfiguration Call Layout Style: ShareAll, to switch to the ShareAll mode, where the content sharing is shown full screen on all monitors. @@ -744,12 +762,29 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } - public class CallRecordInfo + public class CallRecordInfo : NotifiableObject { + private bool _meetingIsBeingRecorded; + public bool canRecord { get; set; } public bool emailRequired { get; set; } public bool amIRecording { get; set; } - public bool meetingIsBeingRecorded { get; set; } + + public bool meetingIsBeingRecorded + { + get + { + return _meetingIsBeingRecorded; + } + set + { + if (value != _meetingIsBeingRecorded) + { + _meetingIsBeingRecorded = value; + NotifyPropertyChanged("meetingIsBeingRecorded"); + } + } + } } } @@ -1123,9 +1158,25 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } - public class Lock + public class Lock : NotifiableObject { - public bool Enable { get; set; } + private bool _enable; + + public bool Enable + { + get + { + return _enable; + } + set + { + if (value != _enable) + { + _enable = value; + NotifyPropertyChanged("Enable"); + } + } + } } public class ClosedCaption 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 1fa58ffb..6ea90c80 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 @@ -26,13 +26,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom IRouting, IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraMute, IHasCameraAutoMode, IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing, IHasZoomRoomLayouts, IHasParticipantPinUnpin, - IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo, IHasPresentationOnlyMeeting + IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo, IHasPresentationOnlyMeeting, + IHasMeetingLock, IHasMeetingRecording { private const long MeetingRefreshTimer = 60000; public uint DefaultMeetingDurationMin { get; private set; } - private const string Delimiter = "\x0D\x0A"; + /// + /// CR LF + /// + private const string EchoDelimiter = "\x0D\x0A\x0D\x0A"; + private const string SendDelimiter = "\x0D"; + + /// + /// CR LF } CR LF + /// + 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; @@ -58,7 +71,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; @@ -70,7 +83,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); @@ -85,9 +98,13 @@ 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, @@ -147,6 +164,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom ContentSwappedWithThumbnailFeedback = new BoolFeedback(ContentSwappedWithThumbnailFeedbackFunc); NumberOfScreensFeedback = new IntFeedback(NumberOfScreensFeedbackFunc); + + MeetingIsLockedFeedback = new BoolFeedback(() => Configuration.Call.Lock.Enable ); + + MeetingIsRecordingFeedback = new BoolFeedback(() => Status.Call.CallRecordInfo.meetingIsBeingRecorded ); } public CommunicationGather PortGather { get; private set; } @@ -499,19 +520,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { var sharingStatus = GetSharingStatus(); - MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true, false); + MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true, false, MeetingIsLockedFeedback.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); + MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost, MeetingIsLockedFeedback.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); + MeetingInfo = new MeetingInfo("", "", "", "", "None", false, false, false, MeetingIsLockedFeedback.BoolValue); } } @@ -578,11 +599,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom case "ShareThumb": { ContentSwappedWithThumbnailFeedback.FireUpdate(); + OnLayoutInfoChanged(); break; } case "Style": { LocalLayoutFeedback.FireUpdate(); + OnLayoutInfoChanged(); break; } case "Size": @@ -597,6 +620,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } }; + Configuration.Call.Lock.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Enable") + { + MeetingIsLockedFeedback.FireUpdate(); + MeetingInfo = new MeetingInfo + ( + MeetingInfo.Id, + MeetingInfo.Name, + MeetingInfo.Host, + MeetingInfo.Password, + GetSharingStatus(), + MeetingInfo.IsHost, + MeetingInfo.IsSharingMeeting, + MeetingInfo.WaitingForHost, + MeetingIsLockedFeedback.BoolValue + ); + } + }; + // This is to deal with incorrect object structure coming back from the Zoom Room on v 5.6.3 Configuration.Client.Call.Layout.PropertyChanged += (o, a) => { @@ -613,11 +656,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom case "ShareThumb": { ContentSwappedWithThumbnailFeedback.FireUpdate(); + OnLayoutInfoChanged(); break; } case "Style": { LocalLayoutFeedback.FireUpdate(); + OnLayoutInfoChanged(); break; } } @@ -634,13 +679,31 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Status.Call.PropertyChanged += (o, a) => { - if (a.PropertyName == "Info") + 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) => { switch (a.PropertyName) @@ -663,7 +726,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom return; } // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); + var meetingInfo = new MeetingInfo(MeetingInfo.Id, + MeetingInfo.Name, + MeetingInfo.Host, + MeetingInfo.Password, + GetSharingStatus(), + GetIsHostMyself(), + MeetingInfo.IsSharingMeeting, + MeetingInfo.WaitingForHost, + MeetingIsLockedFeedback.BoolValue); MeetingInfo = meetingInfo; break; } @@ -714,13 +785,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom LayoutViewIsOnLastPageFeedback.FireUpdate(); break; } - //case "video_type": - // { - // It appears as though the actual value we want to watch is Configuration.Call.Layout.Style - // LocalLayoutFeedback.FireUpdate(); - // break; - // } + case "can_Switch_Floating_Share_Content": + { + CanSwapContentWithThumbnailFeedback.FireUpdate(); + break; + } } + OnLayoutInfoChanged(); }; Status.NumberOfScreens.PropertyChanged += (o, a) => @@ -849,7 +920,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Debug.Console(1, this, "Sending: '{0}'", command); } - Communication.SendText(command + Delimiter); + Communication.SendText(command + SendDelimiter); } /// @@ -859,13 +930,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 /// @@ -924,6 +1010,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 /// @@ -951,7 +1124,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; @@ -968,7 +1141,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); @@ -1013,7 +1186,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. @@ -1035,7 +1208,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"); } @@ -1066,6 +1239,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); @@ -1074,7 +1252,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) { @@ -1244,7 +1422,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Participants.CurrentParticipants = participants; // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, MeetingInfo.ShareStatus, GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); + var meetingInfo = new MeetingInfo( + MeetingInfo.Id, + MeetingInfo.Name, + Participants.Host.Name, + MeetingInfo.Password, + MeetingInfo.ShareStatus, + GetIsHostMyself(), + MeetingInfo.IsSharingMeeting, + MeetingInfo.WaitingForHost, + MeetingIsLockedFeedback.BoolValue); MeetingInfo = meetingInfo; PrintCurrentCallParticipants(); @@ -1446,21 +1633,21 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { Status.NeedWaitForHost = JsonConvert.DeserializeObject(responseObj.ToString()); - Debug.Console(1, this, "NeedWaitForHost: {0}", Status.NeedWaitForHost.Wait); + Debug.Console(1, this, "WaitingForHost: {0}", Status.NeedWaitForHost.Wait); if (Status.NeedWaitForHost.Wait) { if (MeetingInfo == null) { MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", - GetSharingStatus(), false, false, true); + GetSharingStatus(), false, false, true, MeetingIsLockedFeedback.BoolValue); UpdateCallStatus(); break; } MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", - GetSharingStatus(), false, false, true); + GetSharingStatus(), false, false, true, MeetingIsLockedFeedback.BoolValue); UpdateCallStatus(); @@ -1470,12 +1657,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); + GetSharingStatus(), false, false, false, MeetingIsLockedFeedback.BoolValue); break; } MeetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, - GetSharingStatus(), GetIsHostMyself(), false, false); + GetSharingStatus(), GetIsHostMyself(), false, false, MeetingIsLockedFeedback.BoolValue); break; } @@ -1559,7 +1746,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (result.Success) { - MeetingInfo = new MeetingInfo("", "", "", "", "", true, true, MeetingInfo.WaitingForHost); + MeetingInfo = new MeetingInfo("", "", "", "", "", true, true, MeetingInfo.WaitingForHost, MeetingIsLockedFeedback.BoolValue); break; } @@ -1578,19 +1765,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; @@ -1904,6 +2089,22 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom GetSharingStatus(), GetIsHostMyself(), !String.Equals(Status.Call.Info.meeting_type,"NORMAL"), + false, + MeetingIsLockedFeedback.BoolValue + ); + } + // TODO [ ] Issue #868 + else if (item.Status == eCodecCallStatus.Disconnected) + { + MeetingInfo = new MeetingInfo( + string.Empty, + string.Empty, + string.Empty, + string.Empty, + string.Empty, + false, + false, + false, false ); } @@ -2132,16 +2333,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom var layoutsCodec = this as IHasZoomRoomLayouts; if (layoutsCodec != null) { - layoutsCodec.AvailableLayoutsChanged += (o, a) => + layoutsCodec.LayoutInfoChanged += (o, a) => { - trilist.SetBool(joinMap.LayoutGalleryIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.Gallery - == - (a.AvailableLayouts & - zConfiguration.eLayoutStyle.Gallery)); - trilist.SetBool(joinMap.LayoutSpeakerIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.Speaker - == - (a.AvailableLayouts & - zConfiguration.eLayoutStyle.Speaker)); + trilist.SetBool(joinMap.LayoutGalleryIsAvailable.JoinNumber, + zConfiguration.eLayoutStyle.Gallery == (a.AvailableLayouts & zConfiguration.eLayoutStyle.Gallery)); + + trilist.SetBool(joinMap.LayoutSpeakerIsAvailable.JoinNumber, + zConfiguration.eLayoutStyle.Speaker == (a.AvailableLayouts & zConfiguration.eLayoutStyle.Speaker)); + + + trilist.SetBool(joinMap.LayoutStripIsAvailable.JoinNumber, zConfiguration.eLayoutStyle.Strip == (a.AvailableLayouts & zConfiguration.eLayoutStyle.Strip)); @@ -2196,7 +2397,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom trilist.SetUShortSigAction(joinMap.ScreenIndexToPinUserTo.JoinNumber, (u) => ScreenIndexToPinUserTo = u); } - // TODO: #714 [ ] LinkZoomRoomToApi >> layoutSizeCoodec var layoutSizeCodec = this as IHasSelfviewSize; if (layoutSizeCodec != null) { @@ -2218,6 +2418,75 @@ 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) => + { + trilist.SetString(joinMap.MeetingInfoId.JoinNumber, args.Info.Id); + trilist.SetString(joinMap.MeetingInfoHost.JoinNumber, args.Info.Host); + trilist.SetString(joinMap.MeetingInfoPassword.JoinNumber, args.Info.Password); + trilist.SetBool(joinMap.IsHost.JoinNumber, args.Info.IsHost); + trilist.SetBool(joinMap.ShareOnlyMeeting.JoinNumber, args.Info.IsSharingMeeting); + trilist.SetBool(joinMap.WaitingForHost.JoinNumber, args.Info.WaitingForHost); + //trilist.SetString(joinMap.CurrentSource.JoinNumber, args.Info.ShareStatus); + }; + + trilist.SetSigTrueAction(joinMap.StartMeetingNow.JoinNumber, () => StartMeeting(0)); + trilist.SetSigTrueAction(joinMap.ShareOnlyMeeting.JoinNumber, StartSharingOnlyMeeting); + trilist.SetSigTrueAction(joinMap.StartNormalMeetingFromSharingOnlyMeeting.JoinNumber, StartNormalMeetingFromSharingOnlyMeeting); + + // not sure if this would be needed here, should be handled by VideoCodecBase.cs LinkToApi methods + //DirectoryResultReturned += (device, args) => + //{ + // // add logic here if necessary when event fires + + //}; + + + trilist.SetStringSigAction(joinMap.SubmitPassword.JoinNumber, SubmitPassword); + PasswordRequired += (devices, 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) + { + // login attempt failed + return; + } + + trilist.SetBool(joinMap.PasswordIncorrect.JoinNumber, args.LastAttemptWasIncorrect); + trilist.SetBool(joinMap.ShowPasswordPrompt.JoinNumber, true); + }; + trilist.OnlineStatusChange += (device, args) => { if (!args.DeviceOnLine) return; @@ -2334,6 +2603,56 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } + /// + /// Invites contacts to a new meeting for a specified duration + /// + /// + /// + public void InviteContactsToNewMeeting(List contacts, uint duration) + { + if(duration == 0) + { + duration = DefaultMeetingDurationMin; + } + + StringBuilder message = new StringBuilder(); + + // Add the prefix + message.Append(string.Format("zCommand Invite Duration: {0}", duration)); + + // Add each invitee + foreach (var contact in contacts) + { + var invitee = string.Format(" user: {0}", contact.ContactId); + + message.Append(invitee); + } + + SendText(message.ToString()); + } + + /// + /// Invites contacts to an existing meeting + /// + /// + public void InviteContactsToExistingMeeting(List contacts) + { + StringBuilder message = new StringBuilder(); + + // Add the prefix + message.Append(string.Format("zCommand Call Invite")); + + // Add each invitee + foreach (var contact in contacts) + { + var invitee = string.Format(" user: {0}", contact.ContactId); + + message.Append(invitee); + } + + SendText(message.ToString()); + } + /// /// Starts a PMI Meeting for the specified duration (or default meeting duration if 0 is specified) @@ -2469,10 +2788,25 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public CodecParticipants Participants { get; private set; } + public void RemoveParticipant(int userId) + { + SendText(string.Format("zCommand Call Expel Id: {0}", userId)); + } + + public void SetParticipantAsHost(int userId) + { + SendText(string.Format("zCommand Call HostChange Id: {0}", userId)); + } + #endregion #region IHasParticipantAudioMute Members + public void MuteAudioForAllParticipants() + { + SendText(string.Format("zCommand Call MuteAll Mute: on")); + } + public void MuteAudioForParticipant(int userId) { SendText(string.Format("zCommand Call MuteParticipant Mute: on Id: {0}", userId)); @@ -2770,7 +3104,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom #region IHasZoomRoomLayouts Members - public event EventHandler AvailableLayoutsChanged; + public event EventHandler LayoutInfoChanged; private Func LayoutViewIsOnFirstPageFeedbackFunc { @@ -2836,15 +3170,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Debug.Console(1, this, "availablelayouts: {0}", availableLayouts); - var handler = AvailableLayoutsChanged; - if (handler != null) - { - handler(this, new LayoutInfoChangedEventArgs() {AvailableLayouts = availableLayouts}); - } - AvailableLayouts = availableLayouts; } + private void OnLayoutInfoChanged() + { + var handler = LayoutInfoChanged; + if (handler != null) + { + handler(this, new LayoutInfoChangedEventArgs() + { + AvailableLayouts = AvailableLayouts, + 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, + ContentSwappedWithThumbnail = ContentSwappedWithThumbnailFeedback.BoolValue, + }); + } + } + public void GetAvailableLayouts() { SendText("zStatus Call Layout"); @@ -3034,7 +3379,63 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } #endregion - } + + #region IHasMeetingLock Members + + public BoolFeedback MeetingIsLockedFeedback { get; private set; } + + public void LockMeeting() + { + SendText(string.Format("zConfiguration Call Lock Enable: on")); + } + + public void UnLockMeeting() + { + SendText(string.Format("zConfiguration Call Lock Enable: off")); + } + + public void ToggleMeetingLock() + { + if (MeetingIsLockedFeedback.BoolValue) + { + UnLockMeeting(); + } + else + { + LockMeeting(); + } + } + + #endregion + + #region IHasMeetingRecording Members + + public BoolFeedback MeetingIsRecordingFeedback { get; private set; } + + public void StartRecording() + { + SendText(string.Format("Command Call Record Enable: on")); + } + + public void StopRecording() + { + SendText(string.Format("Command Call Record Enable: off")); + } + + public void ToggleRecording() + { + if (MeetingIsRecordingFeedback.BoolValue) + { + StopRecording(); + } + else + { + StartRecording(); + } + } + + #endregion + } /// /// Zoom Room specific info object @@ -3047,8 +3448,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Configuration = configuration; } + [JsonIgnore] public ZoomRoomStatus Status { get; private set; } - public ZoomRoomConfiguration Configuration { get; private set; } + [JsonIgnore] + public ZoomRoomConfiguration Configuration { get; private set; } public override bool AutoAnswerEnabled { @@ -3155,7 +3558,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; } @@ -3171,6 +3576,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public event EventHandler InitialSyncCompleted; + public event EventHandler FirstJsonResponseReceived; + public void StartSync() { DequeueQueries(); @@ -3193,14 +3600,27 @@ 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(); } - private void InitialQueryMessagesSent() + 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; Debug.Console(1, this, "Query Messages Sent."); @@ -3224,7 +3644,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; @@ -3233,7 +3654,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/ZoomRoomJoinMap.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomJoinMap.cs index f70c48bf..b977cfe3 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 @@ -1,301 +1,496 @@ -using System; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges.JoinMaps; - -namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom -{ - public class ZoomRoomJoinMap : VideoCodecControllerJoinMap - { - #region Digital - - [JoinName("CanSwapContentWithThumbnail")] - public JoinDataComplete CanSwapContentWithThumbnail = new JoinDataComplete( - new JoinData - { - JoinNumber = 206, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "FB Indicates if content can be swapped with thumbnail", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("SwapContentWithThumbnail")] - public JoinDataComplete SwapContentWithThumbnail = new JoinDataComplete( - new JoinData - { - JoinNumber = 206, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Pulse to swap content with thumbnail. FB reports current state", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("GetAvailableLayouts")] - public JoinDataComplete GetAvailableLayouts = new JoinDataComplete( - new JoinData - { - JoinNumber = 215, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Gets the available layouts. Will update the LayoutXXXXXIsAvailbale signals.", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("LayoutIsOnFirstPage")] - public JoinDataComplete LayoutIsOnFirstPage = new JoinDataComplete( - new JoinData - { - JoinNumber = 216, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Indicates if layout is on first page", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("LayoutIsOnLastPage")] - public JoinDataComplete LayoutIsOnLastPage = new JoinDataComplete( - new JoinData - { - JoinNumber = 217, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Indicates if layout is on first page", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("LayoutTurnToNextPage")] - public JoinDataComplete LayoutTurnToNextPage = new JoinDataComplete( - new JoinData - { - JoinNumber = 216, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Turns layout view to next page", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("LayoutTurnToPreviousPage")] - public JoinDataComplete LayoutTurnToPreviousPage = new JoinDataComplete( - new JoinData - { - JoinNumber = 217, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Turns layout view to previous page", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Digital - }); - - [JoinName("LayoutGalleryIsAvailable")] - public JoinDataComplete LayoutGalleryIsAvailable = new JoinDataComplete( - new JoinData - { - JoinNumber = 221, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "FB Indicates if layout 'Gallery' is available", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.DigitalSerial - }); - - [JoinName("LayoutSpeakerIsAvailable")] - public JoinDataComplete LayoutSpeakerIsAvailable = new JoinDataComplete( - new JoinData - { - JoinNumber = 222, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "FB Indicates if layout 'Speaker' is available", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.DigitalSerial - }); - - [JoinName("LayoutStripIsAvailable")] - public JoinDataComplete LayoutStripIsAvailable = new JoinDataComplete( - new JoinData - { - JoinNumber = 223, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "FB Indicates if layout 'Strip' is available", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.DigitalSerial - }); - - [JoinName("LayoutShareAllIsAvailable")] - public JoinDataComplete LayoutShareAllIsAvailable = new JoinDataComplete( - new JoinData - { - JoinNumber = 224, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "FB Indicates if layout 'ShareAll' is available", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.DigitalSerial - }); - - // TODO: #714 [ ] JoinMap >> SelfivewPipSizeToggle - [JoinName("SelfviewPipSizeToggle")] - public JoinDataComplete SelfviewPipSizeToggle = new JoinDataComplete( - new JoinData - { - JoinNumber = 231, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Toggles the selfview pip size, (aka layout size)", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Digital - }); - - //[JoinName("ParticipantAudioMuteToggleStart")] - //public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete( - // new JoinData - // { - // JoinNumber = 500, - // JoinSpan = 100 - // }, - // 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 = 100 - // }, - // 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 = 100 - // }, - // new JoinMetadata - // { - // Description = "Toggles the participant's pin status", - // JoinCapabilities = eJoinCapabilities.ToSIMPL, - // JoinType = eJoinType.Digital - // }); - - #endregion - - - #region Analog - - [JoinName("NumberOfScreens")] - public JoinDataComplete NumberOfScreens = new JoinDataComplete( - new JoinData - { - JoinNumber = 11, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Reports the number of screens connected", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - [JoinName("ScreenIndexToPinUserTo")] - public JoinDataComplete ScreenIndexToPinUserTo = new JoinDataComplete( - new JoinData - { - JoinNumber = 11, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Specifies the screen index a participant should be pinned to", - JoinCapabilities = eJoinCapabilities.FromSIMPL, - JoinType = eJoinType.Analog - }); - - #endregion - - - #region Serials - - [JoinName("GetSetCurrentLayout")] - public JoinDataComplete GetSetCurrentLayout = new JoinDataComplete( - new JoinData - { - JoinNumber = 215, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Sets and reports the current layout. Use the LayoutXXXXIsAvailable signals to determine valid layouts", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.Serial - }); - - // TODO: #714 [ ] JoinMap >> GetSetSelfviewPipSize - [JoinName("GetSetSelfviewPipSize")] - public JoinDataComplete GetSetSelfviewPipSize = new JoinDataComplete( - new JoinData - { - JoinNumber = 230, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Sets and reports the selfview pip size, (aka layout size).", - JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - JoinType = eJoinType.DigitalSerial - }); - - #endregion - - public ZoomRoomJoinMap(uint joinStart) - : base(joinStart, typeof(ZoomRoomJoinMap)) - { - } - - public ZoomRoomJoinMap(uint joinStart, Type type) - : base(joinStart, type) - { - } - } +using System; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges.JoinMaps; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public class ZoomRoomJoinMap : VideoCodecControllerJoinMap + { + #region Digital + + // TODO [ ] Issue #868 + [JoinName("ShowPasswordPrompt")] + public JoinDataComplete ShowPasswordPrompt = new JoinDataComplete( + new JoinData + { + JoinNumber = 6, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates to show the password prompt", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + // TODO [ ] Issue #868 + [JoinName("PasswordIncorrect")] + public JoinDataComplete PasswordIncorrect = new JoinDataComplete( + new JoinData + { + JoinNumber = 7, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates the password entered is incorrect", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + // TODO [ ] Issue #868 + [JoinName("PassowrdLoginFailed")] + public JoinDataComplete PasswordLoginFailed = new JoinDataComplete( + new JoinData + { + JoinNumber = 8, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates the password entered is incorrect", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + // TODO [ ] Issue #868 + [JoinName("WaitingForHost")] + public JoinDataComplete WaitingForHost = new JoinDataComplete( + new JoinData + { + JoinNumber = 9, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates system is waiting for host", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + // TODO [ ] Issue #868 + [JoinName("IsHost")] + public JoinDataComplete IsHost = new JoinDataComplete( + new JoinData + { + JoinNumber = 10, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates system is the host", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + // TODO [ ] Issue #868 + [JoinName("StartMeetingNow")] + public JoinDataComplete StartMeetingNow = new JoinDataComplete( + new JoinData + { + JoinNumber = 25, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates the password prompt is active", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + // TODO [ ] Issue #868 + [JoinName("ShareOnlyMeeting")] + public JoinDataComplete ShareOnlyMeeting = new JoinDataComplete( + new JoinData + { + JoinNumber = 26, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Triggers a share only meeting, feedback indicates the current meeting is share only", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + // TODO [ ] Issue #868 + [JoinName("StartNormalMeetingFromSharingOnlyMeeting")] + public JoinDataComplete StartNormalMeetingFromSharingOnlyMeeting = new JoinDataComplete( + new JoinData + { + JoinNumber = 27, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Starts a normal meeting from a share only meeting", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("CanSwapContentWithThumbnail")] + public JoinDataComplete CanSwapContentWithThumbnail = new JoinDataComplete( + new JoinData + { + JoinNumber = 206, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates if content can be swapped with thumbnail", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SwapContentWithThumbnail")] + public JoinDataComplete SwapContentWithThumbnail = new JoinDataComplete( + new JoinData + { + JoinNumber = 206, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Pulse to swap content with thumbnail. FB reports current state", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("GetAvailableLayouts")] + public JoinDataComplete GetAvailableLayouts = new JoinDataComplete( + new JoinData + { + JoinNumber = 215, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Gets the available layouts. Will update the LayoutXXXXXIsAvailbale signals.", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("LayoutIsOnFirstPage")] + public JoinDataComplete LayoutIsOnFirstPage = new JoinDataComplete( + new JoinData + { + JoinNumber = 216, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Indicates if layout is on first page", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("LayoutIsOnLastPage")] + public JoinDataComplete LayoutIsOnLastPage = new JoinDataComplete( + new JoinData + { + JoinNumber = 217, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Indicates if layout is on first page", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("LayoutTurnToNextPage")] + public JoinDataComplete LayoutTurnToNextPage = new JoinDataComplete( + new JoinData + { + JoinNumber = 216, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Turns layout view to next page", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("LayoutTurnToPreviousPage")] + public JoinDataComplete LayoutTurnToPreviousPage = new JoinDataComplete( + new JoinData + { + JoinNumber = 217, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Turns layout view to previous page", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("LayoutGalleryIsAvailable")] + public JoinDataComplete LayoutGalleryIsAvailable = new JoinDataComplete( + new JoinData + { + JoinNumber = 221, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates if layout 'Gallery' is available", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.DigitalSerial + }); + + [JoinName("LayoutSpeakerIsAvailable")] + public JoinDataComplete LayoutSpeakerIsAvailable = new JoinDataComplete( + new JoinData + { + JoinNumber = 222, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates if layout 'Speaker' is available", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.DigitalSerial + }); + + [JoinName("LayoutStripIsAvailable")] + public JoinDataComplete LayoutStripIsAvailable = new JoinDataComplete( + new JoinData + { + JoinNumber = 223, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates if layout 'Strip' is available", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.DigitalSerial + }); + + [JoinName("LayoutShareAllIsAvailable")] + public JoinDataComplete LayoutShareAllIsAvailable = new JoinDataComplete( + new JoinData + { + JoinNumber = 224, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "FB Indicates if layout 'ShareAll' is available", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.DigitalSerial + }); + + // TODO: #714 [ ] JoinMap >> SelfivewPipSizeToggle + [JoinName("SelfviewPipSizeToggle")] + public JoinDataComplete SelfviewPipSizeToggle = new JoinDataComplete( + new JoinData + { + JoinNumber = 231, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Toggles the selfview pip size, (aka layout size)", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + //[JoinName("ParticipantAudioMuteToggleStart")] + //public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete( + // new JoinData + // { + // JoinNumber = 500, + // JoinSpan = 100 + // }, + // 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 = 100 + // }, + // 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 = 100 + // }, + // new JoinMetadata + // { + // Description = "Toggles the participant's pin status", + // JoinCapabilities = eJoinCapabilities.ToSIMPL, + // JoinType = eJoinType.Digital + // }); + + #endregion + + + #region Analog + + [JoinName("NumberOfScreens")] + public JoinDataComplete NumberOfScreens = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Reports the number of screens connected", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("ScreenIndexToPinUserTo")] + public JoinDataComplete ScreenIndexToPinUserTo = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Specifies the screen index a participant should be pinned to", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Analog + }); + + #endregion + + + #region Serials + + // TODO [ ] Issue #868 + [JoinName("SubmitPassword")] + public JoinDataComplete SubmitPassword = new JoinDataComplete( + new JoinData + { + JoinNumber = 6, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Submit password text", + JoinCapabilities = eJoinCapabilities.FromSIMPL, + JoinType = eJoinType.Serial + }); + + // TODO [ ] Issue #868 + [JoinName("PasswordPromptMessage")] + public JoinDataComplete PasswordPromptMessage = new JoinDataComplete( + new JoinData + { + JoinNumber = 6, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Password prompt message", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + // TODO [ ] Issue #868 + [JoinName("MeetingInfoId")] + public JoinDataComplete MeetingInfoId = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Meeting info ID text feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + // TODO [ ] Issue #868 + [JoinName("MeetingInfoHostt")] + public JoinDataComplete MeetingInfoHost = new JoinDataComplete( + new JoinData + { + JoinNumber = 12, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Meeting info Host text feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + // TODO [ ] Issue #868 + [JoinName("MeetingInfoPassword")] + public JoinDataComplete MeetingInfoPassword = new JoinDataComplete( + new JoinData + { + JoinNumber = 13, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Meeting info Password text feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); + + [JoinName("GetSetCurrentLayout")] + public JoinDataComplete GetSetCurrentLayout = new JoinDataComplete( + new JoinData + { + JoinNumber = 215, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Sets and reports the current layout. Use the LayoutXXXXIsAvailable signals to determine valid layouts", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Serial + }); + + // TODO: #714 [ ] JoinMap >> GetSetSelfviewPipSize + [JoinName("GetSetSelfviewPipSize")] + public JoinDataComplete GetSetSelfviewPipSize = new JoinDataComplete( + new JoinData + { + JoinNumber = 230, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Sets and reports the selfview pip size, (aka layout size).", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.DigitalSerial + }); + + #endregion + + public ZoomRoomJoinMap(uint joinStart) + : base(joinStart, typeof(ZoomRoomJoinMap)) + { + } + + public ZoomRoomJoinMap(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/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..41f858a6 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file