diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index a8002f7e..ba5529d9 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -131,29 +131,46 @@ namespace PepperDash.Essentials if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) // Handles 3-series running Windows CE OS { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on {1} Appliance", Global.AssemblyVersion, Global.ProcessorSeries.ToString()); + string userFolder; + string nvramFolder; + bool is4series = false; + + if (eCrestronSeries.Series4 == (Global.ProcessorSeries & eCrestronSeries.Series4)) // Handle 4-series + { + is4series = true; + // Set path to user/ + userFolder = "user"; + nvramFolder = "nvram"; + } + else + { + userFolder = "User"; + nvramFolder = "Nvram"; + } + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on {1} Appliance", Global.AssemblyVersion, is4series ? "4-series" : "3-series"); // Check if User/ProgramX exists - if (Directory.Exists(Global.ApplicationDirectoryPathPrefix + dirSeparator + "User" + if (Directory.Exists(Global.ApplicationDirectoryPathPrefix + dirSeparator + userFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber))) { - Debug.Console(0, @"User/program{0} directory found", InitialParametersClass.ApplicationNumber); - filePathPrefix = directoryPrefix + dirSeparator + "User" + Debug.Console(0, @"{0}/program{1} directory found", userFolder, InitialParametersClass.ApplicationNumber); + filePathPrefix = directoryPrefix + dirSeparator + userFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator; } // Check if Nvram/Programx exists - else if (Directory.Exists(directoryPrefix + dirSeparator + "Nvram" + else if (Directory.Exists(directoryPrefix + dirSeparator + nvramFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber))) { - Debug.Console(0, @"Nvram/program{0} directory found", InitialParametersClass.ApplicationNumber); - filePathPrefix = directoryPrefix + dirSeparator + "Nvram" + Debug.Console(0, @"{0}/program{1} directory found", nvramFolder, InitialParametersClass.ApplicationNumber); + filePathPrefix = directoryPrefix + dirSeparator + nvramFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator; } // If neither exists, set path to User/ProgramX else { - Debug.Console(0, @"No previous directory found. Using User/program{0}", InitialParametersClass.ApplicationNumber); - filePathPrefix = directoryPrefix + dirSeparator + "User" + Debug.Console(0, @"No previous directory found. Using {0}/program{1}", userFolder, InitialParametersClass.ApplicationNumber); + filePathPrefix = directoryPrefix + dirSeparator + userFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator; } } @@ -433,14 +450,13 @@ namespace PepperDash.Essentials return; } + uint fusionIpId = 0xf1; + foreach (var roomConfig in ConfigReader.ConfigObject.Rooms) { - var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as EssentialsRoomBase; + var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as IEssentialsRoom; if (room != null) { - // default IPID - uint fusionIpId = 0xf1; - // default to no join map key string fusionJoinMapKey = string.Empty; @@ -457,11 +473,11 @@ namespace PepperDash.Essentials } } - if (room is EssentialsHuddleSpaceRoom) + if (room is IEssentialsHuddleSpaceRoom) { DeviceManager.AddDevice(room); - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion"); + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId); DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase(room, fusionIpId, fusionJoinMapKey)); @@ -469,12 +485,12 @@ namespace PepperDash.Essentials CreateMobileControlBridge(room); } - else if (room is EssentialsHuddleVtc1Room) + else if (room is IEssentialsHuddleVtc1Room) { DeviceManager.AddDevice(room); - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey)); + 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..."); @@ -485,7 +501,7 @@ namespace PepperDash.Essentials DeviceManager.AddDevice(room); Debug.Console(0, Debug.ErrorLogLevel.Notice, - "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion"); + "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId); DeviceManager.AddDevice(new EssentialsTechRoomFusionSystemController((EssentialsTechRoom)room, fusionIpId, fusionJoinMapKey)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge"); @@ -498,16 +514,20 @@ namespace PepperDash.Essentials DeviceManager.AddDevice(room); } + fusionIpId += 1; } else + { Debug.Console(0, Debug.ErrorLogLevel.Notice, "Notice: Cannot create room from config, key '{0}' - Is this intentional? This may be a valid configuration.", roomConfig.Key); + + } } Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded."); } - private static void CreateMobileControlBridge(EssentialsRoomBase room) + private static void CreateMobileControlBridge(IEssentialsRoom room) { var mobileControl = GetMobileControlDevice(); diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index c4a68d4e..c3877706 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -16,7 +16,7 @@ namespace PepperDash.Essentials.Fusion { BooleanSigData CodecIsInCall; - public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId, string joinMapKey) + public EssentialsHuddleVtc1FusionController(IEssentialsHuddleVtc1Room room, uint ipId, string joinMapKey) : base(room, ipId, joinMapKey) { @@ -37,7 +37,7 @@ namespace PepperDash.Essentials.Fusion { try { - var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; + var codec = (Room as IEssentialsHuddleVtc1Room).VideoCodec; if (codec == null) { @@ -141,7 +141,7 @@ namespace PepperDash.Essentials.Fusion void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e) { - var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; + var codec = (Room as IEssentialsHuddleVtc1Room).VideoCodec; CodecIsInCall.InputSig.BoolValue = codec.IsInCall; } @@ -150,7 +150,7 @@ namespace PepperDash.Essentials.Fusion protected override void CreateSymbolAndBasicSigs(uint ipId) { - Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + Debug.Console(0, this, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId); FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); @@ -174,11 +174,11 @@ namespace PepperDash.Essentials.Fusion // Moved to CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.AttributeName, eSigIoMask.InputSigOnly); // Don't think we need to get current status of this as nothing should be alive yet. - (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; + (Room as IEssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; - FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey)); + FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as IEssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); + FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as IEssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey)); CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; @@ -187,7 +187,7 @@ namespace PepperDash.Essentials.Fusion protected override void SetUpSources() { // Sources - var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).SourceListKey); + var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as IEssentialsHuddleVtc1Room).SourceListKey); if (dict != null) { // NEW PROCESS: @@ -238,7 +238,7 @@ namespace PepperDash.Essentials.Fusion else { Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", - (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key); + (Room as IEssentialsHuddleVtc1Room).SourceListKey, Room.Key); } } @@ -259,7 +259,7 @@ namespace PepperDash.Essentials.Fusion display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); } - var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase; + var defaultDisplay = (Room as IEssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase; if (defaultDisplay == null) { Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); @@ -332,7 +332,7 @@ namespace PepperDash.Essentials.Fusion string displayName = string.Format("Display {0} - ", displayIndex); - if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay) + if (display == (Room as IEssentialsHuddleVtc1Room).DefaultDisplay) { // Power on var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); @@ -351,7 +351,7 @@ namespace PepperDash.Essentials.Fusion // Current Source var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); - defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey); }); ; + defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as IEssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey); }); ; } } } diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index 7b548dd5..d172076a 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -149,6 +149,8 @@ + + diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index 6a59cc56..5ec0565d 100644 --- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -47,7 +47,7 @@ namespace PepperDash.Essentials.Room.Config /// Gets and operating, standalone emergegncy object that can be plugged into a room. /// Returns null if there is no emergency defined /// - public static EssentialsRoomEmergencyBase GetEmergency(EssentialsRoomPropertiesConfig props, EssentialsRoomBase room) + public static EssentialsRoomEmergencyBase GetEmergency(EssentialsRoomPropertiesConfig props, IEssentialsRoom room) { // This emergency var emergency = props.Emergency; @@ -96,7 +96,7 @@ namespace PepperDash.Essentials.Room.Config if (behaviour == "trackroomstate") { // Tie LED enable to room power state - var essRoom = room as EssentialsRoomBase; + var essRoom = room as IEssentialsRoom; essRoom.OnFeedback.OutputChange += (o, a) => { if (essRoom.OnFeedback.BoolValue) diff --git a/PepperDashEssentials/Room/Emergency/EsentialsRoomEmergencyContactClosure.cs b/PepperDashEssentials/Room/Emergency/EsentialsRoomEmergencyContactClosure.cs index ab40d2fe..c40ed496 100644 --- a/PepperDashEssentials/Room/Emergency/EsentialsRoomEmergencyContactClosure.cs +++ b/PepperDashEssentials/Room/Emergency/EsentialsRoomEmergencyContactClosure.cs @@ -17,11 +17,11 @@ namespace PepperDash.Essentials.Room public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase { - EssentialsRoomBase Room; + IEssentialsRoom Room; string Behavior; bool TriggerOnClose; - public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, EssentialsRoomBase room) : + public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, IEssentialsRoom room) : base(key) { Room = room; diff --git a/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs b/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs index ccce0af5..d58f6ac7 100644 --- a/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsDualDisplayRoom.cs @@ -645,9 +645,9 @@ namespace PepperDash.Essentials public static void AllRoomsOff() { var allRooms = DeviceManager.AllDevices.Where(d => - d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); + d is IEssentialsHuddleSpaceRoom && !(d as IEssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); foreach (var room in allRooms) - (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff", (room as EssentialsHuddleSpaceRoom).SourceListKey); + (room as IEssentialsHuddleSpaceRoom).RunRouteAction("roomOff", (room as IEssentialsHuddleSpaceRoom).SourceListKey); } #region IPrivacy Members diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs index 9f388252..7a57e9c1 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs @@ -13,7 +13,7 @@ using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { - public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasCurrentVolumeControls, IHasDefaultDisplay + public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IEssentialsHuddleSpaceRoom { public event EventHandler CurrentVolumeDeviceChange; public event SourceInfoChangeHandler CurrentSourceChange; diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index 85cfe90f..5e7b5c37 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -17,8 +17,7 @@ using PepperDash.Essentials.Core.DeviceTypeInterfaces; namespace PepperDash.Essentials { - public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange, - IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback + public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IEssentialsHuddleVtc1Room { private bool _codecExternalSourceChange; public event EventHandler CurrentVolumeDeviceChange; @@ -733,10 +732,10 @@ namespace PepperDash.Essentials /// public static void AllRoomsOff() { - var allRooms = DeviceManager.AllDevices.Where(d => - d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); + var allRooms = DeviceManager.AllDevices.Where(d => + d is IEssentialsRoom && !(d as IEssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions); foreach (var room in allRooms) - (room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); + (room as IEssentialsHuddleSpaceRoom).RunRouteAction("roomOff"); } diff --git a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs new file mode 100644 index 00000000..c7f68e8b --- /dev/null +++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleSpaceRoom.cs @@ -0,0 +1,24 @@ +using System; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Config; + + + +namespace PepperDash.Essentials +{ + public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasDefaultDisplay + { + bool ExcludeFromGlobalFunctions { get; } + + void RunRouteAction(string routeKey); + + EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; } + + IBasicVolumeControls CurrentVolumeControls { get; } + + event EventHandler CurrentVolumeDeviceChange; + } + + +} \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs new file mode 100644 index 00000000..5a6e9f59 --- /dev/null +++ b/PepperDashEssentials/Room/Types/Interfaces/IEssentialsHuddleVtc1Room.cs @@ -0,0 +1,25 @@ + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Config; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.AudioCodec; + +namespace PepperDash.Essentials +{ + public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange, + IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback + { + EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; } + + void RunRouteAction(string routeKey); + + IHasScheduleAwareness ScheduleSource { get; } + + new BoolFeedback InCallFeedback { get; } + + new BoolFeedback PrivacyModeIsOnFeedback { get; } + + string DefaultCodecRouteString { get; } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs index 92552841..9a8006e5 100644 --- a/PepperDashEssentials/UI/EssentialsTouchpanelController.cs +++ b/PepperDashEssentials/UI/EssentialsTouchpanelController.cs @@ -232,7 +232,7 @@ namespace PepperDash.Essentials // spin up different room drivers depending on room type var room = DeviceManager.GetDeviceForKey(props.DefaultRoomKey); - if (room is EssentialsHuddleSpaceRoom) + if (room is IEssentialsHuddleSpaceRoom) { // Screen Saver Driver mainDriver.ScreenSaverController = new ScreenSaverController(mainDriver, props); @@ -246,7 +246,7 @@ namespace PepperDash.Essentials var avDriver = new EssentialsHuddlePanelAvFunctionsDriver(mainDriver, props); avDriver.DefaultRoomKey = props.DefaultRoomKey; mainDriver.AvDriver = avDriver; - avDriver.CurrentRoom = room as EssentialsHuddleSpaceRoom; + avDriver.CurrentRoom = room as IEssentialsHuddleSpaceRoom; // Environment Driver if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0) @@ -280,7 +280,7 @@ namespace PepperDash.Essentials tsw.Down.UserObject = new Action(avDriver.VolumeDownPress); } } - else if (room is EssentialsHuddleVtc1Room) + else if (room is IEssentialsHuddleVtc1Room) { Debug.Console(0, panelController, "Adding huddle space VTC AV driver"); @@ -294,11 +294,11 @@ namespace PepperDash.Essentials var avDriver = new EssentialsHuddleVtc1PanelAvFunctionsDriver(mainDriver, props); var codecDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver(panelController.Panel, avDriver, - (room as EssentialsHuddleVtc1Room).VideoCodec, mainDriver.HeaderDriver); + (room as IEssentialsHuddleVtc1Room).VideoCodec, mainDriver.HeaderDriver); avDriver.SetVideoCodecDriver(codecDriver); avDriver.DefaultRoomKey = props.DefaultRoomKey; mainDriver.AvDriver = avDriver; - avDriver.CurrentRoom = room as EssentialsHuddleVtc1Room; + avDriver.CurrentRoom = room as IEssentialsHuddleVtc1Room; // Environment Driver if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0) diff --git a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs index 23ab797e..293fddd5 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs @@ -764,6 +764,10 @@ namespace PepperDash.Essentials /// public const uint NextMeetingModalVisible = 15049; /// + /// 15050 + /// + public const uint NextMeetingNotificationRibbonVisible = 15050; + /// /// 15051 /// public const uint Display1SelectPressAndFb = 15051; diff --git a/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs b/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs index 1aeb7a3c..4b29061d 100644 --- a/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs +++ b/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs @@ -146,18 +146,18 @@ // } -// void CurrentRoom_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) +// void CurrentRoom_CurrentSourceInfoChange(IEssentialsRoom room, SourceListItem info, ChangeType type) // { // } -// void CurrentRoom_CurrentDisplay1SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) +// void CurrentRoom_CurrentDisplay1SourceChange(IEssentialsRoom room, SourceListItem info, ChangeType type) // { // TriList.StringInput[UIStringJoin.Display1SourceLabel].StringValue = PendingSource.PreferredName; // } -// void CurrentRoom_CurrentDisplay2SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) +// void CurrentRoom_CurrentDisplay2SourceChange(IEssentialsRoom room, SourceListItem info, ChangeType type) // { // TriList.StringInput[UIStringJoin.Display2SourceLabel].StringValue = PendingSource.PreferredName; // } diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs index f9092958..818498d7 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs @@ -52,7 +52,7 @@ namespace PepperDash.Essentials CaretInterlock = new JoinedSigInterlock(TriList); } - void SetUpGear(IAVDriver avDriver, EssentialsRoomBase currentRoom) + void SetUpGear(IAVDriver avDriver, IEssentialsRoom currentRoom) { // Gear TriList.SetString(UIStringJoin.HeaderButtonIcon5, "Gear"); @@ -105,7 +105,7 @@ namespace PepperDash.Essentials { string message = null; var room = DeviceManager.GetDeviceForKey(Config.DefaultRoomKey) - as EssentialsHuddleSpaceRoom; + as IEssentialsHuddleSpaceRoom; if (room != null) message = room.PropertiesConfig.HelpMessage; else @@ -222,7 +222,7 @@ namespace PepperDash.Essentials /// /// Sets up Header Buttons for the EssentialsHuddleVtc1Room type /// - public void SetupHeaderButtons(EssentialsHuddleVtc1PanelAvFunctionsDriver avDriver, EssentialsHuddleVtc1Room currentRoom) + public void SetupHeaderButtons(EssentialsHuddleVtc1PanelAvFunctionsDriver avDriver, IEssentialsHuddleVtc1Room currentRoom) { HeaderButtonsAreSetUp = false; @@ -284,7 +284,7 @@ namespace PepperDash.Essentials /// /// Sets up Header Buttons for the EssentialsHuddleSpaceRoom type /// - public void SetupHeaderButtons(EssentialsHuddlePanelAvFunctionsDriver avDriver, EssentialsHuddleSpaceRoom currentRoom) + public void SetupHeaderButtons(EssentialsHuddlePanelAvFunctionsDriver avDriver, IEssentialsHuddleSpaceRoom currentRoom) { HeaderButtonsAreSetUp = false; diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs index 4a8cb53c..9c8514ea 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsPresentationPanelAvFunctionsDriver.cs @@ -983,7 +983,7 @@ // /// // /// Handles source change // /// -// void _CurrentRoom_SourceInfoChange(EssentialsRoomBase room, +// void _CurrentRoom_SourceInfoChange(IEssentialsRoom room, // SourceListItem info, ChangeType change) // { // if (change == ChangeType.WillChange) @@ -995,7 +995,7 @@ // /// // /// // /// -// void _CurrentRoom_CurrentDisplay1SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) +// void _CurrentRoom_CurrentDisplay1SourceChange(IEssentialsRoom room, SourceListItem info, ChangeType type) // { // if (type == ChangeType.DidChange) // { @@ -1021,7 +1021,7 @@ // /// // /// // /// -// void _CurrentRoom_CurrentDisplay2SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) +// void _CurrentRoom_CurrentDisplay2SourceChange(IEssentialsRoom room, SourceListItem info, ChangeType type) // { // if (type == ChangeType.DidChange) // { diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs index f22565e5..a0d5947c 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs @@ -78,7 +78,7 @@ namespace PepperDash.Essentials /// /// /// - public EssentialsHuddleSpaceRoom CurrentRoom + public IEssentialsHuddleSpaceRoom CurrentRoom { get { return _CurrentRoom; } set @@ -86,7 +86,7 @@ namespace PepperDash.Essentials SetCurrentRoom(value); } } - EssentialsHuddleSpaceRoom _CurrentRoom; + IEssentialsHuddleSpaceRoom _CurrentRoom; /// /// @@ -498,7 +498,7 @@ namespace PepperDash.Essentials TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true; // Run default source when room is off and share is pressed if (!CurrentRoom.OnFeedback.BoolValue) - CurrentRoom.RunDefaultPresentRoute(); + (CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute(); } @@ -583,7 +583,7 @@ namespace PepperDash.Essentials void UiSelectSource(string key) { // Run the route and when it calls back, show the source - CurrentRoom.RunRouteAction(key, new Action(() => { })); + CurrentRoom.RunRouteAction(key); } /// @@ -745,7 +745,7 @@ namespace PepperDash.Essentials /// /// Helper for property setter. Sets the panel to the given room, latching up all functionality /// - public void RefreshCurrentRoom(EssentialsHuddleSpaceRoom room) + public void RefreshCurrentRoom(IEssentialsHuddleSpaceRoom room) { if (_CurrentRoom != null) { @@ -836,7 +836,7 @@ namespace PepperDash.Essentials } } - void SetCurrentRoom(EssentialsHuddleSpaceRoom room) + void SetCurrentRoom(IEssentialsHuddleSpaceRoom room) { if (_CurrentRoom == room) return; // Disconnect current (probably never called) @@ -871,7 +871,7 @@ namespace PepperDash.Essentials UpdateMCJoins(_CurrentRoom); } - void UpdateMCJoins(EssentialsHuddleSpaceRoom room) + void UpdateMCJoins(IEssentialsHuddleSpaceRoom room) { TriList.SetString(UIStringJoin.RoomMcUrl, room.MobileControlRoomBridge.McServerUrl); TriList.SetString(UIStringJoin.RoomMcQrCodeUrl, room.MobileControlRoomBridge.QrCodeUrl); diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs index b8e1dfd6..776e1034 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs @@ -1,326 +1,326 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.DeviceSupport; - -using PepperDash.Core; -using PepperDash.Essentials; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; +using PepperDash.Essentials; using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.SmartObjects; -using PepperDash.Essentials.Core.Touchpanels.Keyboards; -using PepperDash.Essentials.Devices.Displays; -using PepperDash.Essentials.Room.Config; - -namespace PepperDash.Essentials.UIDrivers -{ - public class EssentialsHuddleTechPageDriver : PanelDriverBase - { - /// - /// - /// - SmartObjectDynamicList MenuList; - /// - /// - /// - SubpageReferenceList StatusList; - /// - /// The list of display controls - /// - SubpageReferenceList DisplayList; - /// - /// References lines in the list against device instances - /// - Dictionary StatusListDeviceIndexes; - /// - /// - /// - JoinedSigInterlock PagesInterlock; - - /// - /// 1 - /// - public const uint JoinText = 1; - - CTimer PinAuthorizedTimer; - - EssentialsRoomTechConfig Config; - - StringBuilder PinEntryBuilder = new StringBuilder(4); - - bool IsAuthorized; - - SmartObjectNumeric PinKeypad; - - /// - /// - /// - /// - /// - public EssentialsHuddleTechPageDriver(BasicTriListWithSmartObject trilist, EssentialsRoomTechConfig config) - : base(trilist) - { - Config = config; - - PagesInterlock = new JoinedSigInterlock(trilist); - PagesInterlock.SetButDontShow(UIBoolJoin.TechSystemStatusVisible); - - trilist.SetSigFalseAction(UIBoolJoin.TechExitButton, Hide); - - MenuList = new SmartObjectDynamicList(trilist.SmartObjects[UISmartObjectJoin.TechMenuList], - true, 3100); - - MenuList.SetFeedback(1, true); // initial fb - ushort count = 0; - - MenuList.SetItemMainText(1, "System Status"); - MenuList.SetItemButtonAction(1, b => { - if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechSystemStatusVisible); - MenuList.SetFeedback(1, true); - }); - - MenuList.SetItemMainText(2, "Display Controls"); - MenuList.SetItemButtonAction(2, b => { - if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechDisplayControlsVisible); - MenuList.SetFeedback(2, true); - }); - - count = 2; - - // Don't show panel setup on iPad or xpanel - if (TriList is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button) - { - count++; - MenuList.SetItemMainText(count, "Panel Setup"); - MenuList.SetItemButtonAction(count, b => - { - if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechPanelSetupVisible); - MenuList.SetFeedback(count, true); - }); - } - - MenuList.Count = count; - BuildStatusList(); - BuildDisplayList(); - SetupPinModal(); - } - - /// - /// - /// - public override void Show() - { - // divert to PIN if we need auth - if (IsAuthorized) - { - // Cancel the auth timer so we don't deauth after coming back in - if (PinAuthorizedTimer != null) - PinAuthorizedTimer.Stop(); - - TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, true); - PagesInterlock.Show(); - base.Show(); - } - else - { - TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, true); - } - } - - /// - /// - /// - public override void Hide() - { - // Leave it authorized for 60 seconds. - if (IsAuthorized) - PinAuthorizedTimer = new CTimer(o => { - IsAuthorized = false; - PinAuthorizedTimer = null; - }, 60000); - TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, false); - PagesInterlock.Hide(); - base.Hide(); - } - - /// - /// Wire up the keypad and buttons - /// - void SetupPinModal() - { - TriList.SetSigFalseAction(UIBoolJoin.PinDialogCancelPress, CancelPinDialog); - PinKeypad = new SmartObjectNumeric(TriList.SmartObjects[UISmartObjectJoin.TechPinDialogKeypad], true); - PinKeypad.Digit0.UserObject = new Action(b => { if (b)DialPinDigit('0'); }); - PinKeypad.Digit1.UserObject = new Action(b => { if (b)DialPinDigit('1'); }); - PinKeypad.Digit2.UserObject = new Action(b => { if (b)DialPinDigit('2'); }); - PinKeypad.Digit3.UserObject = new Action(b => { if (b)DialPinDigit('3'); }); - PinKeypad.Digit4.UserObject = new Action(b => { if (b)DialPinDigit('4'); }); - PinKeypad.Digit5.UserObject = new Action(b => { if (b)DialPinDigit('5'); }); - PinKeypad.Digit6.UserObject = new Action(b => { if (b)DialPinDigit('6'); }); - PinKeypad.Digit7.UserObject = new Action(b => { if (b)DialPinDigit('7'); }); - PinKeypad.Digit8.UserObject = new Action(b => { if (b)DialPinDigit('8'); }); - PinKeypad.Digit9.UserObject = new Action(b => { if (b)DialPinDigit('9'); }); - } - - /// - /// - /// - /// - void DialPinDigit(char d) - { - PinEntryBuilder.Append(d); - var len = PinEntryBuilder.Length; - SetPinDotsFeedback(len); - - // check it! - if (len == 4) - { - if (Config.Password == PinEntryBuilder.ToString()) - { - IsAuthorized = true; - SetPinDotsFeedback(0); - TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false); - Show(); - } - else - { - SetPinDotsFeedback(0); - TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, true); - new CTimer(o => - { - TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, false); - }, 1500); - } - - PinEntryBuilder.Remove(0, len); // clear it either way - } - } - - /// - /// Draws the dots as pin is entered - /// - /// - void SetPinDotsFeedback(int len) - { - TriList.SetBool(UIBoolJoin.PinDialogDot1, len >= 1); - TriList.SetBool(UIBoolJoin.PinDialogDot2, len >= 2); - TriList.SetBool(UIBoolJoin.PinDialogDot3, len >= 3); - TriList.SetBool(UIBoolJoin.PinDialogDot4, len == 4); - - } - - /// - /// Does what it says - /// - void CancelPinDialog() - { - PinEntryBuilder.Remove(0, PinEntryBuilder.Length); - TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false); - } - - - /// - /// - /// - void BuildStatusList() - { - StatusList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechStatusList, 3, 3, 3); - StatusListDeviceIndexes = new Dictionary(); - uint i = 0; - foreach (var d in DeviceManager.AllDevices) - { - // make sure it is both ICommunicationMonitor and a Device - var sd = d as ICommunicationMonitor; - if (sd == null) - continue; - var dd = sd as Device; - if(dd == null) - continue; - i++; - StatusList.StringInputSig(i, 1).StringValue = dd.Name; - StatusList.UShortInputSig(i, 1).UShortValue = (ushort)sd.CommunicationMonitor.Status; - StatusListDeviceIndexes.Add(sd, i); - sd.CommunicationMonitor.StatusChange += CommunicationMonitor_StatusChange ; - } - StatusList.Count = (ushort)i; - } - - /// - /// Builds the list of display controls - /// - void BuildDisplayList() - { - DisplayList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechDisplayControlsList, 10, 3, 3); - - var devKeys = ConfigReader.ConfigObject.Devices.Where(d => - d.Group.Equals("display", StringComparison.OrdinalIgnoreCase) - || d.Group.Equals("projector", StringComparison.OrdinalIgnoreCase)) - .Select(dd => dd.Key); - var disps = DeviceManager.AllDevices.Where(d => - devKeys.Contains(d.Key)); - ushort i = 0; - foreach (var disp in disps) - { - var display = disp as DisplayBase; - if (display != null) - { - i++; - DisplayList.StringInputSig(i, 1).StringValue = display.Name; - DisplayList.GetBoolFeedbackSig(i, 1).SetSigFalseAction(display.PowerOn); - DisplayList.GetBoolFeedbackSig(i, 2).SetSigFalseAction(display.PowerOff); - if (display is TwoWayDisplayBase) - { - var powerOnSig = DisplayList.BoolInputSig(i, 1); - (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkInputSig(powerOnSig); - - var powerOffSig = DisplayList.BoolInputSig(1, 2); - (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkComplementInputSig(powerOffSig); - } - DisplayList.GetBoolFeedbackSig(i, 3).SetSigFalseAction(() => - { if (display is IInputHdmi1) (display as IInputHdmi1).InputHdmi1(); }); - DisplayList.GetBoolFeedbackSig(i, 4).SetSigFalseAction(() => - { if (display is IInputHdmi2) (display as IInputHdmi2).InputHdmi2(); }); - DisplayList.GetBoolFeedbackSig(i, 5).SetSigFalseAction(() => - { if (display is IInputHdmi3) (display as IInputHdmi3).InputHdmi3(); }); - //DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() => - //{ if (display is IInputHdmi4) (display as IInputHdmi4).InputHdmi4(); }); - DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() => - { if (display is IInputDisplayPort1) (display as IInputDisplayPort1).InputDisplayPort1(); }); - - - // Figure out some way to provide current input feedback - if (display is TwoWayDisplayBase) - { - (display as TwoWayDisplayBase).CurrentInputFeedback.OutputChange += CurrentInputFeedback_OutputChange; - } - } - - - } - - DisplayList.Count = i; - } - - - void CurrentInputFeedback_OutputChange(object sender, EventArgs e) - { - - } - - /// - /// - /// - void CommunicationMonitor_StatusChange(object sender, MonitorStatusChangeEventArgs e) - { - var c = sender as ICommunicationMonitor; - if (StatusListDeviceIndexes.ContainsKey(c)) - { - var i = StatusListDeviceIndexes[c]; - StatusList.UShortInputSig(i, 1).UShortValue = (ushort)e.Status; - } - } - } +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.SmartObjects; +using PepperDash.Essentials.Core.Touchpanels.Keyboards; +using PepperDash.Essentials.Devices.Displays; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials.UIDrivers +{ + public class EssentialsHuddleTechPageDriver : PanelDriverBase + { + /// + /// + /// + SmartObjectDynamicList MenuList; + /// + /// + /// + SubpageReferenceList StatusList; + /// + /// The list of display controls + /// + SubpageReferenceList DisplayList; + /// + /// References lines in the list against device instances + /// + Dictionary StatusListDeviceIndexes; + /// + /// + /// + JoinedSigInterlock PagesInterlock; + + /// + /// 1 + /// + public const uint JoinText = 1; + + CTimer PinAuthorizedTimer; + + EssentialsRoomTechConfig Config; + + StringBuilder PinEntryBuilder = new StringBuilder(4); + + bool IsAuthorized; + + SmartObjectNumeric PinKeypad; + + /// + /// + /// + /// + /// + public EssentialsHuddleTechPageDriver(BasicTriListWithSmartObject trilist, EssentialsRoomTechConfig config) + : base(trilist) + { + Config = config; + + PagesInterlock = new JoinedSigInterlock(trilist); + PagesInterlock.SetButDontShow(UIBoolJoin.TechSystemStatusVisible); + + trilist.SetSigFalseAction(UIBoolJoin.TechExitButton, Hide); + + MenuList = new SmartObjectDynamicList(trilist.SmartObjects[UISmartObjectJoin.TechMenuList], + true, 3100); + + MenuList.SetFeedback(1, true); // initial fb + ushort count = 0; + + MenuList.SetItemMainText(1, "System Status"); + MenuList.SetItemButtonAction(1, b => { + if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechSystemStatusVisible); + MenuList.SetFeedback(1, true); + }); + + MenuList.SetItemMainText(2, "Display Controls"); + MenuList.SetItemButtonAction(2, b => { + if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechDisplayControlsVisible); + MenuList.SetFeedback(2, true); + }); + + count = 2; + + // Don't show panel setup on iPad or xpanel + if (TriList is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button) + { + count++; + MenuList.SetItemMainText(count, "Panel Setup"); + MenuList.SetItemButtonAction(count, b => + { + if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechPanelSetupVisible); + MenuList.SetFeedback(count, true); + }); + } + + MenuList.Count = count; + BuildStatusList(); + BuildDisplayList(); + SetupPinModal(); + } + + /// + /// + /// + public override void Show() + { + // divert to PIN if we need auth + if (IsAuthorized) + { + // Cancel the auth timer so we don't deauth after coming back in + if (PinAuthorizedTimer != null) + PinAuthorizedTimer.Stop(); + + TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, true); + PagesInterlock.Show(); + base.Show(); + } + else + { + TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, true); + } + } + + /// + /// + /// + public override void Hide() + { + // Leave it authorized for 60 seconds. + if (IsAuthorized) + PinAuthorizedTimer = new CTimer(o => { + IsAuthorized = false; + PinAuthorizedTimer = null; + }, 60000); + TriList.SetBool(UIBoolJoin.TechCommonItemsVisbible, false); + PagesInterlock.Hide(); + base.Hide(); + } + + /// + /// Wire up the keypad and buttons + /// + void SetupPinModal() + { + TriList.SetSigFalseAction(UIBoolJoin.PinDialogCancelPress, CancelPinDialog); + PinKeypad = new SmartObjectNumeric(TriList.SmartObjects[UISmartObjectJoin.TechPinDialogKeypad], true); + PinKeypad.Digit0.UserObject = new Action(b => { if (b)DialPinDigit('0'); }); + PinKeypad.Digit1.UserObject = new Action(b => { if (b)DialPinDigit('1'); }); + PinKeypad.Digit2.UserObject = new Action(b => { if (b)DialPinDigit('2'); }); + PinKeypad.Digit3.UserObject = new Action(b => { if (b)DialPinDigit('3'); }); + PinKeypad.Digit4.UserObject = new Action(b => { if (b)DialPinDigit('4'); }); + PinKeypad.Digit5.UserObject = new Action(b => { if (b)DialPinDigit('5'); }); + PinKeypad.Digit6.UserObject = new Action(b => { if (b)DialPinDigit('6'); }); + PinKeypad.Digit7.UserObject = new Action(b => { if (b)DialPinDigit('7'); }); + PinKeypad.Digit8.UserObject = new Action(b => { if (b)DialPinDigit('8'); }); + PinKeypad.Digit9.UserObject = new Action(b => { if (b)DialPinDigit('9'); }); + } + + /// + /// + /// + /// + void DialPinDigit(char d) + { + PinEntryBuilder.Append(d); + var len = PinEntryBuilder.Length; + SetPinDotsFeedback(len); + + // check it! + if (len == 4) + { + if (Config.Password == PinEntryBuilder.ToString()) + { + IsAuthorized = true; + SetPinDotsFeedback(0); + TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false); + Show(); + } + else + { + SetPinDotsFeedback(0); + TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, true); + new CTimer(o => + { + TriList.SetBool(UIBoolJoin.PinDialogErrorVisible, false); + }, 1500); + } + + PinEntryBuilder.Remove(0, len); // clear it either way + } + } + + /// + /// Draws the dots as pin is entered + /// + /// + void SetPinDotsFeedback(int len) + { + TriList.SetBool(UIBoolJoin.PinDialogDot1, len >= 1); + TriList.SetBool(UIBoolJoin.PinDialogDot2, len >= 2); + TriList.SetBool(UIBoolJoin.PinDialogDot3, len >= 3); + TriList.SetBool(UIBoolJoin.PinDialogDot4, len == 4); + + } + + /// + /// Does what it says + /// + void CancelPinDialog() + { + PinEntryBuilder.Remove(0, PinEntryBuilder.Length); + TriList.SetBool(UIBoolJoin.PinDialog4DigitVisible, false); + } + + + /// + /// + /// + void BuildStatusList() + { + StatusList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechStatusList, 3, 3, 3); + StatusListDeviceIndexes = new Dictionary(); + uint i = 0; + foreach (var d in DeviceManager.AllDevices) + { + // make sure it is both ICommunicationMonitor and a Device + var sd = d as ICommunicationMonitor; + if (sd == null) + continue; + var dd = sd as Device; + if(dd == null) + continue; + i++; + StatusList.StringInputSig(i, 1).StringValue = dd.Name; + StatusList.UShortInputSig(i, 1).UShortValue = (ushort)sd.CommunicationMonitor.Status; + StatusListDeviceIndexes.Add(sd, i); + sd.CommunicationMonitor.StatusChange += CommunicationMonitor_StatusChange ; + } + StatusList.Count = (ushort)i; + } + + /// + /// Builds the list of display controls + /// + void BuildDisplayList() + { + DisplayList = new SubpageReferenceList(TriList, UISmartObjectJoin.TechDisplayControlsList, 10, 3, 3); + + var devKeys = ConfigReader.ConfigObject.Devices.Where(d => + d.Group.Equals("display", StringComparison.OrdinalIgnoreCase) + || d.Group.Equals("projector", StringComparison.OrdinalIgnoreCase)) + .Select(dd => dd.Key); + var disps = DeviceManager.AllDevices.Where(d => + devKeys.Contains(d.Key)); + ushort i = 0; + foreach (var disp in disps) + { + var display = disp as DisplayBase; + if (display != null) + { + i++; + DisplayList.StringInputSig(i, 1).StringValue = display.Name; + DisplayList.GetBoolFeedbackSig(i, 1).SetSigFalseAction(display.PowerOn); + DisplayList.GetBoolFeedbackSig(i, 2).SetSigFalseAction(display.PowerOff); + if (display is TwoWayDisplayBase) + { + var powerOnSig = DisplayList.BoolInputSig(i, 1); + (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkInputSig(powerOnSig); + + var powerOffSig = DisplayList.BoolInputSig(1, 2); + (display as TwoWayDisplayBase).PowerIsOnFeedback.LinkComplementInputSig(powerOffSig); + } + DisplayList.GetBoolFeedbackSig(i, 3).SetSigFalseAction(() => + { if (display is IInputHdmi1) (display as IInputHdmi1).InputHdmi1(); }); + DisplayList.GetBoolFeedbackSig(i, 4).SetSigFalseAction(() => + { if (display is IInputHdmi2) (display as IInputHdmi2).InputHdmi2(); }); + DisplayList.GetBoolFeedbackSig(i, 5).SetSigFalseAction(() => + { if (display is IInputHdmi3) (display as IInputHdmi3).InputHdmi3(); }); + //DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() => + //{ if (display is IInputHdmi4) (display as IInputHdmi4).InputHdmi4(); }); + DisplayList.GetBoolFeedbackSig(i, 6).SetSigFalseAction(() => + { if (display is IInputDisplayPort1) (display as IInputDisplayPort1).InputDisplayPort1(); }); + + + // Figure out some way to provide current input feedback + if (display is TwoWayDisplayBase) + { + (display as TwoWayDisplayBase).CurrentInputFeedback.OutputChange += CurrentInputFeedback_OutputChange; + } + } + + + } + + DisplayList.Count = i; + } + + + void CurrentInputFeedback_OutputChange(object sender, EventArgs e) + { + + } + + /// + /// + /// + void CommunicationMonitor_StatusChange(object sender, MonitorStatusChangeEventArgs e) + { + var c = sender as ICommunicationMonitor; + if (StatusListDeviceIndexes.ContainsKey(c)) + { + var i = StatusListDeviceIndexes[c]; + StatusList.UShortInputSig(i, 1).UShortValue = (ushort)e.Status; + } + } + } } \ No newline at end of file diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index fd39d4bc..a1ceb50b 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -50,7 +50,7 @@ namespace PepperDash.Essentials /// /// /// - public EssentialsHuddleVtc1Room CurrentRoom + public IEssentialsHuddleVtc1Room CurrentRoom { get { return _CurrentRoom; } set @@ -58,7 +58,7 @@ namespace PepperDash.Essentials SetCurrentRoom(value); } } - EssentialsHuddleVtc1Room _CurrentRoom; + IEssentialsHuddleVtc1Room _CurrentRoom; /// /// For hitting feedbacks @@ -652,7 +652,7 @@ namespace PepperDash.Essentials if (!CurrentRoom.OnFeedback.BoolValue) { // If there's no default, show UI elements - if (!CurrentRoom.RunDefaultPresentRoute()) + if (!(CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute()) TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); } } @@ -743,7 +743,7 @@ namespace PepperDash.Essentials void UiSelectSource(string key) { // Run the route and when it calls back, show the source - CurrentRoom.RunRouteAction(key, new Action(() => { })); + CurrentRoom.RunRouteAction(key); } /// @@ -894,7 +894,7 @@ namespace PepperDash.Essentials /// /// Helper for property setter. Sets the panel to the given room, latching up all functionality /// - void RefreshCurrentRoom(EssentialsHuddleVtc1Room room) + void RefreshCurrentRoom(IEssentialsHuddleVtc1Room room) { if (_CurrentRoom != null) @@ -969,7 +969,7 @@ namespace PepperDash.Essentials } } - void SetCurrentRoom(EssentialsHuddleVtc1Room room) + void SetCurrentRoom(IEssentialsHuddleVtc1Room room) { if (_CurrentRoom == room) return; // Disconnect current (probably never called) @@ -1004,7 +1004,7 @@ namespace PepperDash.Essentials UpdateMCJoins(_CurrentRoom); } - void UpdateMCJoins(EssentialsHuddleVtc1Room room) + void UpdateMCJoins(IEssentialsHuddleVtc1Room room) { TriList.SetString(UIStringJoin.RoomMcUrl, room.MobileControlRoomBridge.McServerUrl); TriList.SetString(UIStringJoin.RoomMcQrCodeUrl, room.MobileControlRoomBridge.QrCodeUrl); @@ -1443,7 +1443,7 @@ namespace PepperDash.Essentials /// public interface IAVWithVCDriver : IAVDriver { - EssentialsHuddleVtc1Room CurrentRoom { get; } + IEssentialsHuddleVtc1Room CurrentRoom { get; } PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController Keyboard { get; } /// diff --git a/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs b/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs index e43abc09..2543e664 100644 --- a/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs +++ b/PepperDashEssentials/UIDrivers/SmartObjectRoomsList.cs @@ -38,7 +38,7 @@ namespace PepperDash.Essentials /// /// Sets feedback for the given room /// - public void SetFeedbackForRoom(EssentialsHuddleSpaceRoom room) + public void SetFeedbackForRoom(IEssentialsHuddleSpaceRoom room) { var itemToSet = Items.FirstOrDefault(i => i.Room == room); if (itemToSet != null) @@ -48,11 +48,11 @@ namespace PepperDash.Essentials public class SmartObjectRoomsListItem { - public EssentialsHuddleSpaceRoom Room { get; private set; } + public IEssentialsHuddleSpaceRoom Room { get; private set; } SmartObjectRoomsList Parent; public uint Index { get; private set; } - public SmartObjectRoomsListItem(EssentialsHuddleSpaceRoom room, uint index, SmartObjectRoomsList parent, + public SmartObjectRoomsListItem(IEssentialsHuddleSpaceRoom room, uint index, SmartObjectRoomsList parent, Action buttonAction) { Room = room; diff --git a/PepperDashEssentials/UIDrivers/SourceChangeArgs.cs b/PepperDashEssentials/UIDrivers/SourceChangeArgs.cs index c777c08e..5c034084 100644 --- a/PepperDashEssentials/UIDrivers/SourceChangeArgs.cs +++ b/PepperDashEssentials/UIDrivers/SourceChangeArgs.cs @@ -12,5 +12,5 @@ namespace PepperDash.Essentials ///// ///// The handler type for a Room's SourceInfoChange ///// - //public delegate void SourceInfoChangeHandler(EssentialsRoomBase room, SourceListItem info, ChangeType type); + //public delegate void SourceInfoChangeHandler(IEssentialsRoom room, SourceListItem info, ChangeType type); } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/C2nIo/C2nIoController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/C2nIo/C2nIoController.cs new file mode 100644 index 00000000..0b70288f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/C2nIo/C2nIoController.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.GeneralIO; +using PepperDash.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + public class C2NIoController:CrestronGenericBaseDevice, IComPorts, IIROutputPorts, IRelayPorts + { + private C2nIo _device; + + public C2NIoController(string key, Func preActivationFunc, DeviceConfig config):base(key, config.Name) + { + AddPreActivationAction(() => + { + _device = preActivationFunc(config); + + RegisterCrestronGenericBase(_device); + }); + } + + #region Implementation of IComPorts + + public CrestronCollection ComPorts + { + get { return _device.ComPorts; } + } + + public int NumberOfComPorts + { + get { return _device.NumberOfComPorts; } + } + + #endregion + + #region Implementation of IIROutputPorts + + public CrestronCollection IROutputPorts + { + get { return _device.IROutputPorts; } + } + + public int NumberOfIROutputPorts + { + get { return _device.NumberOfIROutputPorts; } + } + + #endregion + + #region Implementation of IRelayPorts + + public CrestronCollection RelayPorts + { + get { return _device.RelayPorts; } + } + + public int NumberOfRelayPorts + { + get { return _device.NumberOfRelayPorts; } + } + + #endregion + } + + public class C2NIoControllerFactory : EssentialsDeviceFactory + { + public C2NIoControllerFactory() + { + TypeNames = new List() { "c2nio" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new C2N-IO Device"); + + return new C2NIoController(dc.Key, GetC2NIoDevice, dc); + } + + static C2nIo GetC2NIoDevice(DeviceConfig dc) + { + var control = CommFactory.GetControlPropertiesConfig(dc); + var cresnetId = control.CresnetIdInt; + var branchId = control.ControlPortNumber; + var parentKey = string.IsNullOrEmpty(control.ControlPortDevKey) ? "processor" : control.ControlPortDevKey; + + if (parentKey.Equals("processor", StringComparison.CurrentCultureIgnoreCase)) + { + Debug.Console(0, "Device {0} is a valid cresnet master - creating new C2nIo", parentKey); + return new C2nIo(cresnetId, Global.ControlSystem); + } + var cresnetBridge = DeviceManager.GetDeviceForKey(parentKey) as IHasCresnetBranches; + + if (cresnetBridge != null) + { + Debug.Console(0, "Device {0} is a valid cresnet master - creating new C2nIo", parentKey); + return new C2nIo(cresnetId, cresnetBridge.CresnetBranches[branchId]); + } + Debug.Console(0, "Device {0} is not a valid cresnet master", parentKey); + return null; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs index c87e7865..a2dda43a 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IMobileControl.cs @@ -8,7 +8,7 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces /// public interface IMobileControl : IKeyed { - void CreateMobileControlRoomBridge(EssentialsRoomBase room, IMobileControl parent); + void CreateMobileControlRoomBridge(IEssentialsRoom room, IMobileControl parent); void LinkSystemMonitorToAppServer(); } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs index a1930cf0..2dda0bf8 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs @@ -77,10 +77,9 @@ namespace PepperDash.Essentials.Core var mParams = method.GetParameters(); var convertedParams = mParams - .Select((p, i) => Convert.ChangeType(action.Params[i], p.ParameterType, - System.Globalization.CultureInfo.InvariantCulture)) + .Select((p, i) => ConvertType(action.Params[i], p.ParameterType)) .ToArray(); - var ret = method.Invoke(obj, convertedParams); + method.Invoke(obj, convertedParams); CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name, action.DeviceKey); @@ -91,6 +90,23 @@ namespace PepperDash.Essentials.Core ex.Message);} } + private static object ConvertType(object value, Type conversionType) + { + if (!conversionType.IsEnum) + { + return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture); + } + + var stringValue = Convert.ToString(value); + + if (String.IsNullOrEmpty(stringValue)) + { + throw new InvalidCastException( + String.Format("{0} cannot be converted to a string prior to conversion to enum")); + } + return Enum.Parse(conversionType, stringValue, true); + } + /// /// Gets the properties on a device /// @@ -275,6 +291,8 @@ namespace PepperDash.Essentials.Core //var props = t.GetProperties().Select(p => new PropertyNameType(p, obj)); //return JsonConvert.SerializeObject(props, Formatting.Indented); } + + } public class DeviceActionWrapper diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs index cca16ec7..df864550 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs @@ -60,6 +60,7 @@ namespace PepperDash.Essentials.Core DeviceCriticalSection.Enter(); AddDeviceEnabled = false; // PreActivate all devices + Debug.Console(0,"****PreActivation starting...****"); foreach (var d in Devices.Values) { try @@ -69,9 +70,12 @@ namespace PepperDash.Essentials.Core } catch (Exception e) { - Debug.Console(0, d, "ERROR: Device PreActivation failure:\r{0}", e); + Debug.Console(0, d, "ERROR: Device {1} PreActivation failure: {0}", e.Message, d.Key); + Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace); } } + Debug.Console(0, "****PreActivation complete****"); + Debug.Console(0, "****Activation starting...****"); // Activate all devices foreach (var d in Devices.Values) @@ -83,10 +87,14 @@ namespace PepperDash.Essentials.Core } catch (Exception e) { - Debug.Console(0, d, "ERROR: Device Activation failure:\r{0}", e); + Debug.Console(0, d, "ERROR: Device {1} Activation failure: {0}", e.Message, d.Key); + Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace); } } + Debug.Console(0, "****Activation complete****"); + Debug.Console(0, "****PostActivation starting...****"); + // PostActivate all devices foreach (var d in Devices.Values) { @@ -97,10 +105,13 @@ namespace PepperDash.Essentials.Core } catch (Exception e) { - Debug.Console(0, d, "ERROR: Device PostActivation failure:\r{0}", e); + Debug.Console(0, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key); + Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace); } } + Debug.Console(0, "****PostActivation complete****"); + OnAllDevicesActivated(); } finally @@ -435,7 +446,7 @@ namespace PepperDash.Essentials.Core var min = Convert.ToUInt32(timeout); device.StreamDebugging.SetDebuggingWithSpecificTimeout(debugSetting, min); - Debug.Console(0, "Device: '{0}' debug level set to {1) for {2} minutes", deviceKey, debugSetting, min); + Debug.Console(0, "Device: '{0}' debug level set to {1} for {2} minutes", deviceKey, debugSetting, min); } catch (Exception e) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs index 8d7f2c8d..46fa819b 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs @@ -1,113 +1,134 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharp.Reflection; - -using PepperDash.Core; -using PepperDash.Essentials.Core.Config; - -namespace PepperDash.Essentials.Core -{ - /// - /// Defines the basic needs for an EssentialsDevice to enable it to be build by an IDeviceFactory class - /// - [Description("The base Essentials Device Class")] - public abstract class EssentialsDevice : Device - { - protected EssentialsDevice(string key) - : base(key) - { - - } - - protected EssentialsDevice(string key, string name) - : base(key, name) - { - - } - } - - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] - public class DescriptionAttribute : Attribute - { - private string _Description; - - public DescriptionAttribute(string description) - { - Debug.Console(2, "Setting Description: {0}", description); - _Description = description; - } - - public string Description - { - get { return _Description; } - } - } - - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] - public class ConfigSnippetAttribute : Attribute - { - private string _ConfigSnippet; - - public ConfigSnippetAttribute(string configSnippet) - { - Debug.Console(2, "Setting Config Snippet {0}", configSnippet); - _ConfigSnippet = configSnippet; - } - - public string ConfigSnippet - { - get { return _ConfigSnippet; } - } - } - - /// - /// Devices the basic needs for a Device Factory - /// - public abstract class EssentialsDeviceFactory : IDeviceFactory where T:EssentialsDevice - { - #region IDeviceFactory Members - - /// - /// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device - /// - public List TypeNames { get; protected set; } - - /// - /// Loads an item to the DeviceFactory.FactoryMethods dictionary for each entry in the TypeNames list - /// - public void LoadTypeFactories() - { - foreach (var typeName in TypeNames) - { - Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); - var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[]; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Core +{ + /// + /// Defines the basic needs for an EssentialsDevice to enable it to be build by an IDeviceFactory class + /// + [Description("The base Essentials Device Class")] + public abstract class EssentialsDevice : Device + { + protected EssentialsDevice(string key) + : base(key) + { + SubscribeToActivateComplete(); + } + + protected EssentialsDevice(string key, string name) + : base(key, name) + { + SubscribeToActivateComplete(); + } + + private void SubscribeToActivateComplete() + { + DeviceManager.AllDevicesActivated += DeviceManagerOnAllDevicesActivated; + } + + private void DeviceManagerOnAllDevicesActivated(object sender, EventArgs eventArgs) + { + CrestronInvoke.BeginInvoke((o) => + { + try + { + Initialize(); + } + catch (Exception ex) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Exception initializing device: {0}", ex.Message); + Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stack Trace: {0}", ex.StackTrace); + } + }); + } + } + + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class DescriptionAttribute : Attribute + { + private string _Description; + + public DescriptionAttribute(string description) + { + Debug.Console(2, "Setting Description: {0}", description); + _Description = description; + } + + public string Description + { + get { return _Description; } + } + } + + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class ConfigSnippetAttribute : Attribute + { + private string _ConfigSnippet; + + public ConfigSnippetAttribute(string configSnippet) + { + Debug.Console(2, "Setting Config Snippet {0}", configSnippet); + _ConfigSnippet = configSnippet; + } + + public string ConfigSnippet + { + get { return _ConfigSnippet; } + } + } + + /// + /// Devices the basic needs for a Device Factory + /// + public abstract class EssentialsDeviceFactory : IDeviceFactory where T:EssentialsDevice + { + #region IDeviceFactory Members + + /// + /// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device + /// + public List TypeNames { get; protected set; } + + /// + /// Loads an item to the DeviceFactory.FactoryMethods dictionary for each entry in the TypeNames list + /// + public void LoadTypeFactories() + { + foreach (var typeName in TypeNames) + { + Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); + var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[]; string description = descriptionAttribute[0].Description; - var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; - DeviceFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildDevice); - } - } - - /// - /// The method that will build the device - /// - /// The device config - /// An instance of the device - public abstract EssentialsDevice BuildDevice(DeviceConfig dc); - - #endregion - } - - /// - /// Devices the basic needs for a Device Factory - /// - public abstract class EssentialsPluginDeviceFactory : EssentialsDeviceFactory, IPluginDeviceFactory where T : EssentialsDevice - { - /// - /// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33") - /// - public string MinimumEssentialsFrameworkVersion { get; protected set; } - } + var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; + DeviceFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildDevice); + } + } + + /// + /// The method that will build the device + /// + /// The device config + /// An instance of the device + public abstract EssentialsDevice BuildDevice(DeviceConfig dc); + + #endregion + } + + /// + /// Devices the basic needs for a Device Factory + /// + public abstract class EssentialsPluginDeviceFactory : EssentialsDeviceFactory, IPluginDeviceFactory where T : EssentialsDevice + { + /// + /// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33") + /// + public string MinimumEssentialsFrameworkVersion { get; protected set; } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IProjectorInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IProjectorInterfaces.cs index 6efb7776..70e2e215 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IProjectorInterfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IProjectorInterfaces.cs @@ -1,7 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; using Crestron.SimplSharp; namespace PepperDash.Essentials.Core diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IReconfigurableDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IReconfigurableDevice.cs new file mode 100644 index 00000000..ab0e37c3 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IReconfigurableDevice.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Essentials.Core.Config; + + +namespace PepperDash.Essentials.Core.Devices +{ + public interface IReconfigurableDevice + { + event EventHandler ConfigChanged; + + DeviceConfig Config { get; } + + void SetConfig(DeviceConfig config); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/ReconfigurableDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/ReconfigurableDevice.cs index 81988199..3239e192 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/ReconfigurableDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/ReconfigurableDevice.cs @@ -15,7 +15,7 @@ namespace PepperDash.Essentials.Core.Devices /// /// /// - public abstract class ReconfigurableDevice : EssentialsDevice + public abstract class ReconfigurableDevice : EssentialsDevice, IReconfigurableDevice { public event EventHandler ConfigChanged; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs index dcdba6d8..eb529376 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs @@ -59,6 +59,9 @@ namespace PepperDash.Essentials.Core VolumeLevelFeedback = new IntFeedback(() => { return _FakeVolumeLevel; }); MuteFeedback = new BoolFeedback("MuteOn", () => _IsMuted); + + WarmupTime = 10000; + CooldownTime = 5000; } public override void PowerOn() diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs index f46b4767..055ed5b6 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs @@ -62,6 +62,11 @@ namespace PepperDash.Essentials.Core ValueFunc = valueFunc; } + public void SetValueFunc(Func newFunc) + { + ValueFunc = newFunc; + } + public override void FireUpdate() { bool newValue = InTestMode ? TestValue : ValueFunc.Invoke(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolOutputLogicals.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolOutputLogicals.cs index 3074254e..a8dae7b8 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolOutputLogicals.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolOutputLogicals.cs @@ -23,7 +23,7 @@ namespace PepperDash.Essentials.Core protected bool ComputedValue; - public BoolFeedbackLogic() + protected BoolFeedbackLogic() { Output = new BoolFeedback(() => ComputedValue); } @@ -40,21 +40,18 @@ namespace PepperDash.Essentials.Core public void AddOutputsIn(List outputs) { - foreach (var o in outputs) - { - // skip existing - if (OutputsIn.Contains(o)) continue; - - OutputsIn.Add(o); - o.OutputChange += AnyInput_OutputChange; - } - Evaluate(); + foreach (var o in outputs.Where(o => !OutputsIn.Contains(o))) + { + OutputsIn.Add(o); + o.OutputChange += AnyInput_OutputChange; + } + Evaluate(); } - public void RemoveOutputIn(BoolFeedback output) + public void RemoveOutputIn(BoolFeedback output) { // Don't double up outputs - if (OutputsIn.Contains(output)) return; + if (!OutputsIn.Contains(output)) return; OutputsIn.Remove(output); output.OutputChange -= AnyInput_OutputChange; @@ -71,6 +68,12 @@ namespace PepperDash.Essentials.Core Evaluate(); } + public void ClearOutputs() + { + OutputsIn.Clear(); + Evaluate(); + } + void AnyInput_OutputChange(object sender, EventArgs e) { Evaluate(); @@ -85,11 +88,12 @@ namespace PepperDash.Essentials.Core { var prevValue = ComputedValue; var newValue = OutputsIn.All(o => o.BoolValue); - if (newValue != prevValue) - { - ComputedValue = newValue; - Output.FireUpdate(); - } + if (newValue == prevValue) + { + return; + } + ComputedValue = newValue; + Output.FireUpdate(); } } @@ -99,33 +103,35 @@ namespace PepperDash.Essentials.Core { var prevValue = ComputedValue; var newValue = OutputsIn.Any(o => o.BoolValue); - if (newValue != prevValue) - { - ComputedValue = newValue; - Output.FireUpdate(); - } + if (newValue == prevValue) + { + return; + } + ComputedValue = newValue; + Output.FireUpdate(); } } public class BoolFeedbackLinq : BoolFeedbackLogic { - Func, bool> Predicate; + readonly Func, bool> _predicate; public BoolFeedbackLinq(Func, bool> predicate) : base() { - Predicate = predicate; + _predicate = predicate; } protected override void Evaluate() { var prevValue = ComputedValue; - var newValue = Predicate(OutputsIn); - if (newValue != prevValue) - { - ComputedValue = newValue; - Output.FireUpdate(); - } + var newValue = _predicate(OutputsIn); + if (newValue == prevValue) + { + return; + } + ComputedValue = newValue; + Output.FireUpdate(); } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs index 25390c2c..53bae09a 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs @@ -51,6 +51,12 @@ namespace PepperDash.Essentials.Core ValueFunc = valueFunc; } + public void SetValueFunc(Func newFunc) + { + ValueFunc = newFunc; + } + + public override void FireUpdate() { var newValue = InTestMode ? TestValue : ValueFunc.Invoke(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs index 56251a2e..fb5cccb5 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs @@ -52,7 +52,10 @@ namespace PepperDash.Essentials.Core ValueFunc = valueFunc; } - + public void SetValueFunc(Func newFunc) + { + ValueFunc = newFunc; + } public override void FireUpdate() { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index b26b1980..1bf925d6 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -33,7 +33,7 @@ namespace PepperDash.Essentials.Core.Fusion protected FusionRoom FusionRoom; protected Dictionary FusionStaticAssets; public long PushNotificationTimeout = 5000; - protected EssentialsRoomBase Room; + protected IEssentialsRoom Room; public long SchedulePollInterval = 300000; private Event _currentMeeting; @@ -86,7 +86,7 @@ namespace PepperDash.Essentials.Core.Fusion #endregion - public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId, string joinMapKey) + public EssentialsHuddleSpaceFusionSystemControllerBase(IEssentialsRoom room, uint ipId, string joinMapKey) : base(room.Key + "-fusion") { try @@ -119,9 +119,21 @@ namespace PepperDash.Essentials.Core.Fusion var slot = Global.ControlSystem.ProgramNumber; var guidFilePath = Global.FilePathPrefix + - string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); + string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _ipId); - _guidFileExists = File.Exists(guidFilePath); + var oldGuidFilePath = Global.FilePathPrefix + + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); + + if (File.Exists(oldGuidFilePath)) + { + Debug.Console(0, this, "Migrating from old Fusion GUID file to new Fusion GUID File"); + + File.Copy(oldGuidFilePath, guidFilePath); + + File.Delete(oldGuidFilePath); + } + + _guidFileExists = File.Exists(guidFilePath); // Check if file exists if (!_guidFileExists) @@ -149,19 +161,7 @@ namespace PepperDash.Essentials.Core.Fusion } - AddPostActivationAction(() => - { - CreateSymbolAndBasicSigs(_ipId); - SetUpSources(); - SetUpCommunitcationMonitors(); - SetUpDisplay(); - SetUpError(); - ExecuteCustomSteps(); - - FusionRVI.GenerateFileForAllFusionDevices(); - - GenerateGuidFile(guidFilePath); - }); + AddPostActivationAction(() => PostActivate(guidFilePath)); } catch (Exception e) { @@ -169,6 +169,20 @@ namespace PepperDash.Essentials.Core.Fusion } } + private void PostActivate(string guidFilePath) + { + CreateSymbolAndBasicSigs(_ipId); + SetUpSources(); + SetUpCommunitcationMonitors(); + SetUpDisplay(); + SetUpError(); + ExecuteCustomSteps(); + + FusionRVI.GenerateFileForAllFusionDevices(); + + GenerateGuidFile(guidFilePath); + } + protected string RoomGuid { get { return _guiDs.RoomGuid; } @@ -314,7 +328,7 @@ namespace PepperDash.Essentials.Core.Fusion protected virtual void CreateSymbolAndBasicSigs(uint ipId) { - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId); FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionCustomPropertiesBridge.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionCustomPropertiesBridge.cs index 01b230f9..89b9fcdb 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionCustomPropertiesBridge.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionCustomPropertiesBridge.cs @@ -82,7 +82,7 @@ namespace PepperDash.Essentials.Core.Fusion deviceConfig.Properties = JToken.FromObject(devProps); } - else if (device is EssentialsRoomBase) + else if (device is IEssentialsRoom) { // Set the room name if (!string.IsNullOrEmpty(roomInfo.Name)) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyController.cs index 0f15a02d..55b458a7 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Microphone Privacy/MicrophonePrivacyController.cs @@ -88,11 +88,6 @@ namespace PepperDash.Essentials.Core.Privacy else Debug.Console(0, this, "Unable to add Red LED device"); - DeviceManager.AllDevicesActivated += (o, a) => - { - CheckPrivacyMode(); - }; - AddPostActivationAction(() => { PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange -= PrivacyModeIsOnFeedback_OutputChange; PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange; @@ -103,6 +98,15 @@ namespace PepperDash.Essentials.Core.Privacy return base.CustomActivate(); } + #region Overrides of Device + + public override void Initialize() + { + CheckPrivacyMode(); + } + + #endregion + public void SetPrivacyDevice(IPrivacy privacyDevice) { PrivacyDevice = privacyDevice; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/IOccupancyStatusProviderAggregator.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/IOccupancyStatusProviderAggregator.cs index c321dcad..3da9c0dc 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/IOccupancyStatusProviderAggregator.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/IOccupancyStatusProviderAggregator.cs @@ -2,17 +2,18 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Crestron.SimplSharp; - +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.GeneralIO; using PepperDash.Core; using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.Core { /// /// Aggregates the RoomIsOccupied feedbacks of one or more IOccupancyStatusProvider objects /// - public class IOccupancyStatusProviderAggregator : Device, IOccupancyStatusProvider + public class IOccupancyStatusProviderAggregator : EssentialsDevice, IOccupancyStatusProvider { /// /// Aggregated feedback of all linked IOccupancyStatusProvider devices @@ -21,16 +22,51 @@ namespace PepperDash.Essentials.Core { get { - return AggregatedOccupancyStatus.Output; + return _aggregatedOccupancyStatus.Output; } } - private BoolFeedbackOr AggregatedOccupancyStatus; + private readonly BoolFeedbackOr _aggregatedOccupancyStatus; public IOccupancyStatusProviderAggregator(string key, string name) : base(key, name) { - AggregatedOccupancyStatus = new BoolFeedbackOr(); + _aggregatedOccupancyStatus = new BoolFeedbackOr(); + } + + public IOccupancyStatusProviderAggregator(string key, string name, OccupancyAggregatorConfig config) + : this(key, name) + { + AddPostActivationAction(() => + { + if (config.DeviceKeys.Count == 0) + { + return; + } + + foreach (var deviceKey in config.DeviceKeys) + { + var device = DeviceManager.GetDeviceForKey(deviceKey); + + if (device == null) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, + "Unable to retrieve Occupancy provider with key {0}", deviceKey); + continue; + } + + var provider = device as IOccupancyStatusProvider; + + if (provider == null) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, + "Device with key {0} does NOT implement IOccupancyStatusProvider. Please check configuration."); + continue; + } + + AddOccupancyStatusProvider(provider); + } + }); } /// @@ -39,7 +75,35 @@ namespace PepperDash.Essentials.Core /// public void AddOccupancyStatusProvider(IOccupancyStatusProvider statusProvider) { - AggregatedOccupancyStatus.AddOutputIn(statusProvider.RoomIsOccupiedFeedback); + _aggregatedOccupancyStatus.AddOutputIn(statusProvider.RoomIsOccupiedFeedback); + } + + public void RemoveOccupancyStatusProvider(IOccupancyStatusProvider statusProvider) + { + _aggregatedOccupancyStatus.RemoveOutputIn(statusProvider.RoomIsOccupiedFeedback); + } + + public void ClearOccupancyStatusProviders() + { + _aggregatedOccupancyStatus.ClearOutputs(); } + } + + public class OccupancyAggregatorFactory : EssentialsDeviceFactory + { + public OccupancyAggregatorFactory() + { + TypeNames = new List { "occupancyAggregator", "occAggregate" }; + } + + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new GlsOccupancySensorBaseController Device"); + + var config = dc.Properties.ToObject(); + + return new IOccupancyStatusProviderAggregator(dc.Key, dc.Name, config); + } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/OccupancyAggregatorConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/OccupancyAggregatorConfig.cs new file mode 100644 index 00000000..ef237e72 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Occupancy/OccupancyAggregatorConfig.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core +{ + public class OccupancyAggregatorConfig + { + [JsonProperty("deviceKeys")] public List DeviceKeys { get; set; } + + public OccupancyAggregatorConfig() + { + DeviceKeys = new List(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs new file mode 100644 index 00000000..7066be0e --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/EssentialsPartitionController.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Represents an abstract controller device for a partition dividing rooms that are combinable + /// + /// In Auto mode, it can use a partition sensor to automatically determine whether the partition is present. + /// + /// In Manual mode it accepts user input to tell it whether the partition is present. + /// + public class EssentialsPartitionController : IPartitionController + { + private IPartitionStateProvider _partitionSensor; + + private bool isInAutoMode; + + private bool partitionPresent; + + public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List adjacentRoomKeys) + { + Key = key; + + Name = name; + + AdjacentRoomKeys = adjacentRoomKeys; + + if (sensor != null) + { + _partitionSensor = sensor; + + if (!defaultToManualMode) + { + SetAutoMode(); + } + else + { + SetManualMode(); + } + } + else + { + SetManualMode(); + } + + PartitionPresentFeedback.FireUpdate(); + } + + void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + if (isInAutoMode) + { + PartitionPresentFeedback.FireUpdate(); + } + } + + #region IPartitionController Members + + public List AdjacentRoomKeys { get; private set; } + + public void SetAutoMode() + { + isInAutoMode = true; + if (PartitionPresentFeedback != null) + { + PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue); + } + else + { + PartitionPresentFeedback = new BoolFeedback(() => _partitionSensor.PartitionPresentFeedback.BoolValue); + } + + if (_partitionSensor != null) + { + _partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange; + } + } + + public void SetManualMode() + { + isInAutoMode = false; + if (PartitionPresentFeedback != null) + { + PartitionPresentFeedback.SetValueFunc(() => partitionPresent); + } + else + { + PartitionPresentFeedback = new BoolFeedback(() => partitionPresent); + } + + if (_partitionSensor != null) + { + _partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange; + } + } + + + public void SetPartitionStatePresent() + { + if (!isInAutoMode) + { + partitionPresent = true; + PartitionPresentFeedback.FireUpdate(); + } + } + + public void SetPartitionStateNotPresent() + { + if (!isInAutoMode) + { + partitionPresent = false; + PartitionPresentFeedback.FireUpdate(); + } + } + + public void ToggglePartitionState() + { + if (!isInAutoMode) + { + partitionPresent = !partitionPresent; + PartitionPresentFeedback.FireUpdate(); + } + } + + #endregion + + #region IPartitionStateProvider Members + + public BoolFeedback PartitionPresentFeedback { get; private set; } + + #endregion + + #region IKeyName Members + + public string Name { get; private set; } + + #endregion + + #region IKeyed Members + + public string Key { get; private set; } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs index b0b58f34..26cade95 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/GlsPartitionSensorController.cs @@ -13,13 +13,13 @@ using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.Core { [Description("Wrapper class for GLS Cresnet Partition Sensor")] - public class GlsPartitionSensorController : CrestronGenericBridgeableBaseDevice + public class GlsPartitionSensorController : CrestronGenericBridgeableBaseDevice, IPartitionStateProvider { private GlsPartCn _partitionSensor; public StringFeedback NameFeedback { get; private set; } public BoolFeedback EnableFeedback { get; private set; } - public BoolFeedback PartitionSensedFeedback { get; private set; } + public BoolFeedback PartitionPresentFeedback { get; private set; } public BoolFeedback PartitionNotSensedFeedback { get; private set; } public IntFeedback SensitivityFeedback { get; private set; } @@ -39,10 +39,10 @@ namespace PepperDash.Essentials.Core RegisterCrestronGenericBase(_partitionSensor); NameFeedback = new StringFeedback(() => Name); - EnableFeedback = new BoolFeedback(() => _partitionSensor.EnableFeedback.BoolValue); - PartitionSensedFeedback = new BoolFeedback(() => _partitionSensor.PartitionSensedFeedback.BoolValue); - PartitionNotSensedFeedback = new BoolFeedback(() => _partitionSensor.PartitionNotSensedFeedback.BoolValue); - SensitivityFeedback = new IntFeedback(() => _partitionSensor.SensitivityFeedback.UShortValue); + EnableFeedback = new BoolFeedback(() => InTestMode ? TestEnableFeedback : _partitionSensor.EnableFeedback.BoolValue); + PartitionPresentFeedback = new BoolFeedback(() => InTestMode ? TestPartitionSensedFeedback : _partitionSensor.PartitionSensedFeedback.BoolValue); + PartitionNotSensedFeedback = new BoolFeedback(() => InTestMode ? !TestPartitionSensedFeedback : _partitionSensor.PartitionNotSensedFeedback.BoolValue); + SensitivityFeedback = new IntFeedback(() => InTestMode ? TestSensitivityFeedback : _partitionSensor.SensitivityFeedback.UShortValue); if (_partitionSensor != null) _partitionSensor.BaseEvent += PartitionSensor_BaseEvent; }); @@ -61,7 +61,7 @@ namespace PepperDash.Essentials.Core } case (GlsPartCn.PartitionSensedFeedbackEventId): { - PartitionSensedFeedback.FireUpdate(); + PartitionPresentFeedback.FireUpdate(); break; } case (GlsPartCn.PartitionNotSensedFeedbackEventId): @@ -93,6 +93,9 @@ namespace PepperDash.Essentials.Core if (InTestMode) { TestEnableFeedback = state; + + EnableFeedback.FireUpdate(); + Debug.Console(1, this, "TestEnableFeedback: {0}", TestEnableFeedback.ToString()); return; } @@ -105,6 +108,10 @@ namespace PepperDash.Essentials.Core if (InTestMode) { TestPartitionSensedFeedback = state; + + PartitionPresentFeedback.FireUpdate(); + PartitionNotSensedFeedback.FireUpdate(); + Debug.Console(1, this, "TestPartitionSensedFeedback: {0}", TestPartitionSensedFeedback.ToString()); return; } @@ -117,6 +124,8 @@ namespace PepperDash.Essentials.Core if (InTestMode) { TestSensitivityFeedback = value; + + SensitivityFeedback.FireUpdate(); Debug.Console(1, this, "TestSensitivityFeedback: {0}", TestSensitivityFeedback); return; } @@ -186,7 +195,7 @@ namespace PepperDash.Essentials.Core // link output to simpl IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); EnableFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Enable.JoinNumber]); - PartitionSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionSensed.JoinNumber]); + PartitionPresentFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionSensed.JoinNumber]); PartitionNotSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionNotSensed.JoinNumber]); SensitivityFeedback.LinkInputSig(trilist.UShortInput[joinMap.Sensitivity.JoinNumber]); @@ -216,7 +225,7 @@ namespace PepperDash.Essentials.Core IsOnline.FireUpdate(); NameFeedback.FireUpdate(); EnableFeedback.FireUpdate(); - PartitionSensedFeedback.FireUpdate(); + PartitionPresentFeedback.FireUpdate(); PartitionNotSensedFeedback.FireUpdate(); SensitivityFeedback.FireUpdate(); } @@ -257,7 +266,7 @@ namespace PepperDash.Essentials.Core public override EssentialsDevice BuildDevice(DeviceConfig dc) { - Debug.Console(1, "Factory Attempting to create new C2N-RTHS Device"); + Debug.Console(1, "Factory Attempting to create new GlsPartitionSensorController Device"); return new GlsPartitionSensorController(dc.Key, GetGlsPartCnDevice, dc); } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs new file mode 100644 index 00000000..adb420b7 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PartitionSensor/IPartitionStateProvider.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Describes the functionality of a device that senses and provides partition state + /// + public interface IPartitionStateProvider : IKeyName + { + BoolFeedback PartitionPresentFeedback { get; } + } + + /// + /// Describes the functionality of a device that can provide partition state either manually via user input or optionally via a sensor state + /// + public interface IPartitionController : IPartitionStateProvider + { + List AdjacentRoomKeys { get; } + + void SetPartitionStatePresent(); + + void SetPartitionStateNotPresent(); + + void ToggglePartitionState(); + + void SetManualMode(); + + void SetAutoMode(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index e65b081b..bde91605 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -162,6 +162,7 @@ + @@ -197,6 +198,7 @@ + @@ -232,6 +234,9 @@ + + + @@ -286,8 +291,13 @@ + + + + + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs index f24daf82..81cbff9e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs @@ -38,7 +38,7 @@ namespace PepperDash.Essentials.Core ScheduledEventGroup FeatureEventGroup; - public EssentialsRoomBase Room { get; private set; } + public IEssentialsRoom Room { get; private set; } private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom; @@ -84,7 +84,7 @@ namespace PepperDash.Essentials.Core /// void SetUpDevice() { - Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as EssentialsRoomBase; + Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as IEssentialsRoom; if (Room != null) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs new file mode 100644 index 00000000..6d80913f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombiner.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public class EssentialsRoomCombiner : EssentialsDevice, IEssentialsRoomCombiner + { + private EssentialsRoomCombinerPropertiesConfig _propertiesConfig; + + private IRoomCombinationScenario _currentScenario; + + private List _rooms; + + private bool isInAutoMode; + + private CTimer _scenarioChangeDebounceTimer; + + private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s + + public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props) + : base(key) + { + _propertiesConfig = props; + + Partitions = new List(); + RoomCombinationScenarios = new List(); + + if (_propertiesConfig.ScenarioChangeDebounceTimeSeconds > 0) + { + _scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds; + } + + IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode); + + // default to auto mode + isInAutoMode = true; + + if (_propertiesConfig.defaultToManualMode) + { + isInAutoMode = false; + } + + IsInAutoModeFeedback.FireUpdate(); + + CreateScenarios(); + + AddPostActivationAction(() => + { + SetupPartitionStateProviders(); + + SetRooms(); + }); + } + + void CreateScenarios() + { + RoomCombinationScenarios = new List(); + + foreach (var scenarioConfig in _propertiesConfig.Scenarios) + { + var scenario = new RoomCombinationScenario(scenarioConfig); + RoomCombinationScenarios.Add(scenario); + } + } + + void SetRooms() + { + _rooms = new List(); + + foreach (var roomKey in _propertiesConfig.RoomKeys) + { + var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom; + if (room != null) + { + _rooms.Add(room); + } + } + } + + void SetupPartitionStateProviders() + { + foreach (var pConfig in _propertiesConfig.Partitions) + { + var sensor = DeviceManager.GetDeviceForKey(pConfig.DeviceKey) as IPartitionStateProvider; + + var partition = new EssentialsPartitionController(pConfig.Key, pConfig.Name, sensor, _propertiesConfig.defaultToManualMode, pConfig.AdjacentRoomKeys); + + partition.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange; + + Partitions.Add(partition); + } + } + + void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + StartDebounceTimer(); + } + + void StartDebounceTimer() + { + var time = _scenarioChangeDebounceTimeSeconds * 1000; + + if (_scenarioChangeDebounceTimer == null) + { + _scenarioChangeDebounceTimer = new CTimer((o) => DetermineRoomCombinationScenario(), time); + } + else + { + _scenarioChangeDebounceTimer.Reset(time); + } + } + + /// + /// Determines the current room combination scenario based on the state of the partition sensors + /// + void DetermineRoomCombinationScenario() + { + if (_scenarioChangeDebounceTimer != null) + { + _scenarioChangeDebounceTimer.Dispose(); + _scenarioChangeDebounceTimer = null; + } + + var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) => + { + // iterate the partition states + foreach (var partitionState in s.PartitionStates) + { + // get the partition by key + var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey)); + + if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue) + { + // the partition can't be found or the state doesn't match + return false; + } + } + // if it hasn't returned false by now we have the matching scenario + return true; + }); + + if (currentScenario != null) + { + CurrentScenario = currentScenario; + } + } + + #region IEssentialsRoomCombiner Members + + public event EventHandler RoomCombinationScenarioChanged; + + public IRoomCombinationScenario CurrentScenario + { + get + { + return _currentScenario; + } + set + { + if (value != _currentScenario) + { + _currentScenario = value; + Debug.Console(1, this, "Current Scenario: {0}", _currentScenario.Name); + var handler = RoomCombinationScenarioChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + } + } + + public BoolFeedback IsInAutoModeFeedback { get; private set; } + + public void SetAutoMode() + { + isInAutoMode = true; + IsInAutoModeFeedback.FireUpdate(); + } + + public void SetManualMode() + { + isInAutoMode = false; + IsInAutoModeFeedback.FireUpdate(); + } + + public void ToggleMode() + { + isInAutoMode = !isInAutoMode; + IsInAutoModeFeedback.FireUpdate(); + } + + public List RoomCombinationScenarios { get; private set; } + + public List Partitions { get; private set; } + + public void TogglePartitionState(string partitionKey) + { + var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController; + + if (partition != null) + { + partition.ToggglePartitionState(); + } + } + + public void SetRoomCombinationScenario(string scenarioKey) + { + if (isInAutoMode) + { + Debug.Console(0, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first."); + return; + } + + // Get the scenario + var scenario = RoomCombinationScenarios.FirstOrDefault((s) => s.Key.Equals(scenarioKey)); + + // Set the parition states from the scenario manually + if (scenario != null) + { + foreach (var partitionState in scenario.PartitionStates) + { + var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey)); + + if (partition != null) + { + if (partitionState.PartitionPresent) + { + partition.SetPartitionStatePresent(); + } + else + { + partition.SetPartitionStateNotPresent(); + } + } + } + } + } + + #endregion + } + + public class EssentialsRoomCombinerFactory : EssentialsDeviceFactory + { + public EssentialsRoomCombinerFactory() + { + TypeNames = new List { "essentialsroomcombiner" }; + } + + public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new EssentialsRoomCombiner Device"); + + var props = dc.Properties.ToObject(); + + return new EssentialsRoomCombiner(dc.Key, props); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs new file mode 100644 index 00000000..05295f42 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/EssentialsRoomCombinerPropertiesConfig.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core +{ + /// + /// Config properties for an EssentialsRoomCombiner device + /// + public class EssentialsRoomCombinerPropertiesConfig + { + /// + /// The list of partitions that device the rooms + /// + [JsonProperty("partitions")] + public List Partitions {get; set;} + + /// + /// The list of combinations scenarios for the rooms + /// + [JsonProperty("scenarios")] + public List Scenarios { get; set; } + + /// + /// The list of rooms keys that can be combined + /// + [JsonProperty("roomMap")] + public List RoomKeys {get; set;} + + /// + /// Set to true to default to manual mode + /// + [JsonProperty("defaultToManualMode")] + public bool defaultToManualMode { get; set; } + + /// + /// The key of the scenario to default to at system startup if in manual mode + /// + [JsonProperty("defaultScenarioKey")] + public string defaultScenarioKey { get; set; } + + [JsonProperty("scenarioChangeDebounceTimeSeconds")] + public int ScenarioChangeDebounceTimeSeconds { get; set; } + } + + /// + /// Config properties for a partition that separates rooms + /// + public class PartitionConfig : IKeyName + { + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Key of the device that implements IPartitionStateProvider to provide the state of the partition + /// + [JsonProperty("deviceKey")] + public string DeviceKey { get; set; } + + /// + /// Keys of the rooms that this partion would be located between + /// + [JsonProperty("adjacentRoomKeys")] + public List AdjacentRoomKeys { get; set; } + } + + /// + /// Config propeties for a room combination scenario + /// + public class RoomCombinationScenarioConfig : IKeyName + { + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("partitionStates")] + public List PartitionStates { get; set; } + + [JsonProperty("uiMap")] + public Dictionary UiMap { get; set; } + + [JsonProperty("activationActions")] + public List ActivationActions { get; set; } + + [JsonProperty("deactivationActions")] + public List DeactivationActions { get; set; } + } + + /// + /// Config properties to represent the state of a partition sensor in a RoomCombinationScenario + /// + public class PartitionState + { + [JsonProperty("partitionKey")] + public string PartitionKey { get; set; } + + [JsonProperty("partitionSensedState")] + public bool PartitionPresent { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs new file mode 100644 index 00000000..c0c8101b --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/IEssentialsRoomCombiner.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Describes the functionality for an EssentailsRoomCombiner device + /// + public interface IEssentialsRoomCombiner : IKeyed + { + /// + /// Indicates that the room combination scenario has changed + /// + event EventHandler RoomCombinationScenarioChanged; + + /// + /// The current room combination scenario + /// + IRoomCombinationScenario CurrentScenario { get; } + + /// + /// When true, indicates the current mode is auto mode + /// + BoolFeedback IsInAutoModeFeedback {get;} + + /// + /// Sets auto mode + /// + void SetAutoMode(); + + /// + /// Sets manual mode + /// + void SetManualMode(); + + /// + /// Toggles the current mode between auto and manual + /// + void ToggleMode(); + + /// + /// The available room combinatino scenarios + /// + List RoomCombinationScenarios { get; } + + /// + /// The partition + /// + List Partitions { get; } + + /// + /// Toggles the state of a manual partition sensor + /// + /// + void TogglePartitionState(string partitionKey); + + /// + /// Sets the room combination scenario (if in manual mode) + /// + /// + void SetRoomCombinationScenario(string scenarioKey); + } + + public interface IRoomCombinationScenario : IKeyName + { + /// + /// When true, indicates that this room combination scenario is active + /// + BoolFeedback IsActiveFeedback { get; } + + /// + /// Activates this room combination scenario + /// + void Activate(); + + /// + /// The state of the partitions that would activate this scenario + /// + List PartitionStates { get; } + + /// + /// The mapping of UIs by key to rooms by key + /// + Dictionary UiMap { get; set; } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs new file mode 100644 index 00000000..a5534edc --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Combining/RoomCombinationScenario.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core +{ + /// + /// Represents a room combination scenario + /// + public class RoomCombinationScenario: IRoomCombinationScenario + { + private RoomCombinationScenarioConfig _config; + + public string Key { get; set; } + + public string Name { get; set; } + + public List PartitionStates { get; private set; } + + public Dictionary UiMap { get; set; } + + private bool _isActive; + + public BoolFeedback IsActiveFeedback { get; private set; } + + List activationActions; + + List deactivationActions; + + public RoomCombinationScenario(RoomCombinationScenarioConfig config) + { + Key = config.Key; + + Name = config.Name; + + PartitionStates = config.PartitionStates; + + UiMap = config.UiMap; + + activationActions = config.ActivationActions; + + deactivationActions = config.DeactivationActions; + + _config = config; + + IsActiveFeedback = new BoolFeedback(() => _isActive); + } + + public void Activate() + { + if (activationActions != null) + { + foreach (var action in activationActions) + { + DeviceJsonApi.DoDeviceAction(action); + } + } + + _isActive = true; + IsActiveFeedback.FireUpdate(); + } + + public void Deactivate() + { + if (deactivationActions != null) + { + foreach (var action in deactivationActions) + { + DeviceJsonApi.DoDeviceAction(action); + } + } + + _isActive = false; + IsActiveFeedback.FireUpdate(); + } + + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs index cae027f5..0d8e9803 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs @@ -16,7 +16,7 @@ namespace PepperDash.Essentials.Core /// /// /// - public abstract class EssentialsRoomBase : ReconfigurableDevice + public abstract class EssentialsRoomBase : ReconfigurableDevice, IEssentialsRoom { /// /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs new file mode 100644 index 00000000..e83c4b2f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/IEssentialsRoom.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Room.Config; +using PepperDash.Essentials.Core.Devices; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Describes the basic functionality of an EssentialsRoom + /// + public interface IEssentialsRoom : IKeyName, IReconfigurableDevice + { + BoolFeedback OnFeedback { get; } + + event EventHandler RoomOccupancyIsSet; + + BoolFeedback IsWarmingUpFeedback { get; } + BoolFeedback IsCoolingDownFeedback { get; } + + IOccupancyStatusProvider RoomOccupancy { get; } + bool OccupancyStatusProviderIsRemote { get; } + + bool IsMobileControlEnabled { get; } + IMobileControlRoomBridge MobileControlRoomBridge { get; } + + string SourceListKey { get; } + + SecondsCountdownTimer ShutdownPromptTimer { get; } + int ShutdownPromptSeconds { get; } + int ShutdownVacancySeconds { get; } + eShutdownType ShutdownType { get; } + + EssentialsRoomEmergencyBase Emergency { get; } + + Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; } + + string LogoUrlLightBkgnd { get; } + string LogoUrlDarkBkgnd { get; } + + eVacancyMode VacancyMode { get; } + + bool ZeroVolumeWhenSwtichingVolumeDevices { get; } + + void StartShutdown(eShutdownType type); + void StartRoomVacancyTimer(eVacancyMode mode); + + void Shutdown(); + + void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes); + + void PowerOnToDefaultOrLastSource(); + bool RunDefaultPresentRoute(); + + void SetDefaultLevels(); + + void RoomVacatedForTimeoutPeriod(object o); + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs index 82871228..592b0bd9 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using Crestron.SimplSharp; + namespace PepperDash.Essentials.Core { /// @@ -64,5 +65,7 @@ namespace PepperDash.Essentials.Core { bool RunDefaultCallRoute(); } - + + + } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs index 7d6acdc2..24f1968b 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs @@ -4,15 +4,20 @@ using System.Linq; using System.Text; using Crestron.SimplSharp; +using PepperDash.Core; + +using Newtonsoft.Json; + namespace PepperDash.Essentials.Devices.Common.Codec { + [Flags] public enum eMeetingEventChangeType { - Unkown = 0, - MeetingStartWarning, - MeetingStart, - MeetingEndWarning, - MeetingEnd + Unknown = 0, + MeetingStartWarning = 1, + MeetingStart = 2, + MeetingEndWarning = 4, + MeetingEnd = 8 } public interface IHasScheduleAwareness @@ -32,6 +37,10 @@ namespace PepperDash.Essentials.Devices.Common.Codec private int _meetingWarningMinutes = 5; + private Meeting _previousChangedMeeting; + + private eMeetingEventChangeType _previousChangeType = eMeetingEventChangeType.Unknown; + public int MeetingWarningMinutes { get { return _meetingWarningMinutes; } @@ -75,38 +84,72 @@ namespace PepperDash.Essentials.Devices.Common.Codec _scheduleChecker = new CTimer(CheckSchedule, null, pollTime, pollTime); } + /// + /// Helper method to fire MeetingEventChange. Should only fire once for each changeType on each meeting + /// + /// + /// private void OnMeetingChange(eMeetingEventChangeType changeType, Meeting meeting) { - var handler = MeetingEventChange; - if (handler != null) + Debug.Console(2, "*****************OnMeetingChange. id: {0} changeType: {1}**********************", meeting.Id, changeType); + if (changeType != (changeType & meeting.NotifiedChangeTypes)) { - handler(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting }); + // Add this change type to the NotifiedChangeTypes + meeting.NotifiedChangeTypes |= changeType; + + var handler = MeetingEventChange; + if (handler != null) + { + handler(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting }); + } + } + else + { + Debug.Console(2, "Meeting: {0} already notified of changeType: {1}", meeting.Id, changeType); } } + + /// + /// Checks the schedule to see if any MeetingEventChange updates should be fired + /// + /// private void CheckSchedule(object o) { - // Iterate the meeting list and check if any meeting need to do anythingk + // Iterate the meeting list and check if any meeting need to do anything - const double meetingTimeEpsilon = 0.0001; + const double meetingTimeEpsilon = 0.05; foreach (var m in Meetings) { - var changeType = eMeetingEventChangeType.Unkown; + var changeType = eMeetingEventChangeType.Unknown; - if (m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes) // Meeting is about to start + if (eMeetingEventChangeType.MeetingStartWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStartWarning) && m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingStart.Seconds > 0) // Meeting is about to start + { + Debug.Console(2, "********************* MeetingStartWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingStart.TotalMinutes, m.TimeToMeetingStart.Seconds); changeType = eMeetingEventChangeType.MeetingStartWarning; - else if (Math.Abs(m.TimeToMeetingStart.TotalMinutes) < meetingTimeEpsilon) // Meeting Start + } + else if (eMeetingEventChangeType.MeetingStart != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStart) && Math.Abs(m.TimeToMeetingStart.TotalMinutes) < meetingTimeEpsilon) // Meeting Start + { + Debug.Console(2, "********************* MeetingStart"); changeType = eMeetingEventChangeType.MeetingStart; - else if (m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes) // Meeting is about to end + } + else if (eMeetingEventChangeType.MeetingEndWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEndWarning) && m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingEnd.Seconds > 0) // Meeting is about to end + { + Debug.Console(2, "********************* MeetingEndWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingEnd.TotalMinutes, m.TimeToMeetingEnd.Seconds); changeType = eMeetingEventChangeType.MeetingEndWarning; - else if (Math.Abs(m.TimeToMeetingEnd.TotalMinutes) < meetingTimeEpsilon) // Meeting has ended + } + else if (eMeetingEventChangeType.MeetingEnd != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEnd) && Math.Abs(m.TimeToMeetingEnd.TotalMinutes) < meetingTimeEpsilon) // Meeting has ended + { + Debug.Console(2, "********************* MeetingEnd"); changeType = eMeetingEventChangeType.MeetingEnd; + } - if (changeType != eMeetingEventChangeType.Unkown) - OnMeetingChange(changeType, m); + if (changeType != eMeetingEventChangeType.Unknown) + { + OnMeetingChange(changeType, m); + } } - } } @@ -115,17 +158,24 @@ namespace PepperDash.Essentials.Devices.Common.Codec /// public class Meeting { + [JsonProperty("minutesBeforeMeeting")] public int MinutesBeforeMeeting; + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("organizer")] public string Organizer { get; set; } + [JsonProperty("title")] public string Title { get; set; } + [JsonProperty("agenda")] public string Agenda { get; set; } + [JsonProperty("meetingWarningMinutes")] public TimeSpan MeetingWarningMinutes { get { return TimeSpan.FromMinutes(MinutesBeforeMeeting); } } + [JsonProperty("timeToMeetingStart")] public TimeSpan TimeToMeetingStart { get @@ -133,6 +183,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec return StartTime - DateTime.Now; } } + [JsonProperty("timeToMeetingEnd")] public TimeSpan TimeToMeetingEnd { get @@ -140,8 +191,11 @@ namespace PepperDash.Essentials.Devices.Common.Codec return EndTime - DateTime.Now; } } + [JsonProperty("startTime")] public DateTime StartTime { get; set; } + [JsonProperty("endTime")] public DateTime EndTime { get; set; } + [JsonProperty("duration")] public TimeSpan Duration { get @@ -149,21 +203,34 @@ namespace PepperDash.Essentials.Devices.Common.Codec return EndTime - StartTime; } } + [JsonProperty("privacy")] public eMeetingPrivacy Privacy { get; set; } + [JsonProperty("joinable")] public bool Joinable { get { - return StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now - && DateTime.Now <= EndTime; //.AddMinutes(-5); + var joinable = StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now + && DateTime.Now <= EndTime.AddMinutes(-5); + //Debug.Console(2, "Meeting Id: {0} joinable: {1}", Id, joinable); + return joinable; } } //public string ConferenceNumberToDial { get; set; } + [JsonProperty("conferencePassword")] public string ConferencePassword { get; set; } + [JsonProperty("isOneButtonToPushMeeting")] public bool IsOneButtonToPushMeeting { get; set; } + [JsonProperty("calls")] public List Calls { get; private set; } + /// + /// Tracks the change types that have already been notified for + /// + [JsonIgnore] + public eMeetingEventChangeType NotifiedChangeTypes { get; set; } + public Meeting() { Calls = new List(); diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index a7be2443..7cfe82c0 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -550,6 +550,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + return base.CustomActivate(); + } + + #region Overrides of Device + + public override void Initialize() + { var socket = Communication as ISocketStatus; if (socket != null) { @@ -558,9 +565,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco Communication.Connect(); - CommunicationMonitor.Start(); + CommunicationMonitor.Start(); - string prefix = "xFeedback register "; + const string prefix = "xFeedback register "; CliFeedbackRegistrationExpression = prefix + "/Configuration" + Delimiter + @@ -575,14 +582,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco prefix + "/Status/Video/Layout" + Delimiter + prefix + "/Status/Video/Input/MainVideoMute" + Delimiter + prefix + "/Bookings" + Delimiter + - prefix + "/Event/CallDisconnect" + Delimiter + + prefix + "/Event/CallDisconnect" + Delimiter + prefix + "/Event/Bookings" + Delimiter + prefix + "/Event/CameraPresetListUpdated" + Delimiter + - prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter; - - return base.CustomActivate(); + prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter; } + #endregion + /// /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc. /// 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 c4bfa298..ca199957 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 @@ -139,7 +139,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public override void Dial(Meeting meeting) { - throw new NotImplementedException(); + Debug.Console(1, this, "Dial Meeting: {0}", meeting.Id); + var call = new CodecActiveCallItem() { Name = meeting.Title, Number = meeting.Id, Id = meeting.Id, Status = eCodecCallStatus.Dialing, Direction = eCodecCallDirection.Outgoing, Type = eCodecCallType.Video }; + ActiveCalls.Add(call); + OnCallStatusChange(call); + + //ActiveCallCountFeedback.FireUpdate(); + // Simulate 2-second ring, then connecting, then connected + new CTimer(o => + { + call.Type = eCodecCallType.Video; + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call); + new CTimer(oo => SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connected, call), 1000); + }, 2000); + } /// @@ -396,12 +409,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec if (_CodecSchedule == null || _CodecSchedule.Meetings.Count == 0 || _CodecSchedule.Meetings[_CodecSchedule.Meetings.Count - 1].StartTime < DateTime.Now) { - _CodecSchedule = new CodecScheduleAwareness(); + _CodecSchedule = new CodecScheduleAwareness(1000); for (int i = 0; i < 5; i++) { var m = new Meeting(); - m.StartTime = DateTime.Now.AddMinutes(3).AddHours(i); - m.EndTime = DateTime.Now.AddHours(i).AddMinutes(30); + 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.Title = "Meeting " + i; m.Calls.Add(new Call() { Number = i + "meeting@fake.com"}); _CodecSchedule.Meetings.Add(m); 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 f8d4bf47..0142f5e9 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 @@ -719,7 +719,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom CrestronConsole.AddNewConsoleCommand(s => GetBookings(), "GetZoomRoomBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); - var socket = Communication as ISocketStatus; + + + return base.CustomActivate(); + } + + #region Overrides of Device + + public override void Initialize() + { + var socket = Communication as ISocketStatus; if (socket != null) { socket.ConnectionChange += socket_ConnectionChange; @@ -730,11 +739,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Communication.Connect(); CommunicationMonitor.Start(); + } - return base.CustomActivate(); - } + #endregion - public void SetCommDebug(string s) + public void SetCommDebug(string s) { if (s == "1") { diff --git a/packages.config b/packages.config index 3876040a..3494f2eb 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file