From d2e1979d9627e87462069d4597e5cd276378433f Mon Sep 17 00:00:00 2001 From: Nick Genovese Date: Mon, 6 Nov 2023 15:38:48 -0500 Subject: [PATCH] feat(wip): removing base classes from Essentials.Core --- .../Crestron/CrestronGenericBaseDevice.cs | 1 - .../Devices/PC/Laptop.cs | 1 + .../Display/BasicIrDisplay.cs | 1 + .../Display/DisplayBase.cs | 2 +- .../Shades/Shade Interfaces.cs | 2 +- .../Shades/ShadeController.cs | 4 +- .../Displays/BasicIrDisplay.cs | 224 +++++++++++++ .../Displays/DisplayBase.cs | 315 ++++++++++++++++++ .../{Display => Displays}/InputInterfaces.cs | 0 .../Displays/MockDisplay.cs | 211 ++++++++++++ .../Shades/ShadeBase.cs | 22 ++ .../SoftCodec/BlueJeansPc.cs | 8 +- .../Sources/InRoomPc.cs | 76 +++++ .../Sources/Laptop.cs | 75 +++++ .../EssentialsHuddleVtc1FusionController.cs | 3 +- .../EssentialsHuddleTechPageDriver.cs | 3 + 16 files changed, 937 insertions(+), 11 deletions(-) create mode 100644 src/PepperDash.Essentials.Devices.Common/Displays/BasicIrDisplay.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs rename src/PepperDash.Essentials.Devices.Common/{Display => Displays}/InputInterfaces.cs (100%) create mode 100644 src/PepperDash.Essentials.Devices.Common/Displays/MockDisplay.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Shades/ShadeBase.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs create mode 100644 src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs diff --git a/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs b/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs index f9aa5df7..9d8ebe3d 100644 --- a/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs +++ b/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs @@ -8,7 +8,6 @@ using PepperDash.Essentials.Core.Bridges; namespace PepperDash.Essentials.Core { - [Obsolete("Please use PepperDash.Essentials.Devices.Common")] public abstract class CrestronGenericBaseDevice : EssentialsDevice, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking { protected GenericBase Hardware; diff --git a/src/PepperDash.Essentials.Core/Devices/PC/Laptop.cs b/src/PepperDash.Essentials.Core/Devices/PC/Laptop.cs index 809a0d5c..d392edac 100644 --- a/src/PepperDash.Essentials.Core/Devices/PC/Laptop.cs +++ b/src/PepperDash.Essentials.Core/Devices/PC/Laptop.cs @@ -64,6 +64,7 @@ namespace PepperDash.Essentials.Core.Devices #endregion } + [Obsolete("Please use PepperDash.Essentials.Devices.Common")] public class LaptopFactory : EssentialsDeviceFactory { public LaptopFactory() diff --git a/src/PepperDash.Essentials.Core/Display/BasicIrDisplay.cs b/src/PepperDash.Essentials.Core/Display/BasicIrDisplay.cs index c2e31928..8d0afdcc 100644 --- a/src/PepperDash.Essentials.Core/Display/BasicIrDisplay.cs +++ b/src/PepperDash.Essentials.Core/Display/BasicIrDisplay.cs @@ -203,6 +203,7 @@ namespace PepperDash.Essentials.Core } } + [Obsolete("Please use PepperDash.Essentials.Device.Common")] public class BasicIrDisplayFactory : EssentialsDeviceFactory { public BasicIrDisplayFactory() diff --git a/src/PepperDash.Essentials.Core/Display/DisplayBase.cs b/src/PepperDash.Essentials.Core/Display/DisplayBase.cs index b883cca4..0bb172ca 100644 --- a/src/PepperDash.Essentials.Core/Display/DisplayBase.cs +++ b/src/PepperDash.Essentials.Core/Display/DisplayBase.cs @@ -13,7 +13,7 @@ using PepperDash.Essentials.Core.Bridges; namespace PepperDash.Essentials.Core { [Obsolete("Please use PepperDash.Essentials.Devices.Common")] - public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking, IPower + public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking { public event SourceInfoChangeHandler CurrentSourceChange; diff --git a/src/PepperDash.Essentials.Core/Shades/Shade Interfaces.cs b/src/PepperDash.Essentials.Core/Shades/Shade Interfaces.cs index 94a88c36..93fcbc52 100644 --- a/src/PepperDash.Essentials.Core/Shades/Shade Interfaces.cs +++ b/src/PepperDash.Essentials.Core/Shades/Shade Interfaces.cs @@ -8,7 +8,7 @@ namespace PepperDash.Essentials.Core.Shades /// public interface IShades { - List Shades { get; } + List Shades { get; } } /// diff --git a/src/PepperDash.Essentials.Core/Shades/ShadeController.cs b/src/PepperDash.Essentials.Core/Shades/ShadeController.cs index fc50f631..6dff9803 100644 --- a/src/PepperDash.Essentials.Core/Shades/ShadeController.cs +++ b/src/PepperDash.Essentials.Core/Shades/ShadeController.cs @@ -16,14 +16,14 @@ namespace PepperDash.Essentials.Core.Shades { ShadeControllerConfigProperties Config; - public List Shades { get; private set; } + public List Shades { get; private set; } public ShadeController(string key, string name, ShadeControllerConfigProperties config) : base(key, name) { Config = config; - Shades = new List(); + Shades = new List(); } public override bool CustomActivate() diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/BasicIrDisplay.cs b/src/PepperDash.Essentials.Devices.Common/Displays/BasicIrDisplay.cs new file mode 100644 index 00000000..59e67278 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Displays/BasicIrDisplay.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common.Displays +{ + public class BasicIrDisplay : DisplayBase, IBasicVolumeControls, IBridgeAdvanced + { + public IrOutputPortController IrPort { get; private set; } + public ushort IrPulseTime { get; set; } + + protected Func PowerIsOnFeedbackFunc + { + get { return () => _PowerIsOn; } + } + protected override Func IsCoolingDownFeedbackFunc + { + get { return () => _IsCoolingDown; } + } + protected override Func IsWarmingUpFeedbackFunc + { + get { return () => _IsWarmingUp; } + } + + bool _PowerIsOn; + bool _IsWarmingUp; + bool _IsCoolingDown; + + public BasicIrDisplay(string key, string name, IROutputPort port, string irDriverFilepath) + : base(key, name) + { + IrPort = new IrOutputPortController(key + "-ir", port, irDriverFilepath); + DeviceManager.AddDevice(IrPort); + + IsWarmingUpFeedback.OutputChange += (o, a) => Debug.Console(2, this, "Warming up={0}", _IsWarmingUp); + IsCoolingDownFeedback.OutputChange += (o, a) => Debug.Console(2, this, "Cooling down={0}", _IsCoolingDown); + + InputPorts.AddRange(new RoutingPortCollection + { + new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(Hdmi1), this, false), + new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(Hdmi2), this, false), + new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(Hdmi3), this, false), + new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(Hdmi4), this, false), + new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(Component1), this, false), + new RoutingInputPort(RoutingPortNames.CompositeIn, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(Video1), this, false), + new RoutingInputPort(RoutingPortNames.AntennaIn, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(Antenna), this, false), + }); + } + + public void Hdmi1() + { + IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_1, IrPulseTime); + } + + public void Hdmi2() + { + IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_2, IrPulseTime); + } + + public void Hdmi3() + { + IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_3, IrPulseTime); + } + + public void Hdmi4() + { + IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_4, IrPulseTime); + } + + public void Component1() + { + IrPort.Pulse(IROutputStandardCommands.IROut_COMPONENT_1, IrPulseTime); + } + + public void Video1() + { + IrPort.Pulse(IROutputStandardCommands.IROut_VIDEO_1, IrPulseTime); + } + + public void Antenna() + { + IrPort.Pulse(IROutputStandardCommands.IROut_ANTENNA, IrPulseTime); + } + + #region IPower Members + + public override void PowerOn() + { + IrPort.Pulse(IROutputStandardCommands.IROut_POWER_ON, IrPulseTime); + _PowerIsOn = true; + } + + public override void PowerOff() + { + _PowerIsOn = false; + IrPort.Pulse(IROutputStandardCommands.IROut_POWER_OFF, IrPulseTime); + } + + public override void PowerToggle() + { + _PowerIsOn = false; + IrPort.Pulse(IROutputStandardCommands.IROut_POWER, IrPulseTime); + } + + #endregion + + #region IBasicVolumeControls Members + + public void VolumeUp(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_VOL_PLUS, pressRelease); + } + + public void VolumeDown(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_VOL_MINUS, pressRelease); + } + + public void MuteToggle() + { + IrPort.Pulse(IROutputStandardCommands.IROut_MUTE, 200); + } + + #endregion + + void StartWarmingTimer() + { + _IsWarmingUp = true; + IsWarmingUpFeedback.FireUpdate(); + new CTimer(o => { + _IsWarmingUp = false; + IsWarmingUpFeedback.FireUpdate(); + }, 10000); + } + + void StartCoolingTimer() + { + _IsCoolingDown = true; + IsCoolingDownFeedback.FireUpdate(); + new CTimer(o => + { + _IsCoolingDown = false; + IsCoolingDownFeedback.FireUpdate(); + }, 7000); + } + + #region IRoutingSink Members + + /// + /// Typically called by the discovery routing algorithm. + /// + /// A delegate containing the input selector method to call + public override void ExecuteSwitch(object inputSelector) + { + Debug.Console(2, this, "Switching to input '{0}'", (inputSelector as Action).ToString()); + + Action finishSwitch = () => + { + var action = inputSelector as Action; + if (action != null) + action(); + }; + + if (!_PowerIsOn) + { + PowerOn(); + EventHandler oneTimer = null; + oneTimer = (o, a) => + { + if (IsWarmingUpFeedback.BoolValue) return; // Only catch done warming + IsWarmingUpFeedback.OutputChange -= oneTimer; + finishSwitch(); + }; + IsWarmingUpFeedback.OutputChange += oneTimer; + } + else // Do it! + finishSwitch(); + } + + #endregion + + public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge); + } + } + + public class BasicIrDisplayFactory : EssentialsDeviceFactory + { + public BasicIrDisplayFactory() + { + TypeNames = new List() { "basicirdisplay" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new BasicIrDisplay Device"); + var ir = IRPortHelper.GetIrPort(dc.Properties); + if (ir != null) + { + var display = new BasicIrDisplay(dc.Key, dc.Name, ir.Port, ir.FileName); + display.IrPulseTime = 200; // Set default pulse time for IR commands. + return display; + } + + return null; + } + } + +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs b/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs new file mode 100644 index 00000000..d93d57ff --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Displays/DisplayBase.cs @@ -0,0 +1,315 @@ +extern alias Full; +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using Feedback = PepperDash.Essentials.Core.Feedback; +using JsonConvert = Full::Newtonsoft.Json.JsonConvert; + + +namespace PepperDash.Essentials.Devices.Common.Displays +{ + public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking + { + public event SourceInfoChangeHandler CurrentSourceChange; + + public string CurrentSourceInfoKey { get; set; } + public SourceListItem CurrentSourceInfo + { + get + { + return _CurrentSourceInfo; + } + set + { + if (value == _CurrentSourceInfo) return; + + var handler = CurrentSourceChange; + + if (handler != null) + handler(_CurrentSourceInfo, ChangeType.WillChange); + + _CurrentSourceInfo = value; + + if (handler != null) + handler(_CurrentSourceInfo, ChangeType.DidChange); + } + } + SourceListItem _CurrentSourceInfo; + + public BoolFeedback IsCoolingDownFeedback { get; protected set; } + public BoolFeedback IsWarmingUpFeedback { get; private set; } + + public UsageTracking UsageTracker { get; set; } + + public uint WarmupTime { get; set; } + public uint CooldownTime { get; set; } + + /// + /// Bool Func that will provide a value for the PowerIsOn Output. Must be implemented + /// by concrete sub-classes + /// + abstract protected Func IsCoolingDownFeedbackFunc { get; } + abstract protected Func IsWarmingUpFeedbackFunc { get; } + + + protected CTimer WarmupTimer; + protected CTimer CooldownTimer; + + #region IRoutingInputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + #endregion + + protected DisplayBase(string key, string name) + : base(key, name) + { + IsCoolingDownFeedback = new BoolFeedback("IsCoolingDown", IsCoolingDownFeedbackFunc); + IsWarmingUpFeedback = new BoolFeedback("IsWarmingUp", IsWarmingUpFeedbackFunc); + + InputPorts = new RoutingPortCollection(); + + } + + public abstract void PowerOn(); + public abstract void PowerOff(); + public abstract void PowerToggle(); + + public virtual FeedbackCollection Feedbacks + { + get + { + return new FeedbackCollection + { + IsCoolingDownFeedback, + IsWarmingUpFeedback + }; + } + } + + public abstract void ExecuteSwitch(object selector); + + protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey, + EiscApiAdvanced bridge) + { + var joinMap = new DisplayControllerJoinMap(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."); + } + + LinkDisplayToApi(displayDevice, trilist, joinMap); + } + + protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, DisplayControllerJoinMap joinMap) + { + Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + Debug.Console(0, "Linking to Display: {0}", displayDevice.Name); + + trilist.StringInput[joinMap.Name.JoinNumber].StringValue = displayDevice.Name; + + var commMonitor = displayDevice as ICommunicationMonitor; + if (commMonitor != null) + { + commMonitor.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + } + + var inputNumber = 0; + var inputKeys = new List(); + + var inputNumberFeedback = new IntFeedback(() => inputNumber); + + // Two way feedbacks + var twoWayDisplay = displayDevice as TwoWayDisplayBase; + + if (twoWayDisplay != null) + { + trilist.SetBool(joinMap.IsTwoWayDisplay.JoinNumber, true); + + twoWayDisplay.CurrentInputFeedback.OutputChange += (o, a) => Debug.Console(0, "CurrentInputFeedback_OutputChange {0}", a.StringValue); + + + inputNumberFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputSelect.JoinNumber]); + } + + // Power Off + trilist.SetSigTrueAction(joinMap.PowerOff.JoinNumber, () => + { + inputNumber = 102; + inputNumberFeedback.FireUpdate(); + displayDevice.PowerOff(); + }); + + var twoWayDisplayDevice = displayDevice as TwoWayDisplayBase; + if (twoWayDisplayDevice != null) + { + twoWayDisplayDevice.PowerIsOnFeedback.OutputChange += (o, a) => + { + if (!a.BoolValue) + { + inputNumber = 102; + inputNumberFeedback.FireUpdate(); + + } + else + { + inputNumber = 0; + inputNumberFeedback.FireUpdate(); + } + }; + + twoWayDisplayDevice.PowerIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.PowerOff.JoinNumber]); + twoWayDisplayDevice.PowerIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PowerOn.JoinNumber]); + } + + // PowerOn + trilist.SetSigTrueAction(joinMap.PowerOn.JoinNumber, () => + { + inputNumber = 0; + inputNumberFeedback.FireUpdate(); + displayDevice.PowerOn(); + }); + + + + for (int i = 0; i < displayDevice.InputPorts.Count; i++) + { + if (i < joinMap.InputNamesOffset.JoinSpan) + { + inputKeys.Add(displayDevice.InputPorts[i].Key); + var tempKey = inputKeys.ElementAt(i); + trilist.SetSigTrueAction((ushort)(joinMap.InputSelectOffset.JoinNumber + i), + () => displayDevice.ExecuteSwitch(displayDevice.InputPorts[tempKey].Selector)); + Debug.Console(2, displayDevice, "Setting Input Select Action on Digital Join {0} to Input: {1}", + joinMap.InputSelectOffset.JoinNumber + i, displayDevice.InputPorts[tempKey].Key.ToString()); + trilist.StringInput[(ushort)(joinMap.InputNamesOffset.JoinNumber + i)].StringValue = displayDevice.InputPorts[i].Key.ToString(); + } + else + Debug.Console(0, displayDevice, Debug.ErrorLogLevel.Warning, "Device has {0} inputs. The Join Map allows up to {1} inputs. Discarding inputs {2} - {3} from bridge.", + displayDevice.InputPorts.Count, joinMap.InputNamesOffset.JoinSpan, i + 1, displayDevice.InputPorts.Count); + } + + Debug.Console(2, displayDevice, "Setting Input Select Action on Analog Join {0}", joinMap.InputSelect); + trilist.SetUShortSigAction(joinMap.InputSelect.JoinNumber, (a) => + { + if (a == 0) + { + displayDevice.PowerOff(); + inputNumber = 0; + } + else if (a > 0 && a < displayDevice.InputPorts.Count && a != inputNumber) + { + displayDevice.ExecuteSwitch(displayDevice.InputPorts.ElementAt(a - 1).Selector); + inputNumber = a; + } + else if (a == 102) + { + displayDevice.PowerToggle(); + + } + if (twoWayDisplay != null) + inputNumberFeedback.FireUpdate(); + }); + + + var volumeDisplay = displayDevice as IBasicVolumeControls; + if (volumeDisplay == null) return; + + trilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, volumeDisplay.VolumeUp); + trilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, volumeDisplay.VolumeDown); + trilist.SetSigTrueAction(joinMap.VolumeMute.JoinNumber, volumeDisplay.MuteToggle); + + var volumeDisplayWithFeedback = volumeDisplay as IBasicVolumeWithFeedback; + + if (volumeDisplayWithFeedback == null) return; + trilist.SetSigTrueAction(joinMap.VolumeMuteOn.JoinNumber, volumeDisplayWithFeedback.MuteOn); + trilist.SetSigTrueAction(joinMap.VolumeMuteOff.JoinNumber, volumeDisplayWithFeedback.MuteOff); + + + trilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, volumeDisplayWithFeedback.SetVolume); + volumeDisplayWithFeedback.VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]); + volumeDisplayWithFeedback.MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMute.JoinNumber]); + volumeDisplayWithFeedback.MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]); + volumeDisplayWithFeedback.MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); + } + + } + + public abstract class TwoWayDisplayBase : DisplayBase, IRoutingFeedback, IHasPowerControlWithFeedback + { + public StringFeedback CurrentInputFeedback { get; private set; } + + abstract protected Func CurrentInputFeedbackFunc { get; } + + public BoolFeedback PowerIsOnFeedback { get; protected set; } + + abstract protected Func PowerIsOnFeedbackFunc { get; } + + + public static MockDisplay DefaultDisplay + { + get + { + if (_DefaultDisplay == null) + _DefaultDisplay = new MockDisplay("default", "Default Display"); + return _DefaultDisplay; + } + } + static MockDisplay _DefaultDisplay; + + public TwoWayDisplayBase(string key, string name) + : base(key, name) + { + CurrentInputFeedback = new StringFeedback(CurrentInputFeedbackFunc); + + WarmupTime = 7000; + CooldownTime = 15000; + + PowerIsOnFeedback = new BoolFeedback("PowerOnFeedback", PowerIsOnFeedbackFunc); + + Feedbacks.Add(CurrentInputFeedback); + Feedbacks.Add(PowerIsOnFeedback); + + PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; + + } + + void PowerIsOnFeedback_OutputChange(object sender, EventArgs e) + { + if (UsageTracker != null) + { + if (PowerIsOnFeedback.BoolValue) + UsageTracker.StartDeviceUsage(); + else + UsageTracker.EndDeviceUsage(); + } + } + + public event EventHandler NumericSwitchChange; + + /// + /// Raise an event when the status of a switch object changes. + /// + /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType + protected void OnSwitchChange(RoutingNumericEventArgs e) + { + var newEvent = NumericSwitchChange; + if (newEvent != null) newEvent(this, e); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Display/InputInterfaces.cs b/src/PepperDash.Essentials.Devices.Common/Displays/InputInterfaces.cs similarity index 100% rename from src/PepperDash.Essentials.Devices.Common/Display/InputInterfaces.cs rename to src/PepperDash.Essentials.Devices.Common/Displays/InputInterfaces.cs diff --git a/src/PepperDash.Essentials.Devices.Common/Displays/MockDisplay.cs b/src/PepperDash.Essentials.Devices.Common/Displays/MockDisplay.cs new file mode 100644 index 00000000..ffbb00cd --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Displays/MockDisplay.cs @@ -0,0 +1,211 @@ +using System; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common.Displays +{ + public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced + + { + public RoutingInputPort HdmiIn1 { get; private set; } + public RoutingInputPort HdmiIn2 { get; private set; } + public RoutingInputPort HdmiIn3 { get; private set; } + public RoutingInputPort ComponentIn1 { get; private set; } + public RoutingInputPort VgaIn1 { get; private set; } + + bool _PowerIsOn; + bool _IsWarmingUp; + bool _IsCoolingDown; + + protected override Func PowerIsOnFeedbackFunc + { + get + { + return () => + { + Debug.Console(2, this, "*************************************************** Display Power is {0}", _PowerIsOn ? "on" : "off"); + return _PowerIsOn; + }; + } } + protected override Func IsCoolingDownFeedbackFunc + { + get + { + return () => + { + Debug.Console(2, this, "*************************************************** {0}", _IsCoolingDown ? "Display is cooling down" : "Display has finished cooling down"); + return _IsCoolingDown; + }; + } + } + protected override Func IsWarmingUpFeedbackFunc + { + get + { + return () => + { + Debug.Console(2, this, "*************************************************** {0}", _IsWarmingUp ? "Display is warming up" : "Display has finished warming up"); + return _IsWarmingUp; + }; + } + } + protected override Func CurrentInputFeedbackFunc { get { return () => "Not Implemented"; } } + + int VolumeHeldRepeatInterval = 200; + ushort VolumeInterval = 655; + ushort _FakeVolumeLevel = 31768; + bool _IsMuted; + + public MockDisplay(string key, string name) + : base(key, name) + { + HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + ComponentIn1 = new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.Video, + eRoutingPortConnectionType.Component, null, this); + VgaIn1 = new RoutingInputPort(RoutingPortNames.VgaIn, eRoutingSignalType.Video, + eRoutingPortConnectionType.Composite, null, this); + InputPorts.AddRange(new[] { HdmiIn1, HdmiIn2, HdmiIn3, ComponentIn1, VgaIn1 }); + + VolumeLevelFeedback = new IntFeedback(() => { return _FakeVolumeLevel; }); + MuteFeedback = new BoolFeedback("MuteOn", () => _IsMuted); + + WarmupTime = 10000; + CooldownTime = 10000; + } + + public override void PowerOn() + { + if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) + { + _IsWarmingUp = true; + IsWarmingUpFeedback.InvokeFireUpdate(); + // Fake power-up cycle + WarmupTimer = new CTimer(o => + { + _IsWarmingUp = false; + _PowerIsOn = true; + IsWarmingUpFeedback.InvokeFireUpdate(); + PowerIsOnFeedback.InvokeFireUpdate(); + }, WarmupTime); + } + } + + public override void PowerOff() + { + // If a display has unreliable-power off feedback, just override this and + // remove this check. + if (PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) + { + _IsCoolingDown = true; + IsCoolingDownFeedback.InvokeFireUpdate(); + // Fake cool-down cycle + CooldownTimer = new CTimer(o => + { + Debug.Console(2, this, "Cooldown timer ending"); + _IsCoolingDown = false; + IsCoolingDownFeedback.InvokeFireUpdate(); + _PowerIsOn = false; + PowerIsOnFeedback.InvokeFireUpdate(); + }, CooldownTime); + } + } + + public override void PowerToggle() + { + if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue) + PowerOff(); + else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue) + PowerOn(); + } + + public override void ExecuteSwitch(object selector) + { + Debug.Console(2, this, "ExecuteSwitch: {0}", selector); + + if (!_PowerIsOn) + { + PowerOn(); + } + } + + + + #region IBasicVolumeWithFeedback Members + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public void SetVolume(ushort level) + { + _FakeVolumeLevel = level; + VolumeLevelFeedback.InvokeFireUpdate(); + } + + public void MuteOn() + { + _IsMuted = true; + MuteFeedback.InvokeFireUpdate(); + } + + public void MuteOff() + { + _IsMuted = false; + MuteFeedback.InvokeFireUpdate(); + } + + public BoolFeedback MuteFeedback { get; private set; } + + #endregion + + #region IBasicVolumeControls Members + + public void VolumeUp(bool pressRelease) + { + //while (pressRelease) + //{ + Debug.Console(2, this, "Volume Down {0}", pressRelease); + if (pressRelease) + { + var newLevel = _FakeVolumeLevel + VolumeInterval; + SetVolume((ushort)newLevel); + CrestronEnvironment.Sleep(VolumeHeldRepeatInterval); + } + //} + } + + public void VolumeDown(bool pressRelease) + { + //while (pressRelease) + //{ + Debug.Console(2, this, "Volume Up {0}", pressRelease); + if (pressRelease) + { + var newLevel = _FakeVolumeLevel - VolumeInterval; + SetVolume((ushort)newLevel); + CrestronEnvironment.Sleep(VolumeHeldRepeatInterval); + } + //} + } + + public void MuteToggle() + { + _IsMuted = !_IsMuted; + MuteFeedback.InvokeFireUpdate(); + } + + #endregion + + public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Shades/ShadeBase.cs b/src/PepperDash.Essentials.Devices.Common/Shades/ShadeBase.cs new file mode 100644 index 00000000..2c506750 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Shades/ShadeBase.cs @@ -0,0 +1,22 @@ +using PepperDash.Essentials.Core.Shades; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Shades +{ + public abstract class ShadeBase : EssentialsDevice, IShadesOpenCloseStop + { + public ShadeBase(string key, string name) + : base(key, name) + { + + } + + #region iShadesOpenClose Members + + public abstract void Open(); + public abstract void Stop(); + public abstract void Close(); + + #endregion + } +} diff --git a/src/PepperDash.Essentials.Devices.Common/SoftCodec/BlueJeansPc.cs b/src/PepperDash.Essentials.Devices.Common/SoftCodec/BlueJeansPc.cs index 868e7fca..67b205e5 100644 --- a/src/PepperDash.Essentials.Devices.Common/SoftCodec/BlueJeansPc.cs +++ b/src/PepperDash.Essentials.Devices.Common/SoftCodec/BlueJeansPc.cs @@ -1,19 +1,17 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using Crestron.SimplSharp; using PepperDash.Core; using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Routing; -using PepperDash.Essentials.Core.Devices; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Sources; namespace PepperDash.Essentials.Devices.Common.SoftCodec { - public class BlueJeansPc : InRoomPc, IRoutingInputs, IRunRouteAction, IRoutingSink + public class BlueJeansPc : InRoomPc, IRunRouteAction, IRoutingSink { public RoutingInputPort AnyVideoIn { get; private set; } diff --git a/src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs b/src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs new file mode 100644 index 00000000..af5f274f --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Sources/InRoomPc.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common.Sources +{ + public class InRoomPc : EssentialsDevice, IHasFeedback, IRoutingOutputs, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking + { + public uint DisplayUiType { get { return DisplayUiConstants.TypeLaptop; } } + public string IconName { get; set; } + public BoolFeedback HasPowerOnFeedback { get; private set; } + + public RoutingOutputPort AnyVideoOut { get; private set; } + + #region IRoutingOutputs Members + + /// + /// Options: hdmi + /// + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + public InRoomPc(string key, string name) + : base(key, name) + { + IconName = "PC"; + HasPowerOnFeedback = new BoolFeedback("HasPowerFeedback", + () => this.GetVideoStatuses() != VideoStatusOutputs.NoStatus); + OutputPorts = new RoutingPortCollection(); + OutputPorts.Add(AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.None, 0, this)); + } + + #region IHasFeedback Members + + /// + /// Passes through the VideoStatuses list + /// + public FeedbackCollection Feedbacks + { + get + { + var newList = new FeedbackCollection(); + newList.AddRange(this.GetVideoStatuses().ToList()); + return newList; + } + } + + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } + + public class InRoomPcFactory : EssentialsDeviceFactory + { + public InRoomPcFactory() + { + TypeNames = new List() { "inroompc" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new InRoomPc Device"); + return new InRoomPc(dc.Key, dc.Name); + } + } + +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs b/src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs new file mode 100644 index 00000000..dd1f38c2 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Sources/Laptop.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common.Sources +{ + public class Laptop : EssentialsDevice, IHasFeedback, IRoutingOutputs, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking + { + public uint DisplayUiType { get { return DisplayUiConstants.TypeLaptop; } } + public string IconName { get; set; } + public BoolFeedback HasPowerOnFeedback { get; private set; } + + public RoutingOutputPort AnyVideoOut { get; private set; } + + #region IRoutingOutputs Members + + /// + /// Options: hdmi + /// + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + public Laptop(string key, string name) + : base(key, name) + { + IconName = "Laptop"; + HasPowerOnFeedback = new BoolFeedback("HasPowerFeedback", + () => this.GetVideoStatuses() != VideoStatusOutputs.NoStatus); + OutputPorts = new RoutingPortCollection(); + OutputPorts.Add(AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.None, 0, this)); + } + + #region IHasFeedback Members + + /// + /// Passes through the VideoStatuses list + /// + public FeedbackCollection Feedbacks + { + get + { + var newList = new FeedbackCollection(); + newList.AddRange(this.GetVideoStatuses().ToList()); + return newList; + } + } + + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } + + public class LaptopFactory : EssentialsDeviceFactory + { + public LaptopFactory() + { + TypeNames = new List() { "laptop" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new Laptop Device"); + return new Laptop(dc.Key, dc.Name); + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/src/PepperDash.Essentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 5ee5a893..e84dc974 100644 --- a/src/PepperDash.Essentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/src/PepperDash.Essentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -9,6 +9,7 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Fusion; +using PepperDash.Essentials.Devices.Common.Sources; namespace PepperDash.Essentials.Fusion { @@ -212,7 +213,7 @@ namespace PepperDash.Essentials.Fusion break; } - var laptops = dict.Where(d => d.Value.SourceDevice is Core.Devices.Laptop); + var laptops = dict.Where(d => d.Value.SourceDevice is Laptop); i = 1; foreach (var kvp in laptops) { diff --git a/src/PepperDash.Essentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs b/src/PepperDash.Essentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs index 57aed03e..c07e0b1c 100644 --- a/src/PepperDash.Essentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs +++ b/src/PepperDash.Essentials/UIDrivers/EssentialsHuddle/EssentialsHuddleTechPageDriver.cs @@ -12,8 +12,11 @@ using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.Touchpanels.Keyboards; +using PepperDash.Essentials.Devices.Common.Displays; using PepperDash.Essentials.Devices.Displays; using PepperDash.Essentials.Room.Config; +using DisplayBase = PepperDash.Essentials.Core.DisplayBase; +using TwoWayDisplayBase = PepperDash.Essentials.Core.TwoWayDisplayBase; namespace PepperDash.Essentials.UIDrivers {