diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 698a159f..9c82b403 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,7 @@ 2.0.0-local + $(Version) PepperDash Technologies PepperDash Technologies PepperDash Essentials diff --git a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs index 545733a7..68859760 100644 --- a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs +++ b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs @@ -375,8 +375,7 @@ namespace PepperDash.Essentials.Core.Bridges { try { - if (Debug.Level >= 1) - Debug.Console(1, this, "EiscApiAdvanced change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); + Debug.Console(2, this, "EiscApiAdvanced change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); var uo = args.Sig.UserObject; if (uo == null) return; diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs index 288141bb..388e6ac1 100644 --- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs +++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs @@ -1,6 +1,6 @@ using PepperDash.Essentials.Core; -namespace PepperDash_Essentials_Core.Bridges.JoinMaps +namespace PepperDash.Essentials.Core.Bridges.JoinMaps { public sealed class GenericIrControllerJoinMap : JoinMapBaseAdvanced { diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs index 3f2901c9..04d75d41 100644 --- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs +++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs @@ -1,7 +1,7 @@ using System; using PepperDash.Essentials.Core; -namespace PepperDash_Essentials_Core.Bridges +namespace PepperDash.Essentials.Core.Bridges { public class HdPsXxxControllerJoinMap : JoinMapBaseAdvanced { diff --git a/src/PepperDash.Essentials.Core/Config/ILoadConfig.cs b/src/PepperDash.Essentials.Core/Config/ILoadConfig.cs new file mode 100644 index 00000000..00bbf5f6 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Config/ILoadConfig.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PepperDash.Essentials.Core +{ + public interface ILoadConfig + { + void GoWithLoad(); + } +} diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs index eff9e70f..1ca53f67 100644 --- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs +++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IMobileControl.cs @@ -1,8 +1,17 @@ using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using PepperDash.Core; namespace PepperDash.Essentials.Core.DeviceTypeInterfaces { + /// + /// Use this interface on a device or room if it uses custom Mobile Control messengers + /// + public interface ICustomMobileControl : IKeyed + { + } + /// /// Describes a MobileControlSystemController /// @@ -17,14 +26,45 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces /// Describes a MobileSystemController that accepts IEssentialsRoom /// public interface IMobileControl3 : IMobileControl + { + void SendMessageObject(IMobileControlMessage o); + + void AddAction(string key, Action action); + + void RemoveAction(string key); + + void AddDeviceMessenger(IMobileControlMessenger messenger); + + bool CheckForDeviceMessenger(string key); + } + + /// + /// Describes a mobile control messenger + /// + public interface IMobileControlMessenger: IKeyed { - void CreateMobileControlRoomBridge(IEssentialsRoom room, IMobileControl parent); + IMobileControl3 AppServerController { get; } + string MessagePath { get; } + void RegisterWithAppServer(IMobileControl3 appServerController); + } + + public interface IMobileControlMessage + { + [JsonProperty("type")] + string Type { get; } + + [JsonProperty("clientId", NullValueHandling = NullValueHandling.Ignore)] + string ClientId { get; } + + [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] + JToken Content { get; } + } /// /// Describes a MobileControl Room Bridge /// - public interface IMobileControlRoomBridge : IKeyed + public interface IMobileControlRoomMessenger : IKeyed { event EventHandler UserCodeChanged; @@ -45,5 +85,7 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces string RoomName { get; } string AppUrl { get; } + + void UpdateAppUrl(string url); } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs b/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs index 1846a512..cd9e3923 100644 --- a/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs +++ b/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs @@ -48,6 +48,20 @@ namespace PepperDash.Essentials.Core } }); } + + public override bool CustomActivate() + { + CreateMobileControlMessengers(); + + return base.CustomActivate(); + } + + /// + /// Override this method to build and create custom Mobile Control Messengers during the Activation phase + /// + protected virtual void CreateMobileControlMessengers() { + + } } [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] diff --git a/src/PepperDash.Essentials.Core/Devices/GenericIRController.cs b/src/PepperDash.Essentials.Core/Devices/GenericIRController.cs index b7c15fce..d0c6a61b 100644 --- a/src/PepperDash.Essentials.Core/Devices/GenericIRController.cs +++ b/src/PepperDash.Essentials.Core/Devices/GenericIRController.cs @@ -8,7 +8,7 @@ using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; -using PepperDash_Essentials_Core.Bridges.JoinMaps; +using PepperDash.Essentials.Core.Bridges.JoinMaps; namespace PepperDash.Essentials.Core.Devices { diff --git a/src/PepperDash.Essentials.Core/Devices/PduInterfaces.cs b/src/PepperDash.Essentials.Core/Devices/PduInterfaces.cs index 94aa71ac..1c0431b5 100644 --- a/src/PepperDash.Essentials.Core/Devices/PduInterfaces.cs +++ b/src/PepperDash.Essentials.Core/Devices/PduInterfaces.cs @@ -4,12 +4,11 @@ using Crestron.SimplSharp; using PepperDash.Core; using PepperDash.Essentials.Core; -namespace PepperDash_Essentials_Core.Devices +namespace PepperDash.Essentials.Core.Devices { /// /// Interface for any device that is able to control it'spower and has a configurable reboot time - /// - [Obsolete("PepperDash_Essentials_Core.Devices is Deprecated - use PepperDash.Essentials.Core")] + /// public interface IHasPowerCycle : IKeyName, IHasPowerControlWithFeedback { /// @@ -25,8 +24,7 @@ namespace PepperDash_Essentials_Core.Devices /// /// Interface for any device that contains a collection of IHasPowerReboot Devices - /// - [Obsolete("PepperDash_Essentials_Core.Devices is Deprecated - use PepperDash.Essentials.Core")] + /// public interface IHasControlledPowerOutlets : IKeyName { /// diff --git a/src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs b/src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs index 8b506141..db6c07cc 100644 --- a/src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs +++ b/src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs @@ -189,7 +189,7 @@ namespace PepperDash.Essentials.Core CrestronConsole.ConsoleCommandResponse( @"Type: '{0}' CType: '{1}' - Description: {2}", type.Key, cType, description); + Description: {2}{3}", type.Key, cType, description, CrestronEnvironment.NewLine); } } diff --git a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj index 13af0017..cae9e659 100644 --- a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj +++ b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj @@ -21,14 +21,9 @@ pdbonly - - - - - - + diff --git a/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs b/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs index dcc492df..edfd65ef 100644 --- a/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs +++ b/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs @@ -75,11 +75,6 @@ namespace PepperDash.Essentials version = Global.AssemblyVersion; break; } - case ("PepperDash_Essentials_DM.dll"): - { - version = Global.AssemblyVersion; - break; - } case ("Essentials Devices Common.dll"): { version = Global.AssemblyVersion; diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsDualDisplayRoomPropertiesConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsDualDisplayRoomPropertiesConfig.cs new file mode 100644 index 00000000..2eb56fd3 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsDualDisplayRoomPropertiesConfig.cs @@ -0,0 +1,8 @@ + +namespace PepperDash.Essentials.Room.Config +{ + public class EssentialsDualDisplayRoomPropertiesConfig : EssentialsNDisplayRoomPropertiesConfig + { + + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsHuddleRoomPropertiesConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsHuddleRoomPropertiesConfig.cs new file mode 100644 index 00000000..a0b3499f --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsHuddleRoomPropertiesConfig.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Room.Config +{ + /// + /// + /// + public class EssentialsHuddleRoomPropertiesConfig : EssentialsRoomPropertiesConfig + { + /// + /// The key of the default display device + /// + [JsonProperty("defaultDisplayKey")] + public string DefaultDisplayKey { get; set; } + + /// + /// The key of the default audio device + /// + [JsonProperty("defaultAudioKey")] + public string DefaultAudioKey { get; set; } + + /// + /// The key of the source list for the room + /// + [JsonProperty("sourceListKey")] + public string SourceListKey { get; set; } + + /// + /// The key of the default source item from the source list + /// + [JsonProperty("defaultSourceItem")] + public string DefaultSourceItem { get; set; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs new file mode 100644 index 00000000..27164f57 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs @@ -0,0 +1,13 @@ + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Room.Config +{ + + public class EssentialsHuddleVtc1PropertiesConfig : EssentialsConferenceRoomPropertiesConfig + { + [JsonProperty("defaultDisplayKey")] + public string DefaultDisplayKey { get; set; } + + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsNDisplayRoomPropertiesConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsNDisplayRoomPropertiesConfig.cs new file mode 100644 index 00000000..6d8762fa --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsNDisplayRoomPropertiesConfig.cs @@ -0,0 +1,34 @@ + +using System.Collections.Generic; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Room.Config +{ + /// + /// + /// + public class EssentialsNDisplayRoomPropertiesConfig : EssentialsConferenceRoomPropertiesConfig + { + [JsonProperty("defaultAudioBehavior")] + public string DefaultAudioBehavior { get; set; } + [JsonProperty("defaultVideoBehavior")] + public string DefaultVideoBehavior { get; set; } + [JsonProperty("displays")] + public Dictionary Displays { get; set; } + + public EssentialsNDisplayRoomPropertiesConfig() + { + Displays = new Dictionary(); + } + + } + + public class DisplayItem : IKeyName + { + public string Key { get; set; } + public string Name { get; set; } + } + +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsPresentationPropertiesConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsPresentationPropertiesConfig.cs new file mode 100644 index 00000000..53333f07 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsPresentationPropertiesConfig.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace PepperDash.Essentials.Room.Config +{ + /// + /// + /// + public class EssentialsPresentationRoomPropertiesConfig : EssentialsRoomPropertiesConfig + { + public string DefaultAudioBehavior { get; set; } + public string DefaultAudioKey { get; set; } + public string DefaultVideoBehavior { get; set; } + public List DisplayKeys { get; set; } + public string SourceListKey { get; set; } + public bool HasDsp { get; set; } + + public EssentialsPresentationRoomPropertiesConfig() + { + DisplayKeys = new List(); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomConfig.cs new file mode 100644 index 00000000..ebb0f84b --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomConfig.cs @@ -0,0 +1,364 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Privacy; + +namespace PepperDash.Essentials.Room.Config +{ + public class EssentialsRoomConfigHelper + { + /// + /// 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, IEssentialsRoom room) + { + // This emergency + var emergency = props.Emergency; + if (emergency != null) + { + //switch on emergency type here. Right now only contact and shutdown + var e = new EssentialsRoomEmergencyContactClosure(room.Key + "-emergency", props.Emergency, room); + DeviceManager.AddDevice(e); + } + return null; + } + + /// + /// + /// + /// + /// + /// + public static MicrophonePrivacyController GetMicrophonePrivacy( + EssentialsRoomPropertiesConfig props, IPrivacy room) + { + var microphonePrivacy = props.MicrophonePrivacy; + if (microphonePrivacy == null) + { + Debug.Console(0, "Cannot create microphone privacy with null properties"); + return null; + } + // Get the MicrophonePrivacy device from the device manager + var mP = (DeviceManager.GetDeviceForKey(props.MicrophonePrivacy.DeviceKey) as MicrophonePrivacyController); + // Set this room as the IPrivacy device + if (mP == null) + { + Debug.Console(0, "ERROR: Selected device {0} is not MicrophonePrivacyController", props.MicrophonePrivacy.DeviceKey); + return null; + } + mP.SetPrivacyDevice(room); + + var behaviour = props.MicrophonePrivacy.Behaviour.ToLower(); + + if (behaviour == null) + { + Debug.Console(0, "WARNING: No behaviour defined for MicrophonePrivacyController"); + return null; + } + if (behaviour == "trackroomstate") + { + // Tie LED enable to room power state + var essRoom = room as IEssentialsRoom; + essRoom.OnFeedback.OutputChange += (o, a) => + { + if (essRoom.OnFeedback.BoolValue) + mP.EnableLeds = true; + else + mP.EnableLeds = false; + }; + + mP.EnableLeds = essRoom.OnFeedback.BoolValue; + } + else if (behaviour == "trackcallstate") + { + // Tie LED enable to room power state + var inCallRoom = room as IHasInCallFeedback; + inCallRoom.InCallFeedback.OutputChange += (o, a) => + { + if (inCallRoom.InCallFeedback.BoolValue) + mP.EnableLeds = true; + else + mP.EnableLeds = false; + }; + + mP.EnableLeds = inCallRoom.InCallFeedback.BoolValue; + } + + return mP; + } + + } + + /// + /// + /// + public class EssentialsRoomPropertiesConfig + { + [JsonProperty("addresses")] + public EssentialsRoomAddressPropertiesConfig Addresses { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("emergency")] + public EssentialsRoomEmergencyConfig Emergency { get; set; } + + [JsonProperty("help")] + public EssentialsHelpPropertiesConfig Help { get; set; } + + [JsonProperty("helpMessage")] + public string HelpMessage { get; set; } + + /// + /// Read this value to get the help message. It checks for the old and new config format. + /// + public string HelpMessageForDisplay + { + get + { + if(Help != null && !string.IsNullOrEmpty(Help.Message)) + { + return Help.Message; + } + else + { + return HelpMessage; + } + } + } + + [JsonProperty("environment")] + public EssentialsEnvironmentPropertiesConfig Environment { get; set; } + + [JsonProperty("logo")] + public EssentialsLogoPropertiesConfig LogoLight { get; set; } + + [JsonProperty("logoDark")] + public EssentialsLogoPropertiesConfig LogoDark { get; set; } + + [JsonProperty("microphonePrivacy")] + public EssentialsRoomMicrophonePrivacyConfig MicrophonePrivacy { get; set; } + + [JsonProperty("occupancy")] + public EssentialsRoomOccSensorConfig Occupancy { get; set; } + + [JsonProperty("oneButtonMeeting")] + public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; } + + [JsonProperty("shutdownVacancySeconds")] + public int ShutdownVacancySeconds { get; set; } + + [JsonProperty("shutdownPromptSeconds")] + public int ShutdownPromptSeconds { get; set; } + + [JsonProperty("tech")] + public EssentialsRoomTechConfig Tech { get; set; } + + [JsonProperty("volumes")] + public EssentialsRoomVolumesConfig Volumes { get; set; } + + [JsonProperty("fusion")] + public EssentialsRoomFusionConfig Fusion { get; set; } + + [JsonProperty("essentialsRoomUiBehaviorConfig", NullValueHandling=NullValueHandling.Ignore)] + public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; } + + [JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")] + public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; } + + /// + /// Indicates if this room represents a combination of other rooms + /// + [JsonProperty("isRoomCombinationScenario")] + public bool IsRoomCombinationScenario { get; set; } + + public EssentialsRoomPropertiesConfig() + { + LogoLight = new EssentialsLogoPropertiesConfig(); + LogoDark = new EssentialsLogoPropertiesConfig(); + } + } + + public class EssentialsRoomUiBehaviorConfig + { + [JsonProperty("disableActivityButtonsWhileWarmingCooling")] + public bool DisableActivityButtonsWhileWarmingCooling { get; set; } + } + + public class EssentialsAvRoomPropertiesConfig : EssentialsRoomPropertiesConfig + { + [JsonProperty("defaultAudioKey")] + public string DefaultAudioKey { get; set; } + [JsonProperty("sourceListKey")] + public string SourceListKey { get; set; } + [JsonProperty("destinationListKey")] + public string DestinationListKey { get; set; } + [JsonProperty("defaultSourceItem")] + public string DefaultSourceItem { get; set; } + /// + /// Indicates if the room supports advanced sharing + /// + [JsonProperty("supportsAdvancedSharing")] + public bool SupportsAdvancedSharing { get; set; } + /// + /// Indicates if non-tech users can change the share mode + /// + [JsonProperty("userCanChangeShareMode")] + public bool UserCanChangeShareMode { get; set; } + } + + public class EssentialsConferenceRoomPropertiesConfig : EssentialsAvRoomPropertiesConfig + { + [JsonProperty("videoCodecKey")] + public string VideoCodecKey { get; set; } + [JsonProperty("audioCodecKey")] + public string AudioCodecKey { get; set; } + } + + public class EssentialsEnvironmentPropertiesConfig + { + public bool Enabled { get; set; } + + [JsonProperty("deviceKeys")] + public List DeviceKeys { get; set; } + + public EssentialsEnvironmentPropertiesConfig() + { + DeviceKeys = new List(); + } + + } + + public class EssentialsRoomFusionConfig + { + public uint IpIdInt + { + get + { + try + { + return Convert.ToUInt32(IpId, 16); + } + catch (Exception) + { + throw new FormatException(string.Format("ERROR:Unable to convert IP ID: {0} to hex. Error:\n{1}", IpId)); + } + + } + } + + [JsonProperty("ipId")] + public string IpId { get; set; } + + [JsonProperty("joinMapKey")] + public string JoinMapKey { get; set; } + + } + + public class EssentialsRoomMicrophonePrivacyConfig + { + [JsonProperty("deviceKey")] + public string DeviceKey { get; set; } + + [JsonProperty("behaviour")] + public string Behaviour { get; set; } + } + + /// + /// Properties for the help text box + /// + public class EssentialsHelpPropertiesConfig + { + [JsonProperty("message")] + public string Message { get; set; } + + [JsonProperty("showCallButton")] + public bool ShowCallButton { get; set; } + + /// + /// Defaults to "Call Help Desk" + /// + [JsonProperty("callButtonText")] + public string CallButtonText { get; set; } + + public EssentialsHelpPropertiesConfig() + { + CallButtonText = "Call Help Desk"; + } + } + + /// + /// + /// + public class EssentialsOneButtonMeetingPropertiesConfig + { + [JsonProperty("enable")] + public bool Enable { get; set; } + } + + public class EssentialsRoomAddressPropertiesConfig + { + [JsonProperty("phoneNumber")] + public string PhoneNumber { get; set; } + + [JsonProperty("sipAddress")] + public string SipAddress { get; set; } + } + + + /// + /// Properties for the room's logo on panels + /// + public class EssentialsLogoPropertiesConfig + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + /// + /// Gets either the custom URL, a local-to-processor URL, or null if it's a default logo + /// + public string GetLogoUrlLight() + { + if (Type == "url") + return Url; + if (Type == "system") + return string.Format("http://{0}:8080/logo.png", + CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)); + return null; + } + + public string GetLogoUrlDark() + { + if (Type == "url") + return Url; + if (Type == "system") + return string.Format("http://{0}:8080/logo-dark.png", + CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)); + return null; + } + } + + /// + /// Represents occupancy sensor(s) setup for a room + /// + public class EssentialsRoomOccSensorConfig + { + [JsonProperty("deviceKey")] + public string DeviceKey { get; set; } + + [JsonProperty("timeoutMinutes")] + public int TimeoutMinutes { get; set; } + } + + public class EssentialsRoomTechConfig + { + [JsonProperty("password")] + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomEmergencyConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomEmergencyConfig.cs new file mode 100644 index 00000000..76199a91 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomEmergencyConfig.cs @@ -0,0 +1,30 @@ +namespace PepperDash.Essentials.Room.Config +{ + /// + /// + /// + public class EssentialsRoomEmergencyConfig + { + public EssentialsRoomEmergencyTriggerConfig Trigger { get; set; } + + public string Behavior { get; set; } + } + + /// + /// + /// + public class EssentialsRoomEmergencyTriggerConfig + { + /// + /// contact, + /// + public string Type { get; set; } + /// + /// Input number if contact + /// + public int Number { get; set; } + + public bool TriggerOnClose { get; set; } + + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsTechRoomConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsTechRoomConfig.cs new file mode 100644 index 00000000..507bac5e --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsTechRoomConfig.cs @@ -0,0 +1,77 @@ + +using System.Collections.Generic; +using Newtonsoft.Json; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials.Room.Config +{ + public class EssentialsTechRoomConfig + { + /// + /// The key of the dummy device used to enable routing + /// + [JsonProperty("dummySourceKey")] + public string DummySourceKey { get; set; } + + /// + /// The keys of the displays assigned to this room + /// + [JsonProperty("displays")] + public List Displays { get; set; } + + /// + /// The keys of the tuners assinged to this room + /// + [JsonProperty("tuners")] + public List Tuners { get; set; } + + /// + /// PIN to access the room as a normal user + /// + [JsonProperty("userPin")] + public string UserPin { get; set; } + + /// + /// PIN to access the room as a tech user + /// + [JsonProperty("techPin")] + public string TechPin { get; set; } + + /// + /// Name of the presets file. Path prefix is assumed to be /html/presets/lists/ + /// + [JsonProperty("presetsFileName")] + public string PresetsFileName { get; set; } + + [JsonProperty("scheduledEvents")] + public List ScheduledEvents { get; set; } + + /// + /// Indicates that the room is the primary when true + /// + [JsonProperty("isPrimary")] + public bool IsPrimary { get; set; } + + /// + /// Indicates which tuners should mirror preset recall when two rooms are configured in a primary->secondary scenario + /// + [JsonProperty("mirroredTuners")] + public Dictionary MirroredTuners { get; set; } + + [JsonProperty("helpMessage")] + public string HelpMessage { get; set; } + + /// + /// Indicates the room + /// + [JsonProperty("isTvPresetsProvider")] + public bool IsTvPresetsProvider; + + public EssentialsTechRoomConfig() + { + Displays = new List(); + Tuners = new List(); + ScheduledEvents = new List(); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsVolumeLevelConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsVolumeLevelConfig.cs new file mode 100644 index 00000000..5b9450f2 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsVolumeLevelConfig.cs @@ -0,0 +1,91 @@ +using System; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Room.Config +{ + /// + /// + /// + public class EssentialsRoomVolumesConfig + { + public EssentialsVolumeLevelConfig Master { get; set; } + public EssentialsVolumeLevelConfig Program { get; set; } + public EssentialsVolumeLevelConfig AudioCallRx { get; set; } + public EssentialsVolumeLevelConfig AudioCallTx { get; set; } + } + + /// + /// + /// + public class EssentialsVolumeLevelConfig + { + public string DeviceKey { get; set; } + public string Label { get; set; } + public int Level { get; set; } + + /// + /// Helper to get the device associated with key - one timer. + /// + public IBasicVolumeWithFeedback GetDevice() + { + throw new NotImplementedException("This method references DM CHASSIS Directly"); + /* + // DM output card format: deviceKey--output~number, dm8x8-1--output~4 + var match = Regex.Match(DeviceKey, @"([-_\w]+)--(\w+)~(\d+)"); + if (match.Success) + { + var devKey = match.Groups[1].Value; + var chassis = DeviceManager.GetDeviceForKey(devKey) as DmChassisController; + if (chassis != null) + { + var outputNum = Convert.ToUInt32(match.Groups[3].Value); + if (chassis.VolumeControls.ContainsKey(outputNum)) // should always... + return chassis.VolumeControls[outputNum]; + } + // No volume for some reason. We have failed as developers + return null; + } + + // DSP/DMPS format: deviceKey--levelName, biampTesira-1--master + match = Regex.Match(DeviceKey, @"([-_\w]+)--(.+)"); + if (match.Success) + { + var devKey = match.Groups[1].Value; + var dsp = DeviceManager.GetDeviceForKey(devKey) as BiampTesiraForteDsp; + if (dsp != null) + { + var levelTag = match.Groups[2].Value; + if (dsp.LevelControlPoints.ContainsKey(levelTag)) // should always... + return dsp.LevelControlPoints[levelTag]; + } + + var dmps = DeviceManager.GetDeviceForKey(devKey) as DmpsAudioOutputController; + if (dmps != null) + { + var levelTag = match.Groups[2].Value; + switch (levelTag) + { + case "master": + return dmps.MasterVolumeLevel; + case "source": + return dmps.SourceVolumeLevel; + case "micsmaster": + return dmps.MicsMasterVolumeLevel; + case "codec1": + return dmps.Codec1VolumeLevel; + case "codec2": + return dmps.Codec2VolumeLevel; + default: + return dmps.MasterVolumeLevel; + } + } + // No volume for some reason. We have failed as developers + return null; + } + + return null; + } + * */ + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/Config/SimplRoomPropertiesConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/SimplRoomPropertiesConfig.cs new file mode 100644 index 00000000..fdd9b857 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/Config/SimplRoomPropertiesConfig.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Room.Config +{ + public class SimplRoomPropertiesConfig : EssentialsHuddleVtc1PropertiesConfig + { + [JsonProperty("roomPhoneNumber")] + public string RoomPhoneNumber { get; set; } + [JsonProperty("roomURI")] + public string RoomURI { get; set; } + [JsonProperty("speedDials")] + public List SpeedDials { get; set; } + [JsonProperty("volumeSliderNames")] + public List VolumeSliderNames { get; set; } + } + + public class SimplSpeedDial + { + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("number")] + public string Number { get; set; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/EsentialsRoomEmergencyContactClosure.cs b/src/PepperDash.Essentials.Core/Room/EsentialsRoomEmergencyContactClosure.cs new file mode 100644 index 00000000..7ad7f700 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/EsentialsRoomEmergencyContactClosure.cs @@ -0,0 +1,47 @@ +using System; +using Crestron.SimplSharpPro; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials.Core +{ + public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase + { + IEssentialsRoom Room; + string Behavior; + bool TriggerOnClose; + + public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, IEssentialsRoom room) : + base(key) + { + Room = room; + var cs = Global.ControlSystem; + + if (config.Trigger.Type.Equals("contact", StringComparison.OrdinalIgnoreCase)) + { + var portNum = (uint)config.Trigger.Number; + if (portNum <= cs.NumberOfDigitalInputPorts) + { + cs.DigitalInputPorts[portNum].Register(); + cs.DigitalInputPorts[portNum].StateChange += EsentialsRoomEmergencyContactClosure_StateChange; + } + } + Behavior = config.Behavior; + TriggerOnClose = config.Trigger.TriggerOnClose; + } + + void EsentialsRoomEmergencyContactClosure_StateChange(DigitalInput digitalInput, DigitalInputEventArgs args) + { + if (args.State && TriggerOnClose || !args.State && !TriggerOnClose) + RunEmergencyBehavior(); + } + + /// + /// + /// + public void RunEmergencyBehavior() + { + if (Behavior.Equals("shutdown")) + Room.Shutdown(); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs b/src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs index abfc34b8..d3b78112 100644 --- a/src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs +++ b/src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs @@ -58,7 +58,7 @@ namespace PepperDash.Essentials.Core /// /// The bridge for this room if Mobile Control is enabled /// - public IMobileControlRoomBridge MobileControlRoomBridge { get; private set; } + public IMobileControlRoomMessenger MobileControlRoomBridge { get; private set; } /// /// The config name of the source list @@ -209,7 +209,7 @@ namespace PepperDash.Essentials.Core } else { - MobileControlRoomBridge = mcBridge as IMobileControlRoomBridge; + MobileControlRoomBridge = mcBridge as IMobileControlRoomMessenger; Debug.Console(1, this, "*********************Mobile Control Bridge found and enabled for this room"); IsMobileControlEnabled = true; } diff --git a/src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs b/src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs index 9a70f980..d14a46be 100644 --- a/src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs +++ b/src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs @@ -23,7 +23,7 @@ namespace PepperDash.Essentials.Core BoolFeedback IsCoolingDownFeedback { get; } bool IsMobileControlEnabled { get; } - IMobileControlRoomBridge MobileControlRoomBridge { get; } + IMobileControlRoomMessenger MobileControlRoomBridge { get; } string SourceListKey { get; } diff --git a/src/PepperDash.Essentials.Core/Room/IRoomEventSchedule.cs b/src/PepperDash.Essentials.Core/Room/IRoomEventSchedule.cs new file mode 100644 index 00000000..c2595151 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Room/IRoomEventSchedule.cs @@ -0,0 +1,20 @@ +using PepperDash.Essentials.Room.Config; +using System; +using System.Collections.Generic; + +namespace PepperDash.Essentials.Core +{ + public interface IRoomEventSchedule + { + void AddOrUpdateScheduledEvent(ScheduledEventConfig eventConfig); + + List GetScheduledEvents(); + + event EventHandler ScheduledEventsChanged; + } + + public class ScheduledEventEventArgs : EventArgs + { + public List ScheduledEvents; + } +} diff --git a/src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs b/src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs index c7be5048..00cdc28e 100644 --- a/src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs +++ b/src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs @@ -154,8 +154,7 @@ namespace PepperDash.Essentials.Core.UI private void Panel_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) { - if (Debug.Level == 2) - Debug.Console(2, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); + Debug.Console(5, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); var uo = args.Sig.UserObject; if (uo is Action) (uo as Action)(args.Sig.BoolValue); diff --git a/src/PepperDash.Essentials.Core/Web/EssemtialsWebApi.cs b/src/PepperDash.Essentials.Core/Web/EssemtialsWebApi.cs deleted file mode 100644 index eb012378..00000000 --- a/src/PepperDash.Essentials.Core/Web/EssemtialsWebApi.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharp.WebScripting; -using PepperDash.Core; -using PepperDash.Core.Web; -using PepperDash.Essentials.Core.Web.RequestHandlers; - -namespace PepperDash.Essentials.Core.Web -{ - public class EssemtialsWebApi : EssentialsDevice - { - private readonly WebApiServer _server; - - /// - /// http(s)://{ipaddress}/cws/{basePath} - /// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath} - /// - private readonly string _defaultBasePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance - ? string.Format("/app{0:00}/api", InitialParametersClass.ApplicationNumber) - : "/api"; - - private const int DebugTrace = 0; - private const int DebugInfo = 1; - private const int DebugVerbose = 2; - - /// - /// CWS base path - /// - public string BasePath { get; private set; } - - /// - /// Tracks if CWS is registered - /// - public bool IsRegistered - { - get { return _server.IsRegistered; } - } - - /// - /// Constructor - /// - /// - /// - public EssemtialsWebApi(string key, string name) - : this(key, name, null) - { - } - - /// - /// Constructor - /// - /// - /// - /// - public EssemtialsWebApi(string key, string name, EssentialsWebApiPropertiesConfig config) - : base(key, name) - { - Key = key; - - if (config == null) - BasePath = _defaultBasePath; - else - BasePath = string.IsNullOrEmpty(config.BasePath) ? _defaultBasePath : config.BasePath; - - _server = new WebApiServer(Key, Name, BasePath); - } - - /// - /// Custom activate, add routes - /// - /// - public override bool CustomActivate() - { - var routes = new List - { - new HttpCwsRoute("reportversions") - { - Name = "ReportVersions", - RouteHandler = new ReportVersionsRequestHandler() - }, - new HttpCwsRoute("appdebug") - { - Name = "AppDebug", - RouteHandler = new AppDebugRequestHandler() - }, - new HttpCwsRoute("devlist") - { - Name = "DevList", - RouteHandler = new DevListRequestHandler() - }, - new HttpCwsRoute("devprops") - { - Name = "DevProps", - RouteHandler = new DevPropsRequestHandler() - }, - new HttpCwsRoute("devjson") - { - Name = "DevJson", - RouteHandler = new DevJsonRequestHandler() - }, - new HttpCwsRoute("setdevicestreamdebug") - { - Name = "SetDeviceStreamDebug", - RouteHandler = new SetDeviceStreamDebugRequestHandler() - }, - new HttpCwsRoute("disableallstreamdebug") - { - Name = "DisableAllStreamDebug", - RouteHandler = new DisableAllStreamDebugRequestHandler() - }, - new HttpCwsRoute("showconfig") - { - Name = "ShowConfig", - RouteHandler = new ShowConfigRequestHandler() - }, - new HttpCwsRoute("gettypes") - { - Name = "GetTypes", - RouteHandler = new GetTypesRequestHandler() - }, - new HttpCwsRoute("gettypes/{filter}") - { - Name = "GetTypesByFilter", - RouteHandler = new GetTypesByFilterRequestHandler() - }, - new HttpCwsRoute("getjoinmap/{bridgeKey}") - { - Name = "GetJoinMapsForBridgeKey", - RouteHandler = new GetJoinMapForBridgeKeyRequestHandler() - }, - new HttpCwsRoute("getjoinmap/{bridgeKey}/{deviceKey}") - { - Name = "GetJoinMapsForDeviceKey", - RouteHandler = new GetJoinMapForDeviceKeyRequestHandler() - }, - new HttpCwsRoute("feedbacks/{deviceKey}") - { - Name = "GetFeedbacksForDeviceKey", - RouteHandler = new GetFeedbacksForDeviceRequestHandler() - } - }; - - foreach (var route in routes.Where(route => route != null)) - { - var r = route; - _server.AddRoute(r); - } - - return base.CustomActivate(); - } - - /// - /// Initializes the CWS class - /// - public override void Initialize() - { - // If running on an appliance - if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance) - { - /* - WEBSERVER [ON | OFF | TIMEOUT | MAXSESSIONSPERUSER ] - */ - var response = string.Empty; - CrestronConsole.SendControlSystemCommand("webserver", ref response); - if (response.Contains("OFF")) return; - - var is4Series = eCrestronSeries.Series4 == (Global.ProcessorSeries & eCrestronSeries.Series4); - Debug.Console(DebugTrace, Debug.ErrorLogLevel.Notice, "Starting Essentials Web API on {0} Appliance", is4Series ? "4-series" : "3-series"); - - _server.Start(); - - GetPaths(); - - return; - } - - // Automatically start CWS when running on a server (Linux OS, Virtual Control) - Debug.Console(DebugTrace, Debug.ErrorLogLevel.Notice, "Starting Essentials Web API on Virtual Control Server"); - - _server.Start(); - - GetPaths(); - } - - /// - /// Print the available pahts - /// - /// - /// http(s)://{ipaddress}/cws/{basePath} - /// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath} - /// - public void GetPaths() - { - Debug.Console(DebugTrace, this, "{0}", new String('-', 50)); - - var currentIp = CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); - - var hostname = CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); - - var path = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server - ? string.Format("http(s)://{0}/VirtualControl/Rooms/{1}/cws{2}", hostname, InitialParametersClass.RoomId, BasePath) - : string.Format("http(s)://{0}/cws{1}", currentIp, BasePath); - - Debug.Console(DebugTrace, this, "Server:{0}", path); - - var routeCollection = _server.GetRouteCollection(); - if (routeCollection == null) - { - Debug.Console(DebugTrace, this, "Server route collection is null"); - return; - } - Debug.Console(DebugTrace, this, "Configured Routes:"); - foreach (var route in routeCollection) - { - Debug.Console(DebugTrace, this, "{0}: {1}/{2}", route.Name, path, route.Url); - } - Debug.Console(DebugTrace, this, "{0}", new String('-', 50)); - } - } -} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Web/EssentialsWebApi.cs b/src/PepperDash.Essentials.Core/Web/EssentialsWebApi.cs new file mode 100644 index 00000000..165b9c21 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Web/EssentialsWebApi.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharp.WebScripting; +using PepperDash.Core; +using PepperDash.Core.Web; +using PepperDash.Essentials.Core.Web.RequestHandlers; + +namespace PepperDash.Essentials.Core.Web +{ + public class EssentialsWebApi : EssentialsDevice + { + private readonly WebApiServer _server; + + /// + /// http(s)://{ipaddress}/cws/{basePath} + /// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath} + /// + private readonly string _defaultBasePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance + ? string.Format("/app{0:00}/api", InitialParametersClass.ApplicationNumber) + : "/api"; + + private const int DebugTrace = 0; + private const int DebugInfo = 1; + private const int DebugVerbose = 2; + + /// + /// CWS base path + /// + public string BasePath { get; private set; } + + /// + /// Tracks if CWS is registered + /// + public bool IsRegistered + { + get { return _server.IsRegistered; } + } + + /// + /// Constructor + /// + /// + /// + public EssentialsWebApi(string key, string name) + : this(key, name, null) + { + } + + /// + /// Constructor + /// + /// + /// + /// + public EssentialsWebApi(string key, string name, EssentialsWebApiPropertiesConfig config) + : base(key, name) + { + Key = key; + + if (config == null) + BasePath = _defaultBasePath; + else + BasePath = string.IsNullOrEmpty(config.BasePath) ? _defaultBasePath : config.BasePath; + + _server = new WebApiServer(Key, Name, BasePath); + + SetupRoutes(); + } + + private void SetupRoutes() + { + var routes = new List + { + new HttpCwsRoute("versions") + { + Name = "ReportVersions", + RouteHandler = new ReportVersionsRequestHandler() + }, + new HttpCwsRoute("appdebug") + { + Name = "AppDebug", + RouteHandler = new AppDebugRequestHandler() + }, + new HttpCwsRoute("devices") + { + Name = "DevList", + RouteHandler = new DevListRequestHandler() + }, + new HttpCwsRoute("deviceCommands") + { + Name = "DevJson", + RouteHandler = new DevJsonRequestHandler() + }, + new HttpCwsRoute("deviceProperties/{deviceKey}") + { + Name = "DevProps", + RouteHandler = new DevPropsRequestHandler() + }, + new HttpCwsRoute("deviceMethods/{deviceKey}") + { + Name = "DevMethods", + RouteHandler = new DevMethodsRequestHandler() + }, + new HttpCwsRoute("deviceFeedbacks/{deviceKey}") + { + Name = "GetFeedbacksForDeviceKey", + RouteHandler = new GetFeedbacksForDeviceRequestHandler() + }, + new HttpCwsRoute("deviceStreamDebug") + { + Name = "SetDeviceStreamDebug", + RouteHandler = new SetDeviceStreamDebugRequestHandler() + }, + new HttpCwsRoute("disableAllStreamDebug") + { + Name = "DisableAllStreamDebug", + RouteHandler = new DisableAllStreamDebugRequestHandler() + }, + new HttpCwsRoute("config") + { + Name = "ShowConfig", + RouteHandler = new ShowConfigRequestHandler() + }, + new HttpCwsRoute("types") + { + Name = "GetTypes", + RouteHandler = new GetTypesRequestHandler() + }, + new HttpCwsRoute("types/{filter}") + { + Name = "GetTypesByFilter", + RouteHandler = new GetTypesByFilterRequestHandler() + }, + new HttpCwsRoute("joinMap/{bridgeKey}") + { + Name = "GetJoinMapsForBridgeKey", + RouteHandler = new GetJoinMapForBridgeKeyRequestHandler() + }, + new HttpCwsRoute("joinMap/{bridgeKey}/{deviceKey}") + { + Name = "GetJoinMapsForDeviceKey", + RouteHandler = new GetJoinMapForDeviceKeyRequestHandler() + }, + new HttpCwsRoute("debugSession") + { + Name = "DebugSession", + RouteHandler = new DebugSessionRequestHandler() + }, + new HttpCwsRoute("doNotLoadConfigOnNextBoot") + { + Name = "DoNotLoadConfigOnNextBoot", + RouteHandler = new DoNotLoadConfigOnNextBootRequestHandler() + }, + new HttpCwsRoute("restartProgram") + { + Name = "Restart Program", + RouteHandler = new RestartProgramRequestHandler() + }, + new HttpCwsRoute("loadConfig") + { + Name = "Load Config", + RouteHandler = new LoadConfigRequestHandler() + } + + }; + + AddRoute(routes); + } + + /// + /// Add a single route to the API. MUST be done during the activation phase + /// + /// + public void AddRoute(HttpCwsRoute route) + { + _server.AddRoute(route); + } + + /// + /// Add a collection of routes to the API. MUST be done during the activation phase + /// + /// + public void AddRoute(List routes) + { + foreach (var route in routes) + { + AddRoute(route); + } + } + + /// + /// Initializes the CWS class + /// + public override void Initialize() + { + // If running on an appliance + if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance) + { + /* + WEBSERVER [ON | OFF | TIMEOUT | MAXSESSIONSPERUSER ] + */ + var response = string.Empty; + CrestronConsole.SendControlSystemCommand("webserver", ref response); + if (response.Contains("OFF")) return; + + var is4Series = eCrestronSeries.Series4 == (Global.ProcessorSeries & eCrestronSeries.Series4); + Debug.Console(DebugTrace, Debug.ErrorLogLevel.Notice, "Starting Essentials Web API on {0} Appliance", is4Series ? "4-series" : "3-series"); + + _server.Start(); + + GetPaths(); + + return; + } + + // Automatically start CWS when running on a server (Linux OS, Virtual Control) + Debug.Console(DebugTrace, Debug.ErrorLogLevel.Notice, "Starting Essentials Web API on Virtual Control Server"); + + _server.Start(); + + GetPaths(); + } + + /// + /// Print the available pahts + /// + /// + /// http(s)://{ipaddress}/cws/{basePath} + /// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath} + /// + public void GetPaths() + { + Debug.Console(DebugTrace, this, "{0}", new String('-', 50)); + + var currentIp = CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); + + var hostname = CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); + + var path = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server + ? string.Format("http(s)://{0}/VirtualControl/Rooms/{1}/cws{2}", hostname, InitialParametersClass.RoomId, BasePath) + : string.Format("http(s)://{0}/cws{1}", currentIp, BasePath); + + Debug.Console(DebugTrace, this, "Server:{0}", path); + + var routeCollection = _server.GetRouteCollection(); + if (routeCollection == null) + { + Debug.Console(DebugTrace, this, "Server route collection is null"); + return; + } + Debug.Console(DebugTrace, this, "Configured Routes:"); + foreach (var route in routeCollection) + { + Debug.Console(DebugTrace, this, "{0}: {1}/{2}", route.Name, path, route.Url); + } + Debug.Console(DebugTrace, this, "{0}", new String('-', 50)); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Web/EssentialsWebApiFactory.cs b/src/PepperDash.Essentials.Core/Web/EssentialsWebApiFactory.cs index 3e90808c..5c44d3b3 100644 --- a/src/PepperDash.Essentials.Core/Web/EssentialsWebApiFactory.cs +++ b/src/PepperDash.Essentials.Core/Web/EssentialsWebApiFactory.cs @@ -4,7 +4,7 @@ using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.Core.Web { - public class EssentialsWebApiFactory : EssentialsDeviceFactory + public class EssentialsWebApiFactory : EssentialsDeviceFactory { public EssentialsWebApiFactory() { @@ -16,7 +16,7 @@ namespace PepperDash.Essentials.Core.Web Debug.Console(1, "Factory Attempting to create new Essentials Web API Server"); var props = dc.Properties.ToObject(); - if (props != null) return new EssemtialsWebApi(dc.Key, dc.Name, props); + if (props != null) return new EssentialsWebApi(dc.Key, dc.Name, props); Debug.Console(1, "Factory failed to create new Essentials Web API Server"); return null; diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/AppDebugRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/AppDebugRequestHandler.cs index 46a0f980..1157c5e1 100644 --- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/AppDebugRequestHandler.cs +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/AppDebugRequestHandler.cs @@ -2,6 +2,9 @@ using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Core.Web.RequestHandlers; +using System; +using Serilog.Events; +using Newtonsoft.Json.Converters; namespace PepperDash.Essentials.Core.Web.RequestHandlers { @@ -24,7 +27,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers /// protected override void HandleGet(HttpCwsContext context) { - var appDebug = new AppDebug { Level = Debug.Level }; + var appDebug = new AppDebug { MinimumLevel = Debug.WebsocketMinimumLogLevel }; var body = JsonConvert.SerializeObject(appDebug, Formatting.Indented); @@ -60,11 +63,11 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers } var appDebug = new AppDebug(); - var requestBody = JsonConvert.DeserializeAnonymousType(data, appDebug); + var requestBody = JsonConvert.DeserializeObject(data); - Debug.SetDebugLevel(requestBody.Level); + Debug.SetWebSocketMinimumDebugLevel(requestBody.MinimumLevel); - appDebug.Level = Debug.Level; + appDebug.MinimumLevel = Debug.WebsocketMinimumLogLevel; var responseBody = JsonConvert.SerializeObject(appDebug, Formatting.Indented); context.Response.StatusCode = 200; @@ -76,7 +79,8 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers public class AppDebug { - [JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)] - public int Level { get; set; } + [JsonProperty("minimumLevel", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public LogEventLevel MinimumLevel { get; set; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs new file mode 100644 index 00000000..9de58657 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs @@ -0,0 +1,96 @@ +using Crestron.SimplSharp; +using Crestron.SimplSharp.WebScripting; +using Crestron.SimplSharpPro.EthernetCommunication; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Core.Web.RequestHandlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PepperDash.Essentials.Core.Web.RequestHandlers +{ + public class DebugSessionRequestHandler : WebApiBaseRequestHandler + { + public DebugSessionRequestHandler() + : base(true) + { + } + + /// + /// Gets details for a debug session + /// + /// + protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContext context) + { + var routeData = context.Request.RouteData; + if (routeData == null) + { + context.Response.StatusCode = 400; + context.Response.StatusDescription = "Bad Request"; + context.Response.End(); + + return; + } + + try + { + var ip = CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); + + var port = 0; + + if (!Debug.WebsocketSink.IsRunning) + { + Debug.Console(0, "Starting WS Server"); + // Generate a random port within a specified range + port = new Random().Next(65435, 65535); + // Start the WS Server + Debug.WebsocketSink.StartServerAndSetPort(port); + Debug.SetWebSocketMinimumDebugLevel(Serilog.Events.LogEventLevel.Verbose); + } + + var url = Debug.WebsocketSink.Url; + + object data = new + { + url = Debug.WebsocketSink.Url + }; + + Debug.Console(0, "Debug Session URL: {0}", url); + + // Return the port number with the full url of the WS Server + var res = JsonConvert.SerializeObject(data); + + context.Response.ContentType = "application/json"; + context.Response.ContentEncoding = Encoding.UTF8; + context.Response.StatusCode = 200; + context.Response.StatusDescription = "OK"; + context.Response.Write(res, false); + context.Response.End(); + } + catch (Exception e) + { + Debug.Console(0, "Error: {0}", e); + } + } + + /// + /// Stops a debug session + /// + /// + protected override void HandlePost(HttpCwsContext context) + { + Debug.WebsocketSink.StopServer(); + + context.Response.StatusCode = 200; + context.Response.StatusDescription = "OK"; + context.Response.End(); + + Debug.Console(0, "Websocket Debug Session Stopped"); + } + + } +} diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DevMethodsRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DevMethodsRequestHandler.cs new file mode 100644 index 00000000..c08544cb --- /dev/null +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DevMethodsRequestHandler.cs @@ -0,0 +1,76 @@ +using System.Text; +using Crestron.SimplSharp.WebScripting; +using Newtonsoft.Json; +using PepperDash.Core.Web.RequestHandlers; + +namespace PepperDash.Essentials.Core.Web.RequestHandlers +{ + public class DevMethodsRequestHandler : WebApiBaseRequestHandler + { + /// + /// Constructor + /// + /// + /// base(true) enables CORS support by default + /// + public DevMethodsRequestHandler() + : base(true) + { + } + + /// + /// Handles GET method requests + /// + /// + protected override void HandleGet(HttpCwsContext context) + { + var routeData = context.Request.RouteData; + if (routeData == null) + { + context.Response.StatusCode = 400; + context.Response.StatusDescription = "Bad Request"; + context.Response.End(); + + return; + } + + object deviceObj; + if (!routeData.Values.TryGetValue("deviceKey", out deviceObj)) + { + context.Response.StatusCode = 400; + context.Response.StatusDescription = "Bad Request"; + context.Response.End(); + + return; + } + + var device = DeviceManager.GetDeviceForKey(deviceObj.ToString()); + + if (device == null) + { + context.Response.StatusCode = 404; + context.Response.StatusDescription = "Device Not Found"; + context.Response.End(); + + return; + } + + var deviceMethods = DeviceJsonApi.GetMethods(device.Key); + if (deviceMethods == null || deviceMethods.ToLower().Contains("no device")) + { + context.Response.StatusCode = 404; + context.Response.StatusDescription = "Not Found"; + context.Response.End(); + + return; + } + + context.Response.StatusCode = 200; + context.Response.StatusDescription = "OK"; + context.Response.ContentType = "application/json"; + context.Response.ContentEncoding = Encoding.UTF8; + context.Response.Write(deviceMethods, false); + context.Response.End(); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DevPropsRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DevPropsRequestHandler.cs index 25fadbce..c00e47c2 100644 --- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DevPropsRequestHandler.cs +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DevPropsRequestHandler.cs @@ -18,59 +18,59 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers { } - /// - /// Handles POST method requests - /// - /// - protected override void HandlePost(HttpCwsContext context) - { - if (context.Request.ContentLength < 0) - { - context.Response.StatusCode = 400; - context.Response.StatusDescription = "Bad Request"; - context.Response.End(); + /// + /// Handles GET method requests + /// + /// + protected override void HandleGet(HttpCwsContext context) + { + var routeData = context.Request.RouteData; + if (routeData == null) + { + context.Response.StatusCode = 400; + context.Response.StatusDescription = "Bad Request"; + context.Response.End(); - return; - } + return; + } - var data = EssentialsWebApiHelpers.GetRequestBody(context.Request); - if (string.IsNullOrEmpty(data)) - { - context.Response.StatusCode = 400; - context.Response.StatusDescription = "Bad Request"; - context.Response.End(); + object deviceObj; + if (!routeData.Values.TryGetValue("deviceKey", out deviceObj)) + { + context.Response.StatusCode = 400; + context.Response.StatusDescription = "Bad Request"; + context.Response.End(); - return; - } + return; + } - var o = new DeviceActionWrapper(); - var body = JsonConvert.DeserializeAnonymousType(data, o); - - if (string.IsNullOrEmpty(body.DeviceKey)) - { - context.Response.StatusCode = 400; - context.Response.StatusDescription = "Bad Request"; - context.Response.End(); + var device = DeviceManager.GetDeviceForKey(deviceObj.ToString()); - return; - } + if (device == null) + { + context.Response.StatusCode = 404; + context.Response.StatusDescription = "Device Not Found"; + context.Response.End(); - var deviceProps = DeviceJsonApi.GetProperties(body.DeviceKey); - if (deviceProps == null || deviceProps.ToLower().Contains("no device")) - { - context.Response.StatusCode = 404; - context.Response.StatusDescription = "Not Found"; - context.Response.End(); + return; + } - return; - } + var deviceProperties = DeviceJsonApi.GetProperties(device.Key); + if (deviceProperties == null || deviceProperties.ToLower().Contains("no device")) + { + context.Response.StatusCode = 404; + context.Response.StatusDescription = "Not Found"; + context.Response.End(); - context.Response.StatusCode = 200; - context.Response.StatusDescription = "OK"; - context.Response.ContentType = "application/json"; - context.Response.ContentEncoding = Encoding.UTF8; - context.Response.Write(deviceProps, false); - context.Response.End(); - } - } + return; + } + + context.Response.StatusCode = 200; + context.Response.StatusDescription = "OK"; + context.Response.ContentType = "application/json"; + context.Response.ContentEncoding = Encoding.UTF8; + context.Response.Write(deviceProperties, false); + context.Response.End(); + } + } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DoNotLoadConfigOnNextBootRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DoNotLoadConfigOnNextBootRequestHandler.cs new file mode 100644 index 00000000..4ec3bc97 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DoNotLoadConfigOnNextBootRequestHandler.cs @@ -0,0 +1,84 @@ +using Crestron.SimplSharp.WebScripting; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Core.Web.RequestHandlers; + +namespace PepperDash.Essentials.Core.Web.RequestHandlers +{ + public class DoNotLoadConfigOnNextBootRequestHandler : WebApiBaseRequestHandler + { + /// + /// Constructor + /// + /// + /// base(true) enables CORS support by default + /// + public DoNotLoadConfigOnNextBootRequestHandler() + : base(true) + { + } + + /// + /// Handles GET method requests + /// + /// + protected override void HandleGet(HttpCwsContext context) + { + var data = new Data + { + DoNotLoadConfigOnNextBoot = Debug.DoNotLoadConfigOnNextBoot + }; + + var body = JsonConvert.SerializeObject(data, Formatting.Indented); + + context.Response.StatusCode = 200; + context.Response.StatusDescription = "OK"; + context.Response.Write(body, false); + context.Response.End(); + } + + /// + /// Handles POST method requests + /// + /// + protected override void HandlePost(HttpCwsContext context) + { + if (context.Request.ContentLength < 0) + { + context.Response.StatusCode = 400; + context.Response.StatusDescription = "Bad Request"; + context.Response.End(); + + return; + } + + var data = EssentialsWebApiHelpers.GetRequestBody(context.Request); + if (string.IsNullOrEmpty(data)) + { + context.Response.StatusCode = 400; + context.Response.StatusDescription = "Bad Request"; + context.Response.End(); + + return; + } + + var d = new Data(); + var requestBody = JsonConvert.DeserializeAnonymousType(data, d); + + Debug.SetDoNotLoadConfigOnNextBoot(requestBody.DoNotLoadConfigOnNextBoot); + + var responseBody = JsonConvert.SerializeObject(d, Formatting.Indented); + + context.Response.StatusCode = 200; + context.Response.StatusDescription = "OK"; + context.Response.Write(responseBody, false); + context.Response.End(); + } + } + + public class Data + { + [JsonProperty("doNotLoadConfigOnNextBoot", NullValueHandling = NullValueHandling.Ignore)] + public bool DoNotLoadConfigOnNextBoot { get; set; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesByFilterRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesByFilterRequestHandler.cs index 105d2419..637c533c 100644 --- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesByFilterRequestHandler.cs +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesByFilterRequestHandler.cs @@ -1,8 +1,6 @@ -extern alias Full - -using System.Linq; +using System.Linq; using Crestron.SimplSharp.WebScripting; -using Full.Newtonsoft.Json; +using Newtonsoft.Json; using PepperDash.Core.Web.RequestHandlers; namespace PepperDash.Essentials.Core.Web.RequestHandlers diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesRequestHandler.cs index a72fe7e4..564cb00d 100644 --- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesRequestHandler.cs +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/GetTypesRequestHandler.cs @@ -1,8 +1,6 @@ -extern alias Full - -using System.Linq; +using System.Linq; using Crestron.SimplSharp.WebScripting; -using Full.Newtonsoft.Json; +using Newtonsoft.Json; using PepperDash.Core.Web.RequestHandlers; namespace PepperDash.Essentials.Core.Web.RequestHandlers diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/LoadConfigRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/LoadConfigRequestHandler.cs new file mode 100644 index 00000000..61932f30 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/LoadConfigRequestHandler.cs @@ -0,0 +1,39 @@ +using Crestron.SimplSharp; +using Crestron.SimplSharp.WebScripting; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Core.Web.RequestHandlers; + +namespace PepperDash.Essentials.Core.Web.RequestHandlers +{ + public class LoadConfigRequestHandler : WebApiBaseRequestHandler + { + /// + /// Constructor + /// + /// + /// base(true) enables CORS support by default + /// + public LoadConfigRequestHandler() + : base(true) + { + } + + /// + /// Handles POST method requests + /// + /// + protected override void HandlePost(HttpCwsContext context) + { + var message = ""; + var cs = Global.ControlSystem as ILoadConfig; + if(cs != null) + cs.GoWithLoad(); + + context.Response.StatusCode = 200; + context.Response.StatusDescription = "OK"; + context.Response.Write(message, false); + context.Response.End(); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/ReportVersionsRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/ReportVersionsRequestHandler.cs index 925a95f0..3447a1eb 100644 --- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/ReportVersionsRequestHandler.cs +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/ReportVersionsRequestHandler.cs @@ -1,8 +1,6 @@ -extern alias Full - -using System.Linq; +using System.Linq; using Crestron.SimplSharp.WebScripting; -using Full.Newtonsoft.Json; +using Newtonsoft.Json; using PepperDash.Core.Web.RequestHandlers; namespace PepperDash.Essentials.Core.Web.RequestHandlers diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/RestartProgramRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/RestartProgramRequestHandler.cs new file mode 100644 index 00000000..0bb568f6 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/RestartProgramRequestHandler.cs @@ -0,0 +1,38 @@ +using Crestron.SimplSharp; +using Crestron.SimplSharp.WebScripting; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Core.Web.RequestHandlers; + +namespace PepperDash.Essentials.Core.Web.RequestHandlers +{ + public class RestartProgramRequestHandler : WebApiBaseRequestHandler + { + /// + /// Constructor + /// + /// + /// base(true) enables CORS support by default + /// + public RestartProgramRequestHandler() + : base(true) + { + } + + /// + /// Handles POST method requests + /// + /// + protected override void HandlePost(HttpCwsContext context) + { + var message = ""; + if(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance) + CrestronConsole.SendControlSystemCommand($"progres -p:{InitialParametersClass.ApplicationNumber}", ref message); + + context.Response.StatusCode = 200; + context.Response.StatusDescription = "OK"; + context.Response.Write(message, false); + context.Response.End(); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/ShowConfigRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/ShowConfigRequestHandler.cs index b017627d..65af1d06 100644 --- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/ShowConfigRequestHandler.cs +++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/ShowConfigRequestHandler.cs @@ -1,7 +1,5 @@ -extern alias Full - -using Crestron.SimplSharp.WebScripting; -using Full.Newtonsoft.Json; +using Crestron.SimplSharp.WebScripting; +using Newtonsoft.Json; using PepperDash.Core.Web.RequestHandlers; using PepperDash.Essentials.Core.Config; diff --git a/src/PepperDash.Essentials.Devices.Common/Lighting/LightingBase.cs b/src/PepperDash.Essentials.Devices.Common/Lighting/LightingBase.cs new file mode 100644 index 00000000..12b027b3 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Lighting/LightingBase.cs @@ -0,0 +1,142 @@ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Lighting; + +namespace PepperDash.Essentials.Devices.Common.Lighting +{ + [Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")] + public abstract class LightingBase : EssentialsBridgeableDevice, ILightingScenes + { + #region ILightingScenes Members + + public event EventHandler LightingSceneChange; + + public List LightingScenes { get; protected set; } + + public LightingScene CurrentLightingScene { get; protected set; } + + public IntFeedback CurrentLightingSceneFeedback { get; protected set; } + + #endregion + + protected LightingBase(string key, string name) + : base(key, name) + { + LightingScenes = new List(); + + CurrentLightingScene = new LightingScene(); + //CurrentLightingSceneFeedback = new IntFeedback(() => { return int.Parse(this.CurrentLightingScene.ID); }); + } + + public abstract void SelectScene(LightingScene scene); + + public void SimulateSceneSelect(string sceneName) + { + Debug.Console(1, this, "Simulating selection of scene '{0}'", sceneName); + + var scene = LightingScenes.FirstOrDefault(s => s.Name.Equals(sceneName)); + + if (scene != null) + { + CurrentLightingScene = scene; + OnLightingSceneChange(); + } + } + + /// + /// Sets the IsActive property on each scene and fires the LightingSceneChange event + /// + protected void OnLightingSceneChange() + { + foreach (var scene in LightingScenes) + { + if (scene == CurrentLightingScene) + scene.IsActive = true; + + else + scene.IsActive = false; + } + + var handler = LightingSceneChange; + if (handler != null) + { + handler(this, new LightingSceneChangeEventArgs(CurrentLightingScene)); + } + } + + protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, uint joinStart, + string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new GenericLightingJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + } + + return LinkLightingToApi(lightingDevice, trilist, joinMap); + } + + protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, GenericLightingJoinMap joinMap) + { + Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + + Debug.Console(0, "Linking to Lighting Type {0}", lightingDevice.GetType().Name.ToString()); + + // GenericLighitng Actions & FeedBack + trilist.SetUShortSigAction(joinMap.SelectScene.JoinNumber, u => lightingDevice.SelectScene(lightingDevice.LightingScenes[u])); + + var sceneIndex = 0; + foreach (var scene in lightingDevice.LightingScenes) + { + var index = sceneIndex; + + trilist.SetSigTrueAction((uint)(joinMap.SelectSceneDirect.JoinNumber + index), () => lightingDevice.SelectScene(lightingDevice.LightingScenes[index])); + scene.IsActiveFeedback.LinkInputSig(trilist.BooleanInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)]); + trilist.StringInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)].StringValue = scene.Name; + trilist.BooleanInput[(uint)(joinMap.ButtonVisibility.JoinNumber + index)].BoolValue = true; + + sceneIndex++; + } + + trilist.OnlineStatusChange += (sender, args) => + { + if (!args.DeviceOnLine) return; + + sceneIndex = 0; + foreach (var scene in lightingDevice.LightingScenes) + { + var index = sceneIndex; + + trilist.StringInput[(uint) (joinMap.SelectSceneDirect.JoinNumber + index)].StringValue = scene.Name; + trilist.BooleanInput[(uint) (joinMap.ButtonVisibility.JoinNumber + index)].BoolValue = true; + scene.IsActiveFeedback.FireUpdate(); + + sceneIndex++; + } + }; + + return joinMap; + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj b/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj index 1378b836..12370935 100644 --- a/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj +++ b/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj @@ -27,6 +27,6 @@ - + \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsHuddleSpaceRoom.cs b/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsHuddleSpaceRoom.cs new file mode 100644 index 00000000..1af09f1c --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsHuddleSpaceRoom.cs @@ -0,0 +1,15 @@ +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials.Devices.Common.Room +{ + public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IHasDefaultDisplay, IHasCurrentVolumeControls, IRoomOccupancy, + IEmergency, IMicrophonePrivacy + { + bool ExcludeFromGlobalFunctions { get; } + + void RunRouteAction(string routeKey); + + EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsHuddleVtc1Room.cs b/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsHuddleVtc1Room.cs new file mode 100644 index 00000000..b7f1a619 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsHuddleVtc1Room.cs @@ -0,0 +1,26 @@ +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.AudioCodec; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials.Devices.Common.Room +{ + public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback, + IRoomOccupancy, IEmergency, IMicrophonePrivacy + { + EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; } + + bool ExcludeFromGlobalFunctions { get; } + + void RunRouteAction(string routeKey); + + IHasScheduleAwareness ScheduleSource { get; } + + new BoolFeedback InCallFeedback { get; } + + new BoolFeedback PrivacyModeIsOnFeedback { get; } + + string DefaultCodecRouteString { get; } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsTechRoom.cs b/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsTechRoom.cs new file mode 100644 index 00000000..947be5d9 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Room/IEssentialsTechRoom.cs @@ -0,0 +1,22 @@ +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Room.Config; +using System.Collections.Generic; +using TwoWayDisplayBase = PepperDash.Essentials.Devices.Common.Displays.TwoWayDisplayBase; + + +namespace PepperDash.Essentials.Devices.Common.Room +{ + public interface IEssentialsTechRoom:IEssentialsRoom, ITvPresetsProvider,IBridgeAdvanced,IRunDirectRouteAction + { + EssentialsTechRoomConfig PropertiesConfig { get; } + Dictionary Tuners { get; } + + Dictionary Displays { get; } + + void RoomPowerOn(); + + void RoomPowerOff(); + } +} diff --git a/src/PepperDash.Essentials/ControlSystem.cs b/src/PepperDash.Essentials/ControlSystem.cs index a98e25b2..34bd30d9 100644 --- a/src/PepperDash.Essentials/ControlSystem.cs +++ b/src/PepperDash.Essentials/ControlSystem.cs @@ -10,12 +10,15 @@ using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Web; +using PepperDash.Essentials.Devices.Common.Room; +using PepperDash.Essentials.Room.Config; using System; using System.Linq; namespace PepperDash.Essentials { - public class ControlSystem : CrestronControlSystem + public class ControlSystem : CrestronControlSystem, ILoadConfig { HttpLogoServer LogoServer; @@ -87,7 +90,7 @@ namespace PepperDash.Essentials { DeterminePlatform(); - if (Debug.DoNotLoadOnNextBoot) + if (Debug.DoNotLoadConfigOnNextBoot) { CrestronConsole.AddNewConsoleCommand(s => CrestronInvoke.BeginInvoke((o) => GoWithLoad()), "go", "Loads configuration file", ConsoleAccessLevelEnum.AccessOperator); @@ -135,7 +138,9 @@ namespace PepperDash.Essentials CrestronConsole.AddNewConsoleCommand(DeviceManager.GetRoutingPorts, "getroutingports", "Reports all routing ports, if any. Requires a device key", ConsoleAccessLevelEnum.AccessOperator); - if (!Debug.DoNotLoadOnNextBoot) + DeviceManager.AddDevice(new EssentialsWebApi("essentialsWebApi", "Essentials Web API")); + + if (!Debug.DoNotLoadConfigOnNextBoot) { GoWithLoad(); return; @@ -170,6 +175,8 @@ namespace PepperDash.Essentials Global.SetAssemblyVersion(fullVersion); + //Global.SetAssemblyVersion(fullVersionAtt.InformationalVersion); + if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) // Handles 3-series running Windows CE OS { string userFolder; @@ -217,7 +224,7 @@ namespace PepperDash.Essentials } else // Handles Linux OS (Virtual Control) { - Debug.SetDebugLevel(2); + //Debug.SetDebugLevel(2); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on Virtual Control Server", Global.AssemblyVersion); @@ -240,7 +247,7 @@ namespace PepperDash.Essentials { try { - Debug.SetDoNotLoadOnNextBoot(false); + Debug.SetDoNotLoadConfigOnNextBoot(false); PluginLoader.AddProgramAssemblies(); @@ -305,6 +312,8 @@ namespace PepperDash.Essentials { Debug.Console(0, "Verifying and/or creating folder structure"); var configDir = Global.FilePathPrefix; + + Debug.Console(0, "FilePathPrefix: {0}", configDir); var configExists = Directory.Exists(configDir); if (!configExists) Directory.Create(configDir); @@ -373,7 +382,6 @@ namespace PepperDash.Essentials // Build the processor wrapper class DeviceManager.AddDevice(new PepperDash.Essentials.Core.Devices.CrestronProcessor("processor")); - // DeviceManager.AddDevice(new EssemtialsWebApi("essentialsWebApi","Essentials Web API")); // Add global System Monitor device if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance) @@ -402,51 +410,6 @@ namespace PepperDash.Essentials "WARNING: Config file defines processor type as '{0}' but actual processor is '{1}'! Some ports may not be available", devConf.Type.ToUpper(), Global.ControlSystem.ControllerPrompt.ToUpper()); - //if (newDev == null) - // newDev = PepperDash.Essentials.Core.ProcessorExtensionDeviceFactory.GetExtensionDevice(devConf); - - //if (newDev != null) - //{ - // DeviceManager.AddDevice(newDev); - - // continue; - //} - - // Check if the processor is a DMPS model - //if (this.ControllerPrompt.IndexOf("dmps", StringComparison.OrdinalIgnoreCase) > -1) - //{ - // Debug.Console(2, "Adding DmpsRoutingController for {0} to Device Manager.", this.ControllerPrompt); - - // var propertiesConfig = JsonConvert.DeserializeObject(devConf.Properties.ToString()); - - // if(propertiesConfig == null) - // propertiesConfig = new DM.Config.DmpsRoutingPropertiesConfig(); - - // DeviceManager.AddDevice(DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", this.ControllerPrompt, propertiesConfig)); - //} - //else - - //if (this.ControllerPrompt.IndexOf("mpc3", StringComparison.OrdinalIgnoreCase) > -1) - //{ - // Debug.Console(2, "MPC3 processor type detected. Adding Mpc3TouchpanelController."); - - // var butToken = devConf.Properties["buttons"]; - // if (butToken != null) - // { - // var buttons = butToken.ToObject>(); - // var tpController = new Essentials.Core.Touchpanels.Mpc3TouchpanelController(devConf.Key, devConf.Name, Global.ControlSystem, buttons); - // DeviceManager.AddDevice(tpController); - // } - // else - // { - // Debug.Console(0, Debug.ErrorLogLevel.Error, "Error: Unable to deserialize buttons collection for device: {0}", devConf.Key); - // } - - //} - //else - //{ - // Debug.Console(2, "************Processor is not DMPS type***************"); - //} continue; } @@ -507,103 +470,48 @@ namespace PepperDash.Essentials return; } - // uint fusionIpId = 0xf1; - foreach (var roomConfig in ConfigReader.ConfigObject.Rooms) { - /* - var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as IEssentialsRoom; - if (room != null) - { - // default to no join map key - string fusionJoinMapKey = string.Empty; + var room = Core.DeviceFactory.GetDevice(roomConfig); - if (room.Config.Properties["fusion"] != null) - { - Debug.Console(2, "Custom Fusion config found. Using custom values"); + DeviceManager.AddDevice(room); + if (room is ICustomMobileControl) + { + continue; + } - var fusionConfig = room.Config.Properties["fusion"].ToObject(); - - if (fusionConfig != null) - { - fusionIpId = fusionConfig.IpIdInt; - fusionJoinMapKey = fusionConfig.JoinMapKey; - } - } - - AddRoomAndBuildMC(room); - - if (room is IEssentialsHuddleSpaceRoom) - { - - 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)); - - } - else if (room is IEssentialsHuddleVtc1Room) - { - - if (!(room is EssentialsCombinedHuddleVtc1Room)) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId); - DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((IEssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey)); - } - - } - else if (room is EssentialsTechRoom) - { - - Debug.Console(0, Debug.ErrorLogLevel.Notice, - "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId); - DeviceManager.AddDevice(new EssentialsTechRoomFusionSystemController((EssentialsTechRoom)room, fusionIpId, fusionJoinMapKey)); - - } - 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); - - } - */ + BuildMC(room as IEssentialsRoom); } Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded."); } - private static void AddRoomAndBuildMC(IEssentialsRoom room) - { - DeviceManager.AddDevice(room); - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge"); + private static void BuildMC(IEssentialsRoom room) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, $"Attempting to build Mobile Control Bridge for {room?.Key}"); CreateMobileControlBridge(room); } - private static void CreateMobileControlBridge(object room) + private static void CreateMobileControlBridge(IEssentialsRoom room) { + if(room == null) + { + Debug.Console(0, Debug.ErrorLogLevel.Warning, $"Room does not implement IEssentialsRoom"); + return; + } + var mobileControl = GetMobileControlDevice(); - if (mobileControl == null) return; - - var mobileControl3 = mobileControl as IMobileControl3; - - if (mobileControl3 != null) - { - mobileControl3.CreateMobileControlRoomBridge(room as IEssentialsRoom, mobileControl); - } - else - { - mobileControl.CreateMobileControlRoomBridge(room as EssentialsRoomBase, mobileControl); - } + mobileControl?.CreateMobileControlRoomBridge(room, mobileControl); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Mobile Control Bridge Added..."); } - private static IMobileControl GetMobileControlDevice() + private static IMobileControl3 GetMobileControlDevice() { - var mobileControlList = DeviceManager.AllDevices.OfType().ToList(); + var mobileControlList = DeviceManager.AllDevices.OfType().ToList(); if (mobileControlList.Count > 1) { diff --git a/src/PepperDash.Essentials/PepperDash.Essentials.csproj b/src/PepperDash.Essentials/PepperDash.Essentials.csproj index 663a5f84..f8e6108f 100644 --- a/src/PepperDash.Essentials/PepperDash.Essentials.csproj +++ b/src/PepperDash.Essentials/PepperDash.Essentials.csproj @@ -11,6 +11,7 @@ bin\$(Configuration)\ PepperDash Essentials PepperDashEssentials + $(Version) full @@ -46,7 +47,7 @@ - +