From 0428b6ac59441f909b957482ac504e58c8e7fa2e Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Wed, 20 Apr 2022 16:17:28 -0500 Subject: [PATCH 01/16] feat: seeded repo with base plugin, config, and readme --- .../EssentialsPluginBridgeJoinMapTemplate.cs | 151 ------ .../EssentialsPluginConfigObjectTemplate.cs | 244 ---------- .../EssentialsPluginCrestronDeviceTemplate.cs | 95 ---- .../EssentialsPluginDeviceTemplate.cs | 303 ------------- .../EssentialsPluginFactoryTemplate.cs | 267 ----------- .../EssentialsPluginLogicDeviceTemplate.cs | 86 ---- README.md | 428 +++++++----------- ...urationFile.json => configurationFile.json | 188 ++++---- fcs-avi-interface-commands.pdf | Bin 0 -> 252609 bytes images/logo_pdt_no_tagline_600.png | Bin 0 -> 350979 bytes packages.config | 4 +- src/AcuityFrescoBridgeJoinMap.cs | 72 +++ src/AcuityFrescoDevice.cs | 273 +++++++++++ src/AcuityFrescoFactory.cs | 59 +++ src/AcuityFrescoPropertiesConfig.cs | 62 +++ .../PepperDashPluginAcuityFresco.csproj | 18 +- .../PepperDashPluginAcuityFresco.nuspec | 12 +- .../PepperDashPluginAcuityFresco.sln | 2 +- .../Properties/AssemblyInfo.cs | 6 +- .../Properties/ControlSystem.cfg | 0 20 files changed, 745 insertions(+), 1525 deletions(-) delete mode 100644 EssentialsPluginTemplate/EssentialsPluginBridgeJoinMapTemplate.cs delete mode 100644 EssentialsPluginTemplate/EssentialsPluginConfigObjectTemplate.cs delete mode 100644 EssentialsPluginTemplate/EssentialsPluginCrestronDeviceTemplate.cs delete mode 100644 EssentialsPluginTemplate/EssentialsPluginDeviceTemplate.cs delete mode 100644 EssentialsPluginTemplate/EssentialsPluginFactoryTemplate.cs delete mode 100644 EssentialsPluginTemplate/EssentialsPluginLogicDeviceTemplate.cs rename EssentialsPluginTemplate/configurationFile.json => configurationFile.json (68%) create mode 100644 fcs-avi-interface-commands.pdf create mode 100644 images/logo_pdt_no_tagline_600.png create mode 100644 src/AcuityFrescoBridgeJoinMap.cs create mode 100644 src/AcuityFrescoDevice.cs create mode 100644 src/AcuityFrescoFactory.cs create mode 100644 src/AcuityFrescoPropertiesConfig.cs rename EssentialsPluginTemplate/EssentialsPluginTemplate.csproj => src/PepperDashPluginAcuityFresco.csproj (88%) rename EssentialsPluginTemplate/EssentialsPluginTemplate.nuspec => src/PepperDashPluginAcuityFresco.nuspec (62%) rename EssentialsPluginTemplate/EssentialsPluginTemplate.sln => src/PepperDashPluginAcuityFresco.sln (82%) rename {EssentialsPluginTemplate => src}/Properties/AssemblyInfo.cs (62%) rename {EssentialsPluginTemplate => src}/Properties/ControlSystem.cfg (100%) diff --git a/EssentialsPluginTemplate/EssentialsPluginBridgeJoinMapTemplate.cs b/EssentialsPluginTemplate/EssentialsPluginBridgeJoinMapTemplate.cs deleted file mode 100644 index 54ef3d9..0000000 --- a/EssentialsPluginTemplate/EssentialsPluginBridgeJoinMapTemplate.cs +++ /dev/null @@ -1,151 +0,0 @@ -using PepperDash.Essentials.Core; - -namespace EssentialsPluginTemplate -{ - /// - /// Plugin device Bridge Join Map - /// - /// - /// Rename the class to match the device plugin being developed. Reference Essentials JoinMaps, if one exists for the device plugin being developed - /// - /// - /// - /// "EssentialsPluginBridgeJoinMapTemplate" renamed to "SamsungMdcBridgeJoinMap" - /// - public class EssentialsPluginBridgeJoinMapTemplate : JoinMapBaseAdvanced - { - #region Digital - - // TODO [ ] Add digital joins below plugin being developed - - /// - /// Plugin online join map - /// - /// - /// Reports the plugin online sate to SiMPL as a boolean value - /// - [JoinName("IsOnline")] - public JoinDataComplete IsOnline = new JoinDataComplete( - new JoinData - { - JoinNumber = 1, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Is Online", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Digital - }); - - // TODO [ ] If connection state is managed by Essentials, delete the following. If connection is managed by SiMPL, uncomment the following - /// - /// Plugin connect join map - /// This property would only be needed if the plugin connection needs to be managed by SiMPL via the bridge - /// - /// - /// Typically used with socket based communications. Connects (held) and disconnects (released) socket based communcations when triggered from SiMPL. - /// Additionally, the connection state feedback will report to SiMP Las a boolean value. - /// - //[JoinName("Connect")] - //public JoinDataComplete Connect = new JoinDataComplete( - // new JoinData - // { - // JoinNumber = 2, - // JoinSpan = 1 - // }, - // new JoinMetadata - // { - // Description = "Connect (Held)/Disconnect (Release) & Connect state feedback", - // JoinCapabilities = eJoinCapabilities.ToFromSIMPL, - // JoinType = eJoinType.Digital - // }); - - #endregion - - - #region Analog - - // TODO [ ] Add analog joins below plugin being developed - - /// - /// Plugin socket status join map - /// - /// - /// Typically used with socket based communications. Reports the socket state to SiMPL as an analog value. - /// - /// - [JoinName("SocketStatus")] - public JoinDataComplete SocketStatus = new JoinDataComplete( - new JoinData - { - JoinNumber = 1, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Socket SocketStatus", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - /// - /// Plugin monitor status join map - /// - /// - /// Typically used with comms monitor to report plugin monitor state for system status page and Fusion monitor state. - /// - /// - [JoinName("MonitorStatus")] - public JoinDataComplete MonitorStatus = new JoinDataComplete( - new JoinData - { - JoinNumber = 2, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Monitor Status", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Analog - }); - - #endregion - - - #region Serial - - // TODO [ ] Add serial joins below plugin being developed - - /// - /// Plugin device name - /// - /// - /// Reports the plugin name, as read from the configuration file, to SiMPL as a string value. - /// - [JoinName("DeviceName")] - public JoinDataComplete DeviceName = new JoinDataComplete( - new JoinData - { - JoinNumber = 1, - JoinSpan = 1 - }, - new JoinMetadata - { - Description = "Device Name", - JoinCapabilities = eJoinCapabilities.ToSIMPL, - JoinType = eJoinType.Serial - }); - - #endregion - - /// - /// Plugin device BridgeJoinMap constructor - /// - /// This will be the join it starts on the EISC bridge - public EssentialsPluginBridgeJoinMapTemplate(uint joinStart) - : base(joinStart, typeof(EssentialsPluginBridgeJoinMapTemplate)) - { - } - } -} diff --git a/EssentialsPluginTemplate/EssentialsPluginConfigObjectTemplate.cs b/EssentialsPluginTemplate/EssentialsPluginConfigObjectTemplate.cs deleted file mode 100644 index 5cdfcfd..0000000 --- a/EssentialsPluginTemplate/EssentialsPluginConfigObjectTemplate.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; -using PepperDash.Essentials.Core; - -namespace EssentialsPluginTemplate -{ - /// - /// Plugin device configuration object - /// - /// - /// Rename the class to match the device plugin being created - /// - /// - /// "EssentialsPluginConfigObjectTemplate" renamed to "SamsungMdcConfig" - /// - /// { - /// "devices": [ - /// { - /// "key": "essentialsPluginKey", - /// "name": "Essentials Plugin Name", - /// "type": "essentialsPluginTypeName", - /// "group": "pluginDevices", - /// "properties": { - /// "control": { - /// "method": "PepperDash.Core.eControlMethod", - /// "controlPortDevKey": "examplePortDevKey", - /// "controlPortNumber": 1, - /// "comParams": { - /// "baudRate": 9600, - /// "dataBits": 8, - /// "stopBits": 1, - /// "parity": "None", - /// "protocol": "RS232", - /// "hardwareHandshake": "None", - /// "softwareHandshake": "None" - /// }, - /// "tcpSshProperties": { - /// "address": "172.22.0.101", - /// "port": 23, - /// "username": "admin", - /// "password": "password", - /// "autoReconnect": true, - /// "autoReconnectIntervalMs": 10000 - /// } - /// }, - /// "pollTimeMs": 30000, - /// "warningTimeoutMs": 180000, - /// "errorTimeoutMs": 300000, - /// "pluginCollection": { - /// "item1": { - /// "name": "Item 1", - /// "value": 1 - /// } - /// "item2": { - /// "name": "Item 2",, - /// "value": 2 - /// } - /// } - /// } - /// } - /// ] - /// } - /// - /// - [ConfigSnippet("{\"devices\":[{\"key\":\"essentialsPluginKey\",\"name\":\"Essentials Plugin Name\",\"type\":\"essentialsPluginTypeName\",\"group\":\"pluginDevices\",\"properties\":{\"control\":{\"method\":\"PepperDash.Core.eControlMethod\",\"controlPortDevKey\":\"exampleControlPortDevKey\",\"controlPortNumber\":1,\"comParams\":{\"baudRate\":9600,\"dataBits\":8,\"stopBits\":1,\"parity\":\"None\",\"protocol\":\"RS232\",\"hardwareHandshake\":\"None\",\"softwareHandshake\":\"None\"},\"tcpSshProperties\":{\"address\":\"172.22.0.101\",\"port\":22,\"username\":\"admin\",\"password\":\"password\",\"autoReconnect\":true,\"autoReconnectIntervalMs\":10000}},\"pollTimeMs\":30000,\"warningTimeoutMs\":180000,\"errorTimeoutMs\":300000,\"pluginCollection\":{\"item1\":{\"name\":\"Item 1\",\"value\":1},\"item2\":{\"name\":\"Item 2\",\"value\":2}}}}]}")] - public class EssentialsPluginConfigObjectTemplate - { - /// - /// JSON control object - /// - /// - /// Typically this object is not required, but in some instances it may be needed. For example, when building a - /// plugin that is using Telnet (TCP/IP) communications and requires login, the device will need to handle the login. - /// In order to do so, you will need the username and password in the "tcpSshProperties" object. - /// - /// - /// - /// "control": { - /// "method": "tcpIp", - /// "controlPortDevKey": "processor", - /// "controlPortNumber": 1, - /// "comParams": { - /// "baudRate": 9600, - /// "dataBits": 8, - /// "stopBits": 1, - /// "parity": "None", - /// "protocol": "RS232", - /// "hardwareHandshake": "None", - /// "softwareHandshake": "None" - /// }, - /// "tcpSshProperties": { - /// "address": "172.22.0.101", - /// "port": 23, - /// "username": "admin", - /// "password": "password", - /// "autoReconnect": true, - /// "autoReconnectIntervalMs": 10000 - /// } - /// } - /// - /// - [JsonProperty("control")] - public EssentialsControlPropertiesConfig Control { get; set; } - - /// - /// Serializes the poll time value - /// - /// - /// This is an exmaple device plugin property. This should be modified or deleted as needed for the plugin being built. - /// - /// - /// PollTimeMs property gets/sets the value as a long - /// - /// - /// - /// "properties": { - /// "polltimeMs": 30000 - /// } - /// - /// - [JsonProperty("pollTimeMs")] - public long PollTimeMs { get; set; } - - /// - /// Serializes the warning timeout value - /// - /// - /// This is an exmaple device plugin property. This should be modified or deleted as needed for the plugin being built. - /// - /// - /// WarningTimeoutMs property gets/sets the value as a long - /// - /// - /// - /// "properties": { - /// "warningTimeoutMs": 180000 - /// } - /// - /// - [JsonProperty("warningTimeoutMs")] - public long WarningTimeoutMs { get; set; } - - /// - /// Serializes the error timeout value - /// - /// /// - /// This is an exmaple device plugin property. This should be modified or deleted as needed for the plugin being built. - /// - /// - /// ErrorTimeoutMs property gets/sets the value as a long - /// - /// - /// - /// "properties": { - /// "errorTimeoutMs": 300000 - /// } - /// - /// - [JsonProperty("errorTimeoutMs")] - public long ErrorTimeoutMs { get; set; } - - /// - /// Example dictionary of objects - /// - /// - /// This is an example collection configuration object. This should be modified or deleted as needed for the plugin being built. - /// - /// - /// - /// "properties": { - /// "presets": { - /// "preset1": { - /// "enabled": true, - /// "name": "Preset 1" - /// } - /// } - /// } - /// - /// - /// - /// - /// "properties": { - /// "inputNames": { - /// "input1": "Input 1", - /// "input2": "Input 2" - /// } - /// } - /// - /// - [JsonProperty("pluginCollection")] - public Dictionary PluginCollection { get; set; } - - /// - /// Constuctor - /// - /// - /// If using a collection you must instantiate the collection in the constructor - /// to avoid exceptions when reading the configuration file - /// - public EssentialsPluginConfigObjectTemplate() - { - PluginCollection = new Dictionary(); - } - } - - /// - /// Example plugin configuration dictionary object - /// - /// - /// This is an example collection of configuration objects. This can be modified or deleted as needed for the plugin being built. - /// - /// - /// - /// "properties": { - /// "dictionary": { - /// "item1": { - /// "name": "Item 1 Name", - /// "value": "Item 1 Value" - /// } - /// } - /// } - /// - /// - public class EssentialsPluginConfigObjectDictionaryTemplate - { - /// - /// Serializes collection name property - /// - /// - /// This is an example collection of configuration objects. This can be modified or deleted as needed for the plugin being built. - /// - [JsonProperty("name")] - public string Name { get; set; } - - /// - /// Serializes collection value property - /// - /// - /// This is an example collection of configuration objects. This can be modified or deleted as needed for the plugin being built. - /// - [JsonProperty("value")] - public uint Value { get; set; } - } -} \ No newline at end of file diff --git a/EssentialsPluginTemplate/EssentialsPluginCrestronDeviceTemplate.cs b/EssentialsPluginTemplate/EssentialsPluginCrestronDeviceTemplate.cs deleted file mode 100644 index 275b8c6..0000000 --- a/EssentialsPluginTemplate/EssentialsPluginCrestronDeviceTemplate.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.DeviceSupport; -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; - -namespace EssentialsPluginTemplate -{ - /// - /// Plugin device - /// - /// - /// Rename the class to match the device plugin being developed. - /// - /// - /// "EssentialsPluginDeviceTemplate" renamed to "SamsungMdcDevice" - /// - public class EssentialsPluginCrestronDeviceTemplate : CrestronGenericBridgeableBaseDevice - { - /// - /// It is often desirable to store the config - /// - private EssentialsPluginConfigObjectTemplate _config; - - - #region Constructor for Devices without IBasicCommunication. Remove if not needed - - /// - /// Plugin device constructor for Crestron devices - /// - /// A string - /// A string - /// An EssentialsPluginConfigObjectTemplate object - /// A GenericBase object - public EssentialsPluginCrestronDeviceTemplate(string key, string name, EssentialsPluginConfigObjectTemplate config, GenericBase hardware) - : base(key, name, hardware) - { - Debug.Console(0, this, "Constructing new {0} instance", name); - - // The base class takes care of registering the hardware device for you - - // TODO [ ] Update the constructor as needed for the plugin device being developed - - _config = config; - } - - #endregion - - - #region Overrides of EssentialsBridgeableDevice - - /// - /// Links the plugin device to the EISC bridge - /// - /// A BasicTriList object - /// A uint - /// A string - /// An EiscApiAdvanced object - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = new EssentialsPluginBridgeJoinMapTemplate(joinStart); - - // This adds the join map to the collection on the bridge - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - - var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); - - if (customJoins != null) - { - joinMap.SetCustomJoinData(customJoins); - } - - Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(0, "Linking to Bridge Type {0}", GetType().Name); - - // TODO [ ] Implement bridge links as needed - - // links to bridge - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - - trilist.OnlineStatusChange += (o, a) => - { - if (!a.DeviceOnLine) return; - - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - }; - } - - #endregion - - } -} \ No newline at end of file diff --git a/EssentialsPluginTemplate/EssentialsPluginDeviceTemplate.cs b/EssentialsPluginTemplate/EssentialsPluginDeviceTemplate.cs deleted file mode 100644 index f06f9bc..0000000 --- a/EssentialsPluginTemplate/EssentialsPluginDeviceTemplate.cs +++ /dev/null @@ -1,303 +0,0 @@ -using Crestron.SimplSharpPro.DeviceSupport; -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; - -namespace EssentialsPluginTemplate -{ - /// - /// Plugin device - /// - /// - /// Rename the class to match the device plugin being developed. - /// - /// - /// "EssentialsPluginDeviceTemplate" renamed to "SamsungMdcDevice" - /// - public class EssentialsPluginDeviceTemplate : EssentialsBridgeableDevice - { - /// - /// It is often desirable to store the config - /// - private EssentialsPluginConfigObjectTemplate _config; - - #region IBasicCommunication Properties and Constructor. Remove if not needed. - - // TODO [ ] Add, modify, remove properties and fields as needed for the plugin being developed - private readonly IBasicCommunication _comms; - private readonly GenericCommunicationMonitor _commsMonitor; - - // TODO [ ] Delete the properties below if using a HEX/byte based API - // _comms gather is commonly used for ASCII based API's with deelimiters - private readonly CommunicationGather _commsGather; - - /// - /// Set this value to that of the delimiter used by the API (if applicable) - /// - private const string CommsDelimiter = "\r"; - - // TODO [ ] Delete the properties below if using an ASCII based API - // _comms byte buffer is commonly used for HEX/byte based API's - private byte[] _commsByteBuffer = { }; - - // TODO [ ] If connection state is managed by Essentials, delete the following. If connection is managed by SiMPL, uncomment the following - /// - /// Connects/disconnects the comms of the plugin device - /// This property would only be needed if the plugin connection needs to be managed by SiMPL via the bridge - /// - /// - /// triggers the _comms.Connect/Disconnect as well as thee comms monitor start/stop - /// - //public bool Connect - //{ - // get { return _comms.IsConnected; } - // set - // { - // if (value) - // { - // _comms.Connect(); - // _commsMonitor.Start(); - // } - // else - // { - // _comms.Disconnect(); - // _commsMonitor.Stop(); - // } - // } - //} - - /// - /// Reports connect feedback through the bridge - /// - public BoolFeedback ConnectFeedback { get; private set; } - - /// - /// Reports online feedback through the bridge - /// - public BoolFeedback OnlineFeedback { get; private set; } - - /// - /// Reports socket status feedback through the bridge - /// - public IntFeedback SocketStatusFeedback { get; private set; } - - /// - /// Reports monitor status feedback through the bridge - /// Typically used for Fusion status reporting and system status LED's - /// - public IntFeedback MonitorStatusFeedback { get; private set; } - - /// - /// Plugin device constructor - /// - /// device key - /// device name - /// device configuration object - /// device communication as IBasicCommunication - /// - /// - public EssentialsPluginDeviceTemplate(string key, string name, EssentialsPluginConfigObjectTemplate config, IBasicCommunication comms) - : base(key, name) - { - Debug.Console(0, this, "Constructing new {0} instance", name); - - // TODO [ ] Update the constructor as needed for the plugin device being developed - - _config = config; - - ConnectFeedback = new BoolFeedback(() => _comms.IsConnected); - OnlineFeedback = new BoolFeedback(() => _commsMonitor.IsOnline); - MonitorStatusFeedback = new IntFeedback(() => (int)_commsMonitor.Status); - - _comms = comms; - _commsMonitor = new GenericCommunicationMonitor(this, _comms, _config.PollTimeMs, _config.WarningTimeoutMs, _config.ErrorTimeoutMs, Poll); - - var socket = _comms as ISocketStatus; - if (socket != null) - { - // device comms is IP **ELSE** device comms is RS232 - socket.ConnectionChange += socket_ConnectionChange; - SocketStatusFeedback = new IntFeedback(() => (int)socket.ClientStatus); - } - - #region Communication data event handlers. Comment out any that don't apply to the API type - - // TODO [ ] Delete the properties below if using a HEX/byte based API - // _comms gather is commonly used for ASCII based API's that have a defined delimiter - _commsGather = new CommunicationGather(_comms, CommsDelimiter); - // Event fires when the defined delimter is found - _commsGather.LineReceived += Handle_LineRecieved; - - // TODO [ ] Delete event if the device has a delimiter - // Event fires as data is recieved - _comms.TextReceived += Handle_TextReceived; - - // TODO [ ] Delete the properties below if using an ASCII based API - // _comms byte buffer for HEX/byte based API's in raw format. Commonly used for API's such as Samsung MDC - // Event fires as data is recieved - _comms.BytesReceived += Handle_BytesReceived; - - #endregion Communication data event handlers. Comment out any that don't apply to the API type - - Debug.Console(0, this, "Constructing new {0} instance complete", name); - Debug.Console(0, new string('*', 80)); - Debug.Console(0, new string('*', 80)); - } - - /// - /// Use the custom activiate to connect the device and start the comms monitor. - /// This method will be called when the device is built. - /// - /// - public override bool CustomActivate() - { - // Essentials will handle the connect method to the device - _comms.Connect(); - // Essentialss will handle starting the comms monitor - _commsMonitor.Start(); - - return base.CustomActivate(); - } - - private void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs args) - { - if (ConnectFeedback != null) - ConnectFeedback.FireUpdate(); - - if (SocketStatusFeedback != null) - SocketStatusFeedback.FireUpdate(); - } - - // TODO [ ] Delete the properties below if using a HEX/byte based API - // commonly used with ASCII based API's with a defined delimiter - private void Handle_LineRecieved(object sender, GenericCommMethodReceiveTextArgs args) - { - // TODO [ ] Implement method - throw new System.NotImplementedException(); - } - - // TODO [ ] Delete the properties below if using a HEX/byte based API - // commonly used with ASCII based API's without a delimiter - void Handle_TextReceived(object sender, GenericCommMethodReceiveTextArgs e) - { - // TODO [ ] Implement method - throw new System.NotImplementedException(); - } - - // TODO [ ] Delete the properties below if using an ASCII based API - private void Handle_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs args) - { - // TODO [ ] Implement method - throw new System.NotImplementedException(); - } - - // TODO [ ] Delete the properties below if using a HEX/byte based API - /// - /// Sends text to the device plugin comms - /// - /// - /// Can be used to test commands with the device plugin using the DEVPROPS and DEVJSON console commands - /// - /// Command to be sent - public void SendText(string text) - { - if (string.IsNullOrEmpty(text)) return; - - _comms.SendText(string.Format("{0}{1}", text, CommsDelimiter)); - } - - // TODO [ ] Delete the properties below if using an ASCII based API - /// - /// Sends bytes to the device plugin comms - /// - /// - /// Can be used to test commands with the device plugin using the DEVPROPS and DEVJSON console commands - /// - /// Bytes to be sent - public void SendBytes(byte[] bytes) - { - if (bytes == null) return; - - _comms.SendBytes(bytes); - } - - /// - /// Polls the device - /// - /// - /// Poll method is used by the communication monitor. Update the poll method as needed for the plugin being developed - /// - public void Poll() - { - // TODO [ ] Update Poll method as needed for the plugin being developed - // Example: SendText("getStatus"); - throw new System.NotImplementedException(); - } - - #endregion IBasicCommunication Properties and Constructor. Remove if not needed. - - - #region Overrides of EssentialsBridgeableDevice - - /// - /// Links the plugin device to the EISC bridge - /// - /// - /// - /// - /// - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = new EssentialsPluginBridgeJoinMapTemplate(joinStart); - - // This adds the join map to the collection on the bridge - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - - var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); - - if (customJoins != null) - { - joinMap.SetCustomJoinData(customJoins); - } - - Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(0, "Linking to Bridge Type {0}", GetType().Name); - - // TODO [ ] Implement bridge links as needed - - // links to bridge - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - - // TODO [ ] If connection state is managed by Essentials, delete the following. If connection is managed by SiMPL, uncomment the following - //trilist.SetBoolSigAction(joinMap.Connect.JoinNumber, sig => Connect = sig); - //ConnectFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Connect.JoinNumber]); - - SocketStatusFeedback.LinkInputSig(trilist.UShortInput[joinMap.SocketStatus.JoinNumber]); - OnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - - UpdateFeedbacks(); - - trilist.OnlineStatusChange += (o, a) => - { - if (!a.DeviceOnLine) return; - - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - UpdateFeedbacks(); - }; - } - - private void UpdateFeedbacks() - { - // TODO [ ] Update as needed for the plugin being developed - ConnectFeedback.FireUpdate(); - OnlineFeedback.FireUpdate(); - SocketStatusFeedback.FireUpdate(); - } - - #endregion Overrides of EssentialsBridgeableDevice - } -} - diff --git a/EssentialsPluginTemplate/EssentialsPluginFactoryTemplate.cs b/EssentialsPluginTemplate/EssentialsPluginFactoryTemplate.cs deleted file mode 100644 index 30d973b..0000000 --- a/EssentialsPluginTemplate/EssentialsPluginFactoryTemplate.cs +++ /dev/null @@ -1,267 +0,0 @@ -using System; -using System.Collections.Generic; -using Crestron.SimplSharpPro.UI; -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; - -namespace EssentialsPluginTemplate -{ - /// - /// Plugin factory for devices that require communications using IBasicCommunications or custom communication methods - /// - /// - /// Rename the class to match the device plugin being developed and update the factory as needed. - /// If this class is not used, delete the class and delete the associated EssentialsPluginDeviceTemplate.cs file from the solution - /// - /// - /// "EssentialsPluginFactoryTemplate" renamed to "SamsungMdcFactory" - /// - public class EssentialsPluginFactoryTemplate : EssentialsPluginDeviceFactory - { - /// - /// Plugin device factory constructor - /// - /// - /// Update the MinimumEssentialsFrameworkVersion & TypeNames as needed when creating a plugin - /// - /// - /// Set the minimum Essentials Framework Version - /// - /// MinimumEssentialsFrameworkVersion = "1.5.5"; - /// - /// In the constructor we initialize the list with the typenames that will build an instance of this device - /// - /// TypeNames = new List() { "SamsungMdc", "SamsungMdcDisplay" }; - /// - /// - public EssentialsPluginFactoryTemplate() - { - // Set the minimum Essentials Framework Version - // TODO [ ] Update the Essentials minimum framework version which this plugin has been tested against - MinimumEssentialsFrameworkVersion = "1.6.5"; - - // In the constructor we initialize the list with the typenames that will build an instance of this device - // only include unique typenames, when the constructur is used all the typenames will be evaluated in lower case. - // TODO [ ] Update the TypeNames for the plugin being developed - TypeNames = new List() { "examplePluginDevice" }; - } - - /// - /// Builds and returns an instance of EssentialsPluginDeviceTemplate - /// - /// device configuration - /// plugin device or null - /// - /// The example provided below takes the device key, name, properties config and the comms device created. - /// Modify the EssetnialsPlugingDeviceTemplate constructor as needed to meet the requirements of the plugin device. - /// - /// - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - try - { - Debug.Console(0, new string('*', 80)); - Debug.Console(0, new string('*', 80)); - Debug.Console(0, "[{0}] Factory Attempting to create new device from type: {1}", dc.Key, dc.Type); - - // get the plugin device properties configuration object & check for null - var propertiesConfig = dc.Properties.ToObject(); - if (propertiesConfig == null) - { - Debug.Console(0, "[{0}] Factory: failed to read properties config for {1}", dc.Key, dc.Name); - return null; - } - - // If using a communication method not supported in PepperDash.Core.eControlMethod reference the EXAMPLE below of pulling out control method properties - // ** Update as needed for YOUR plugin ** - // get the plugin device control properties configuratin object & check for null - var controlConfig = CommFactory.GetControlPropertiesConfig(dc); - if (controlConfig == null) - { - Debug.Console(0, "[{0}] Factory: failed to read control config for {1}", dc.Key, dc.Name); - } - // TODO [ ] If using an unsupported PepperDash.Core.eControlMethod, you can selective pull property values out of the JSON control block with the examples below - else if(controlConfig.Method.ToString().Contains("http")) - { - - var address = controlConfig.TcpSshProperties.Address; - var port = controlConfig.TcpSshProperties.Port; - Debug.Console(0, "[{0}] {1} will attempt to connect using: {2}:{3}", dc.Key, dc.Name, address, port); - - var username = controlConfig.TcpSshProperties.Username; - var password = controlConfig.TcpSshProperties.Password; - Debug.Console(1, "[{0}] {1} will attempt to use authorization credentials: {2}:{3}", dc.Key, dc.Name, username, password); - - // TODO [ ] Update with the proper constructor to instantiate the device using HTTPS - throw new NotImplementedException(); - } - - // TODO [ ] If your device is using a PepperDash.Core.eControlMethod supported enum, the snippet below will support standard comm methods - // build the plugin device comms (for all other comms methods) & check for null - var comms = CommFactory.CreateCommForDevice(dc); - if (comms != null) return new EssentialsPluginDeviceTemplate(dc.Key, dc.Name, propertiesConfig, comms); - Debug.Console(0, "[{0}] Factory: failed to create comm for {1}", dc.Key, dc.Name); - return null; - } - catch (Exception ex) - { - Debug.Console(0, "[{0}] Factory BuildDevice Exception: {1}", dc.Key, ex); - return null; - } - } - } - - - - /// - /// Plugin factory for devices that don't require communications using IBasicCommunications or custom communication methods ** logic only plugin ** - /// - /// - /// Rename the class to match the device plugin being developed and update the factory as needed. - /// If this class is not used, delete the class and delete the associated EssentialsPluginLogicDeviceTemplate.cs file from the solution - /// - /// - /// "EssentialsPluginFactoryTemplate" renamed to "MyLogicDeviceFactory" - /// - public class EssentialsPluginFactoryLogicDeviceTemplate : EssentialsPluginDeviceFactory - { - /// - /// Plugin device factory constructor - /// - /// - /// Update the MinimumEssentialsFrameworkVersion & TypeNames as needed when creating a plugin - /// - /// - /// Set the minimum Essentials Framework Version - /// - /// MinimumEssentialsFrameworkVersion = "1.6.4; - /// - /// In the constructor we initialize the list with the typenames that will build an instance of this device - /// - /// TypeNames = new List() { "SamsungMdc", "SamsungMdcDisplay" }; - /// - /// - public EssentialsPluginFactoryLogicDeviceTemplate() - { - // Set the minimum Essentials Framework Version - // only include unique typenames, when the constructur is used all the typenames will be evaluated in lower case. - // TODO [ ] Update the Essentials minimum framework version which this plugin has been tested against - MinimumEssentialsFrameworkVersion = "1.6.5"; - - // In the constructor we initialize the list with the typenames that will build an instance of this device - // TODO [ ] Update the TypeNames for the plugin being developed - TypeNames = new List() { "examplePluginLogicDevice" }; - } - - /// - /// Builds and returns an instance of EssentialsPluginTemplateLogicDevice - /// - /// device configuration - /// plugin device or null - /// - /// The example provided below takes the device key, name, properties config and the comms device created. - /// Modify the EssetnialsPlugingDeviceTemplate constructor as needed to meet the requirements of the plugin device. - /// - /// - public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc) - { - - Debug.Console(1, "[{0}] Factory Attempting to create new device from type: {1}", dc.Key, dc.Type); - - // get the plugin device properties configuration object & check for null - var propertiesConfig = dc.Properties.ToObject(); - if (propertiesConfig == null) - { - Debug.Console(0, "[{0}] Factory: failed to read properties config for {1}", dc.Key, dc.Name); - return null; - } - - // get the plugin device control properties configuratin object & check for null - var controlConfig = CommFactory.GetControlPropertiesConfig(dc); - if (controlConfig == null) - { - return new EssentialsPluginLogicDeviceTemplate(dc.Key, dc.Name, propertiesConfig); - } - Debug.Console(0, "[{0}] Factory: Unable to get control properties from device config for {1}", dc.Key, dc.Name); - return null; - } - } - - - - /// - /// Plugin factory for Crestron wrapper devices - /// - /// - /// Rename the class to match the device plugin being developed and update the factory as needed. - /// If this class is not used, delete the class and delete the associated EssentialsPluginCrestronDeviceTemplate.cs file from the solution - /// - /// - /// "EssentialsPluginFactoryTemplate" renamed to "MyCrestronDeviceFactory" - /// - public class EssentialsPluginFactoryCrestronDeviceTemplate : EssentialsPluginDeviceFactory - { - /// - /// Plugin device factory constructor - /// - /// - /// Update the MinimumEssentialsFrameworkVersion & TypeNames as needed when creating a plugin - /// - /// - /// Set the minimum Essentials Framework Version - /// - /// MinimumEssentialsFrameworkVersion = "1.6.4; - /// - /// In the constructor we initialize the list with the typenames that will build an instance of this device - /// - /// TypeNames = new List() { "SamsungMdc", "SamsungMdcDisplay" }; - /// - /// - public EssentialsPluginFactoryCrestronDeviceTemplate() - { - // Set the minimum Essentials Framework Version - // only include unique typenames, when the constructur is used all the typenames will be evaluated in lower case. - // TODO [ ] Update the Essentials minimum framework version which this plugin has been tested against - MinimumEssentialsFrameworkVersion = "1.6.5"; - - // In the constructor we initialize the list with the typenames that will build an instance of this device - // TODO [ ] Update the TypeNames for the plugin being developed - TypeNames = new List() { "examplePluginCrestronDevice" }; - } - - /// - /// Builds and returns an instance of EssentialsPluginTemplateCrestronDevice - /// - /// device configuration - /// plugin device or null - /// - /// The example provided below takes the device key, name, properties config and the comms device created. - /// Modify the EssetnialsPlugingDeviceTemplate constructor as needed to meet the requirements of the plugin device. - /// - /// - public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc) - { - - Debug.Console(1, "[{0}] Factory Attempting to create new device from type: {1}", dc.Key, dc.Type); - - // get the plugin device properties configuration object & check for null - var propertiesConfig = dc.Properties.ToObject(); - if (propertiesConfig == null) - { - Debug.Console(0, "[{0}] Factory: failed to read properties config for {1}", dc.Key, dc.Name); - return null; - } - - // get the plugin device control properties configuratin object & check for null - var controlConfig = CommFactory.GetControlPropertiesConfig(dc); - if (controlConfig != null) - { - var myTouchpanel = new Tsw760(controlConfig.IpIdInt, Global.ControlSystem); - return new EssentialsPluginCrestronDeviceTemplate(dc.Key, dc.Name, propertiesConfig, myTouchpanel); - } - Debug.Console(0, "[{0}] Factory: Unable to get control properties from device config for {1}", dc.Key, dc.Name); - return null; - } - } -} \ No newline at end of file diff --git a/EssentialsPluginTemplate/EssentialsPluginLogicDeviceTemplate.cs b/EssentialsPluginTemplate/EssentialsPluginLogicDeviceTemplate.cs deleted file mode 100644 index 9408340..0000000 --- a/EssentialsPluginTemplate/EssentialsPluginLogicDeviceTemplate.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Crestron.SimplSharpPro.DeviceSupport; -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; - -namespace EssentialsPluginTemplate -{ - /// - /// Plugin device template for logic devices that don't communicate outside the program - /// - /// - /// Rename the class to match the device plugin being developed. - /// - /// - /// "EssentialsPluginLogicDeviceTemplate" renamed to "SamsungMdcDevice" - /// - public class EssentialsPluginLogicDeviceTemplate : EssentialsBridgeableDevice - { - /// - /// It is often desirable to store the config - /// - private EssentialsPluginConfigObjectTemplate _config; - - - /// - /// Plugin device constructor - /// - /// A string - /// A string - /// An EssentialsPluginConfigObjectTemplate object - public EssentialsPluginLogicDeviceTemplate(string key, string name, EssentialsPluginConfigObjectTemplate config) - : base(key, name) - { - Debug.Console(0, this, "Constructiong new {0} instance", name); - - // store the config - _config = config; - - // TODO [ ] Update the constructor as needed for the plugin device being developed - } - - #region Overrides of EssentialsBridgeableDevice - - /// - /// Links the plugin device to the EISC bridge - /// - /// - /// - /// - /// - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = new EssentialsPluginBridgeJoinMapTemplate(joinStart); - - // This adds the join map to the collection on the bridge - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - - var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); - - if (customJoins != null) - { - joinMap.SetCustomJoinData(customJoins); - } - - Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(0, "Linking to Bridge Type {0}", GetType().Name); - - // TODO [ ] Implement bridge links as needed - - // links to bridge - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - - trilist.OnlineStatusChange += (o, a) => - { - if (!a.DeviceOnLine) return; - - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - }; - } - - #endregion - } -} \ No newline at end of file diff --git a/README.md b/README.md index 4ee19ac..c88e298 100644 --- a/README.md +++ b/README.md @@ -1,264 +1,164 @@ -# Internal Essentials Plugin Template (c) 2020 - -## License - -Provided under MIT license - -## Overview - -Use this repo as a template when creating a new plugin for Essentials. For more information about plugins, refer to the Essentials Wiki [Plugins](https://github.com/PepperDash/Essentials/wiki/Plugins) article. - -## Nuget - -You must have nuget.exe installed and in the PATH environment variable to use the following command. Nuget.exe is available at nuget.org. It is recommended to use [Scoop](https://scoop.sh/) to install nuget using the command: - -``` -scoop install nuget -``` - -### Manually Installing Dependencies - -To install dependencies once nuget.exe is installed, after cloning the template or creating your template repo, run the following command: - -``` -nuget install .\packages.config -OutputDirectory .\packages -excludeVersion -``` - -Verify you are using the proper "\\" or "/" per the console used. To verify that the packages installed correctly, open Essentials and make sure that all references are found, then try and build it. **This issue will be found when using WSL on Windows10.** - -Once the nuget package has been installed locally you will need to update your project references. -1. Right click on **References** -2. Select **Add Reference** -3. Browse to the **packages** folder -4. Select the required references. - -### Installing Different versions of PepperDash Essentials - -If you need a different version of PepperDash Essentials, use the command: -``` -nuget install PepperDashEssentials -OutputDirectory .\packages -excludeVersion -Version {versionToGet} -``` - -Omitting the **-Version** option will pull the latest version available. - -## Github Actions - -Github Action workflow Templates will build this project automatically. Any branches named `feature/*`, `release/*`, `hotfix/*` or `development` will automatically be built with the action and create a release in the repository with a version number based on the latest release on the main branch. If there are no releases yet, the version number will be 0.0.1. The version number will be modified based on what branch triggered the build: - -- `feature` branch builds will be tagged with an `alpha` descriptor, with the Action run appended: `0.0.1-alpha-1` -- `development` branch builds will be tagged with a `beta` descriptor, with the Action run appended: `0.0.1-beta-2` -- `release` branches will be tagged with an `rc` descriptor, with the Action run appended: `0.0.1-rc-3` -- `hotfix` branch builds will be tagged with a `hotfix` descriptor, with the Action run appended: `0.0.1-hotfix-4` - -Builds on the Main branch will ONLY be triggered by manually creating a release using the web interface in the repository. They will be versioned with the tag that is created when the release is created. The tags MUST take the form `major.minor.revision` to be compatible with the build process. A tag like `v0.1.0-alpha` is NOT compatabile and may result in the build process failing. - -To trigger a Main branch build follow the steps: -1. Click ***Releases*** on the left of the repo file window. -2. On the Releases page, click the ***Draft*** a new release" button on the right. -3. Enter a ***Tag version*** in the form `major.minor`. -4. Select the ***Target***, typically this will be the `Main` branch. -5. Enter a ***Release title***, typically this will be the same as the ***Tag version***. -6. Click ***Publish Release*** - -## Intial steps to build a plugin - -When building a plugin the following steps can be followed to get you up and running quickly. The steps below assume you have cloned the template repo and installed the necessary NuGet packages. - -1. In GitHub click the ***Use This Template*** to create a new repo from the template. -2. In GitHub click the ***Actions*** tab at the top of the repo created. -3. From GitHub ***Actions*** page, click ***New Workflow*** to setup build actions. -4. From GitHub ***Actions Workflow Template*** page, find ***Workflows created by PepperDash-Engineering***, there should be 2 actions: - - **Essentials Plugins Beta Builds Workflow** - - **Essentials Plugins Release Builds Workflow**. -5. Click ***Set up this workflow*** for both workflow actions -6. Clone the newly created template repo to begin working locally. -7. Rename the ***EssentialsPluginTemplate*** folder to represent the plugin being built -8. Rename the ****.sln*** and the ****.nuspec*** files to represent the plugin being built -9. Install Essentials as a Nuget package. - - You can click on the ***GetPackages.bat*** file to automate installation of the nuget packages. -10. Update the ***.nuspec*** file. The file contains comments providing additional directions on what updates are required. -11. Open the solution and resolve the reference issues - - Right click on ***References*** - - Select ***Add References*** - - Select ***Browse*** - - Navigate the the ***packages*** folder created when installing the NuGet packages - - Select the necessary ****.dll's*** to resolve all reference warnings -12. Review the ***EssentialsPluginFactoryTemplate.cs*** and remove the unused classes and associated ****.cs*** files from the solution. -13. Rename the classes to represent the device plugin being built. - - ***Plugin Template*** - ``` - EssentialsPluginFactoryTemplate.cs - ``` - ***Plugin Example*** - ``` - TacoTuesdayCalculatorFactory.cs - ``` -14. Follow the ***TODO [ ]*** comments found within the template solution. -15. Update the readme.md information below to help document your plugin. -16. When development is complete, commit the changes and push back to GitHub. - - - -## Device Specific Information - -Update the below readme as needed to support documentation of the plugin - -### Communication Settings - -Update the communication settings as needed for the plugin being developed. - -| Setting | Value | -|--------------|-------------| -| Baud rate | 9600 | -| Data bits | 8 | -| Stop bits | 1 | -| Parity | None | -| Flow Control | None | -| Delimiter | "\r" | -| Default IP | 169.254.1.1 | -| Default Port | 23 | -| Username | admin | -| Password | password | - -#### Plugin Valid Communication methods - -Reference PepperDash Core eControlMethods Enum for valid values, (currently listed below). Update to reflect the valid values for the plugin device being developed. - -```c# -Cec -Comm -Cresnet -IpId -IpIdTcp -IR -None -Ssh -Tcpip -Telnet -Udp -``` - -##### Communication Method Note - ***DELETE WHEN UPDATING PLUGIN README*** - -As of PepperDash Core 1.0.41, HTTP and HTTPS are not valid control mehtods and will throw an exception in the plugin factory if not properly handled. The plugin template is currently setup to check the method before attempting to create the comms for the device. When using HTTP or HTTPS you will need to create a custom comm object and modify the constructor as needed. - -For reference see the [Watt Box PDU Plugin](https://github.com/PepperDash-Engineering/epi-pdu-wattbox) as a working example of implementing both HTTP and standard socket communications. - -### Plugin Configuration Object - -Update the configuration object as needed for the plugin being developed. - -```json -{ - "devices": [ - { - "key": "essentialsPluginKey", - "name": "Essentials Plugin Name", - "type": "essentialsPluginTypeName", - "group": "pluginDevices", - "properties": { - "control": { - "method": "See PepperDash.Core.eControlMethod for valid control methods", - "controlPortDevKey": "exampleControlPortDevKey", - "controlPortNumber": 1, - "comParams": { - "baudRate": 9600, - "dataBits": 8, - "stopBits": 1, - "parity": "None", - "protocol": "RS232", - "hardwareHandshake": "None", - "softwareHandshake": "None" - }, - "tcpSshProperties": { - "address": "172.22.0.101", - "port": 22, - "username": "admin", - "password": "password", - "autoReconnect": true, - "autoReconnectIntervalMs": 10000 - } - }, - "pollTimeMs": 30000, - "warningTimeoutMs": 180000, - "errorTimeoutMs": 300000, - "pluginCollection": { - "item1": { - "name": "Item 1", - "value": 1 - }, - "item2": { - "name": "Item 2", - "value": 2 - } - } - } - } - ] -} -``` - -### Plugin Bridge Configuration Object - -Update the bridge configuration object as needed for the plugin being developed. - -```json -{ - "devices": [ - { - "key": "essentialsPluginBridgeKey", - "name": "Essentials Plugin Bridge Name", - "group": "api", - "type": "eiscApiAdvanced", - "properties": { - "control": { - "ipid": "1A", - "tcpSshProperties": { - "address": "127.0.0.2", - "port": 0 - } - }, - "devices": [ - { - "deviceKey": "essentialsPluginKey", - "joinStart": 1 - } - ] - } - } - ] -} -``` - -### SiMPL EISC Bridge Map - -The selection below documents the digital, analog, and serial joins used by the SiMPL EISC. Update the bridge join maps as needed for the plugin being developed. - -#### Digitals -| dig-o (Input/Triggers) | I/O | dig-i (Feedback) | -|---------------------------------------|-----|------------------| -| | 1 | Is Online | -| Connect (Held) / Disconnect (Release) | 2 | Connected | -| | 3 | | -| | 4 | | -| | 5 | | -#### Analogs -| an_o (Input/Triggers) | I/O | an_i (Feedback) | -|-----------------------|-----|-----------------| -| | 1 | Socket Status | -| | 2 | Monitor Status | -| | 3 | | -| | 4 | | -| | 5 | | - - -#### Serials -| serial-o (Input/Triggers) | I/O | serial-i (Feedback) | -|---------------------------|-----|---------------------| -| | 1 | Device Name | -| | 2 | | -| | 3 | | -| | 4 | | -| | 5 | | - +![PepperDash Logo](/images/logo_pdt_no_tagline_600.png) + +# Acuity Fresco Lighting Plugin + +## Device Specific Information + +Update the below readme as needed to support documentation of the plugin + +### Communication Settings + +Update the communication settings as needed for the plugin being developed. + +| Setting | Value | +| ------------ | ------- | +| Baud rate | 115,200 | +| Data bits | 8 | +| Stop bits | 1 | +| Parity | None | +| Flow Control | None | +| Delimiter | "\n" | + +#### Plugin Valid Communication methods + +```c# +Com +``` + +### Plugin Configuration Object + +Update the configuration object as needed for the plugin being developed. + +```json +{ + "devices": [ + { + "key": "lights-1", + "name": "Acuity Fresco Lighting Scenes", + "type": "acuityfresco", + "group": "pluginDevices", + "properties": { + "control": { + "method": "com", + "controlPortDevKey": "processor", + "controlPortNumber": 1, + "comParams": { + "baudRate": 115200, + "dataBits": 8, + "stopBits": 1, + "parity": "None", + "protocol": "RS232", + "hardwareHandshake": "None", + "softwareHandshake": "None" + } + }, + "pollTimeMs": 30000, + "warningTimeoutMs": 180000, + "errorTimeoutMs": 300000, + "scenes": [ + { + "name": "Scene 1", + "id": 1, + "roomId": "A", + "level": 100 + }, + { + "name": "Scene 2", + "id": 5, + "roomId": "B", + "level": 50 + }, + { + "name": "Scene 3", + "id": 36, + "roomId": "X", + "level": 0 + } + ] + } + } + ] +} +``` + +### Plugin Bridge Configuration Object + +Update the bridge configuration object as needed for the plugin being developed. + +```json +{ + "devices": [ + { + "key": "lights-1-bridge", + "uid": 11, + "name": "Example Plugin Bridge", + "group": "api", + "type": "eiscApiAdvanced", + "properties": { + "control": { + "tcpSshProperties": { + "address": "127.0.0.2", + "port": 0 + }, + "ipid": "B1" + }, + "devices": [ + { + "deviceKey": "lights-1", + "joinStart": 1 + } + ] + } + } + ] +} +``` + +### SiMPL EISC Bridge Map + +#### Digitals + +| dig-o (Input/Triggers) | I/O | dig-i (Feedback) | +| ---------------------- | --- | ------------------------- | +| | 1 | Is Online | +| Scene 1 Select | 11 | Scene 1 Feedback | +| Scene 2 Select | 12 | Scene 2 Feedback | +| Scene 3 Select | 13 | Scene 3 Feedback | +| Scene 4 Select | 14 | Scene 4 Feedback | +| Scene 5 Select | 15 | Scene 5 Feedback | +| Scene 6 Select | 16 | Scene 6 Feedback | +| Scene 7 Select | 17 | Scene 7 Feedback | +| Scene 8 Select | 18 | Scene 8 Feedback | +| Scene 9 Select | 19 | Scene 9 Feedback | +| Scene 10 Select | 20 | Scene 10 Feedback | +| | 41 | Scene 1 Visible Feedback | +| | 42 | Scene 2 Visible Feedback | +| | 43 | Scene 3 Visible Feedback | +| | 44 | Scene 4 Visible Feedback | +| | 45 | Scene 5 Visible Feedback | +| | 46 | Scene 6 Visible Feedback | +| | 47 | Scene 7 Visible Feedback | +| | 48 | Scene 8 Visible Feedback | +| | 49 | Scene 9 Visible Feedback | +| | 50 | Scene 10 Visible Feedback | + +#### Analogs + +| an_o (Input/Triggers) | I/O | an_i (Feedback) | +| --------------------- | --- | -------------------- | +| Select Scene by Index | 1 | Scene Index Feedback | + +#### Serials + +| serial-o (Input/Triggers) | I/O | serial-i (Feedback) | +| ----------------------------- | --- | ------------------- | +| Integration ID Set (NOT USED) | 1 | | + +### DEVJSON Commands + +```plaintext +devjson:1 {"deviceKey":"lights-1", "methodName":"GetScenes", "params":[]} + +devjson:1 {"deviceKey":"lights-1", "methodName":"ResetDebugLevels", "params":[]} +devjson:1 {"deviceKey":"lights-1", "methodName":"SetDebugLevels", "params":[{level 0-2}]} +``` diff --git a/EssentialsPluginTemplate/configurationFile.json b/configurationFile.json similarity index 68% rename from EssentialsPluginTemplate/configurationFile.json rename to configurationFile.json index c5027c1..119d011 100644 --- a/EssentialsPluginTemplate/configurationFile.json +++ b/configurationFile.json @@ -1,94 +1,96 @@ -{ - "system_url": "", - "template": { - "info": { - "comment": "", - "requiredControlSofwareVersion": "", - "systemType": "huddle", - "lastModifiedDate": "2018-07-09T20:00:47.873Z", - "lastUid": 23, - "processorType": "rmc3" - }, - "devices": [ - { - "key": "processor", - "group": "processor", - "uid": 0, - "supportsCompliance": true, - "type": "rmc3", - "properties": {}, - "name": "RMC3" - }, - { - "key": "example-plugin-1", - "name": "Example Plugin Name", - "type": "examplePluginDevice", - "group": "pluginDevices", - "properties": { - "control": { - "method": "tcpIp", - "controlPortDevKey": "processor", - "controlPortNumber": 1, - "comParams": { - "baudRate": 9600, - "dataBits": 8, - "stopBits": 1, - "parity": "None", - "protocol": "RS232", - "hardwareHandshake": "None", - "softwareHandshake": "None" - }, - "tcpSshProperties": { - "address": "10.0.0.200", - "port": 23, - "username": "admin", - "password": "password", - "autoReconnect": true, - "autoReconnectIntervalMs": 10000 - } - }, - "pollTimeMs": 30000, - "warningTimeoutMs": 180000, - "errorTimeoutMs": 300000, - "pluginCollection": { - "1": { - "name": "Item 1", - "value": 1 - }, - "2": { - "name": "Item 2", - "value": 2 - } - } - } - }, - { - "key": "example-plugin-bridge-1", - "uid": 11, - "name": "Example Plugin Bridge", - "group": "api", - "type": "eiscApiAdvanced", - "properties": { - "control": { - "tcpSshProperties": { - "address": "127.0.0.2", - "port": 0 - }, - "ipid": "B1" - }, - "devices": [ - { - "deviceKey": "example-plugin-1", - "joinStart": 1 - } - ] - } - } - ], - "rooms": [], - "sourceLists": {}, - "tieLines": [] - }, - "template_url": "", - "system": {} +{ + "system_url": "", + "template": { + "info": { + "comment": "", + "requiredControlSofwareVersion": "", + "systemType": "huddle", + "lastModifiedDate": "2018-07-09T20:00:47.873Z", + "lastUid": 23, + "processorType": "rmc3" + }, + "devices": [ + { + "key": "processor", + "group": "processor", + "uid": 0, + "supportsCompliance": true, + "type": "rmc3", + "properties": {}, + "name": "RMC3" + }, + { + "key": "lights-1", + "name": "Acuity Fresco Lighting Scenes", + "type": "acuityfresco", + "group": "pluginDevices", + "properties": { + "control": { + "method": "com", + "controlPortDevKey": "processor", + "controlPortNumber": 1, + "comParams": { + "baudRate": 115200, + "dataBits": 8, + "stopBits": 1, + "parity": "None", + "protocol": "RS232", + "hardwareHandshake": "None", + "softwareHandshake": "None" + } + }, + "pollTimeMs": 30000, + "warningTimeoutMs": 180000, + "errorTimeoutMs": 300000, + "scenes": [ + { + "name": "Scene 1", + "id": 1, + "roomId": "A", + "level": 100 + }, + { + "name": "Scene 2", + "id": 5, + "roomId": "B", + "level": 50 + }, + { + "name": "Scene 3", + "id": 36, + "roomId": "X", + "level": 0 + } + ] + } + }, + { + "key": "lights-1-bridge", + "uid": 11, + "name": "Example Plugin Bridge", + "group": "api", + "type": "eiscApiAdvanced", + "properties": { + "control": { + "tcpSshProperties": { + "address": "127.0.0.2", + "port": 0 + }, + "ipid": "B1" + }, + "devices": [ + { + "deviceKey": "lights-1", + "joinStart": 1 + } + ] + } + } + ], + "rooms": [], + "sourceLists": {}, + "tieLines": [] + }, + "template_url": "", + "system": {} } \ No newline at end of file diff --git a/fcs-avi-interface-commands.pdf b/fcs-avi-interface-commands.pdf new file mode 100644 index 0000000000000000000000000000000000000000..429740c3cdba4709b1e596c063eec5b034511107 GIT binary patch literal 252609 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≀45^%xsK|K5$cf?W18hD@fgGNXo5QhL>4Pt;m8w#KlVEG+U6HhAy5WYqL zK{*rx4X^{|Kj?)Ias2OgBpL&}<@ot-LN3fkYurvBN#`?dFx$pa=ew(=woyyPi^a&18;!4T=M6b%nmlYweK=0OFecsT zsEcV4Br_O&E>&3?y}40QJ9*Q7BakT;q;9fYN}9*NKLDDyn;)1AQO`3pP^^$ikBl_$ zzxr&zl+1M@x@?4_MJSw`AXvGuS`+3C72lq%poCsg9-gg(;`b+`uCWdx%4wmn)iRLmnOXX zUi&!MqdqAyExi>c6Rq8K{^al(CJI{J4e?RQ(sA2UMxm;K*0t7GYKGY(gw0rix4kQMG7hHuw=}J zlh%^elm^ag8p|fQWf#q{`4JOdp-&(@;p4=9vrIz!@Hln&m~CZ!t7gbE)ye|d$Ddgk zV0P$FRg;n|-O17U7Ix@qP(Lg{*``I{>dVhZ+0hP8+Bc55eOF2?{HxgLLQnE(oaBx&UnkAZ{vE` znB|_SDt(mB&T+u!ER(2$8}zt)`%xidcKj~KaZ%JAqna2hl4P{ll-<7@L~)&GlxLg5 zbn7G(#Mbt3zw2V)P8Qt@`u5ndQ|d_g$$iDT2p!j;H&+h)N{GlVkV*x9o+s^WI_Ey@ zp?j90w~{g?CsrGY#qF(Xq&1oEqJtG3sE8Xp1vA~$WCHADYvB;*JVzax1KxX3yi3zH zRvD3OxB5}V;C$yyIBQjv_eq*wfyTXsS33@SLsEW4im-OG)uQ+B7xttvzIz-KBGsOs zKaX#|U0zNUW}VFCjlt0Fzx=$_Y68`Aruts-?dMVN zhn@*N0Mn-GHk@atwZH7U#l|9FaC2;uF?8+ArS5qDvO9XxKC{!I(`D${a{)uSZ~UV^ zr*acK!F|{<8fWPS?+Z!SX=V&OvrTt_w_4n`dB|VlZaTzV#_`}3;zn1?J?+b91y1m0 zwts~kn3ujS(~q;g7e~lz)3Rf8FDH5+9W7xtPMkPt_9W!hIoV=r)Ywver@=BduV|%^ z6Gy;q=AMh3oJzn*9ei zZDg{OMIYu(5xqZzh$lQaE_9MqwL(W>UVGl;q0XVn(}u!U(lk^X^;VYJvV>V#%E+dP zI%8FIfAQ^PUhF|et!YymTs?l&?R6Ii@uSLYHaB4|)JunMFl-$jtC?AB9HYzZPiqBGzE;R;g_z)OS*5)f zIwywgdLG|+MK5B~RV-(GjeC-F)@H}riLsj`sjw`LPD4$Xw=YXu0~38VVGtFdd|&-| zn<^Dz)y9WOMVpxUjq`rOlGJ *TD+X&{*e(-3Fuw_emE@zEQCD9u6P$Mdu=(6~ow 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 z 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 v9tKhW5PbWu<)4 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

2sLjZ+64;=KYp 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 - + + \ No newline at end of file diff --git a/src/AcuityFrescoBridgeJoinMap.cs b/src/AcuityFrescoBridgeJoinMap.cs new file mode 100644 index 0000000..a3c949d --- /dev/null +++ b/src/AcuityFrescoBridgeJoinMap.cs @@ -0,0 +1,72 @@ +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDashPluginAcuityFresco +{ + ///

+ /// Plugin device Bridge Join Map + /// + public class AcuityFrescoBridgeJoinMap : GenericLightingJoinMap + { + /* + GenericLightingJoinMap + + [JoinName("IsOnline")] + * JoinNumber = 1 + * JoinSpan = 1 + * JoinCapabilities = eJoinCapabilities.ToSIMPL + * JoinType = eJoinType.Digital + + [JoinName("SelectScene")] + * JoinNumber = 1 + * JoinSpan = 1 + * JoinCapabilities = eJoinCapabilities.FromSIMPL + * JoinType = eJoinType.Digital + + [JoinName("SelectSceneDirect")] + * JoinNumber = 11 + * JoinSpan = 10 + * JoinCapabilities = eJoinCapabilities.ToFromSIMPL + * JoinType = eJoinType.DigitalSerial + + [JoinName("ButtonVisibility")] + * JoinNumber = 41 + * JoinSpan = 10 + * JoinCapabilities = eJoinCapabilities.ToSIMPL + * JoinType = eJoinType.Digital + + ** NOT USED ** + [JoinName("IntegrationIdSet")] + * JoinNumber = 1 + * JoinSpan = 1 + * JoinCapabilities = eJoinCapabilities.FromSIMPL + * JoinType = eJoinType.Serial + */ + + #region Digital + + + + #endregion + + + #region Analog + + + #endregion + + + #region Serial + + + #endregion + + /// + /// Plugin device BridgeJoinMap constructor + /// + /// This will be the join it starts on the EISC bridge + public AcuityFrescoBridgeJoinMap(uint joinStart) + : base(joinStart, typeof(AcuityFrescoBridgeJoinMap)) + { + } + } +} diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs new file mode 100644 index 0000000..ee5adc9 --- /dev/null +++ b/src/AcuityFrescoDevice.cs @@ -0,0 +1,273 @@ +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Lighting; + + +namespace PepperDashPluginAcuityFresco +{ + /// + /// Plugin device + /// + public class AcuityFrescoDevice : LightingBase + { + private const string CommsDelimiter = "\n"; + private readonly List _scenes; + private readonly IBasicCommunication _comms; + private readonly GenericCommunicationMonitor _commsMonitor; + + /// + /// Plugin device constructor + /// + /// device key + /// device name + /// device configuration object + /// device communication as IBasicCommunication + /// + /// + public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig config, IBasicCommunication comms) + : base(key, name) + { + Debug.Console(TraceLevel, this, "Constructing new {0} instance", name); + + _scenes = config.Scenes; + + _comms = comms; + _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); + + var commsGather = new CommunicationGather(_comms, CommsDelimiter); + commsGather.LineReceived += Handle_LineRecieved; + + Debug.Console(TraceLevel, this, "Constructing new {0} instance complete", name); + Debug.Console(TraceLevel, new string('*', 80)); + Debug.Console(TraceLevel, new string('*', 80)); + } + + /// + /// Use the custom activiate to connect the device and start the comms monitor. + /// This method will be called when the device is built. + /// + /// + public override bool CustomActivate() + { + // Essentials will handle the connect method to the device + _comms.Connect(); + // Essentialss will handle starting the comms monitor + _commsMonitor.Start(); + + return base.CustomActivate(); + } + + // commonly used with ASCII based API's with a defined delimiter + private void Handle_LineRecieved(object sender, GenericCommMethodReceiveTextArgs args) + { + if (args == null || string.IsNullOrEmpty(args.Text)) + { + Debug.Console(DebugLevel, this, "Handle_LineReceived args is null or args.Text is null or empty"); + return; + } + + Debug.Console(DebugLevel, this, "Handle_LineReceived args.Text: {0}", args.Text); + + // TODO [ ] Process device response + } + + /// + /// Sends text to the device plugin comms + /// + /// + /// 'scene {Scene ID} {Level} [0 {room ID}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void SendText(string text) + { + if (string.IsNullOrEmpty(text)) return; + + var cmd = string.IsNullOrEmpty(CommsDelimiter) + ? string.Format("{0}", text) + : string.Format("{0}{1}", text, CommsDelimiter); + + _comms.SendText(cmd); + } + + /// + /// Polls the device scene status for all + /// + /// + /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void Poll() + { + Poll(0, null); + } + + /// + /// Polls the device scene status for the scene ID + /// + /// + /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void Poll(uint index) + { + Poll(index, null); + } + + /// + /// Polls the device scene status for the scene ID and room ID + /// + /// + /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void Poll(uint index, string roomId) + { + if (index == 0 && string.IsNullOrEmpty(roomId)) + { + SendText("status scene ALL"); + } + else + { + SendText(string.IsNullOrEmpty(roomId) + ? string.Format("status scene {0}", index) + : string.Format("status scene {0} 0 {1}", index, roomId)); + } + } + + #region Overrides of EssentialsBridgeableDevice + + /// + /// Links the plugin device to the EISC bridge + /// + /// + /// + /// + /// + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + LinkLightingToApi(this, trilist, joinStart, joinMapKey, bridge); + } + + #endregion Overrides of EssentialsBridgeableDevice + + /// + /// Scene select + /// + /// + /// 'scene {Scene ID, 1-36} {Level, 0-100%} [0 {room ID, A-X}]' + /// '{}' are required params + /// '[]' are optional params + /// + public override void SelectScene(LightingScene scene) + { + if (scene == null) + { + Debug.Console(DebugLevel, this, "scene is null"); + return; + } + + var s = scene as AcuityFrescoScene; + if(s == null) return; + + var cmd = (string.IsNullOrEmpty(s.RoomId)) + ? string.Format("scene {0} {1}", s.ID, s.Level) + : string.Format("scene {0} {1} {2}", s.ID, s.Level, s.RoomId); + + SendText(cmd); + } + + /// + /// Prints the list of scenes to console + /// + /// + /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"GetScenes", "params":[]} + /// + public void GetScenes() + { + Debug.Console(TraceLevel, this, new string('*', 80)); + Debug.Console(TraceLevel, this, "Scene List:"); + var index = 0; + foreach (var scene in _scenes) + { + Debug.Console(TraceLevel, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", index, scene.ID, scene.Level, scene.RoomId); + index ++; + } + + Debug.Console(TraceLevel, this, new string('*', 80)); + } + + + #region DebugLevels + + /// + /// Trace level (0) + /// + public uint TraceLevel = 0; + + /// + /// Debug level (1) + /// + public uint DebugLevel = 1; + + /// + /// Error Level (2) + /// + public uint ErrorLevel = 2; + + private CTimer _debugTimer; + private bool _debugTimerActive; + + /// + /// Resets debug levels for this device instancee + /// + /// + /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"ResetDebugLevels", "params":[]} + /// + public void ResetDebugLevels() + { + TraceLevel = 0; + DebugLevel = 1; + ErrorLevel = 2; + + if (_debugTimerActive) + _debugTimer.Stop(); + + if(!_debugTimer.Disposed) + _debugTimer.Dispose(); + + _debugTimerActive = _debugTimer != null; + } + + /// + /// Sets the debug levels for this device instance + /// + /// + /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"SetDebugLevels", "params":[{level, 0-2}]} + /// + /// + public void SetDebugLevels(uint level) + { + TraceLevel = level; + DebugLevel = level; + ErrorLevel = level; + + if(_debugTimer == null) + _debugTimer = new CTimer(dt => ResetDebugLevels(), 900000); // 900,000 = 15-mins + else + _debugTimer.Reset(); + + _debugTimerActive = _debugTimer != null; + } + + #endregion + } +} + diff --git a/src/AcuityFrescoFactory.cs b/src/AcuityFrescoFactory.cs new file mode 100644 index 0000000..1c2ff0b --- /dev/null +++ b/src/AcuityFrescoFactory.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDashPluginAcuityFresco +{ + /// + /// Plugin factory for devices that require communications using IBasicCommunications or custom communication methods + /// + public class AcuityFrescoFactory : EssentialsPluginDeviceFactory + { + /// + /// Plugin device factory constructor + /// + public AcuityFrescoFactory() + { + // Set the minimum Essentials Framework Version + MinimumEssentialsFrameworkVersion = "1.9.7"; + + // In the constructor we initialize the list with the typenames that will build an instance of this device + // only include unique typenames, when the constructur is used all the typenames will be evaluated in lower case. + TypeNames = new List() { "acuityfresco" }; + } + + /// + /// Builds and returns an instance of AcuityFrescoDevice + /// + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + try + { + Debug.Console(2, new string('*', 80)); + Debug.Console(2, new string('*', 80)); + Debug.Console(0, "[{0}] Factory Attempting to create new device from type: {1}", dc.Key, dc.Type); + + // get the plugin device properties configuration object & check for null + var propertiesConfig = dc.Properties.ToObject(); + if (propertiesConfig == null) + { + Debug.Console(0, "[{0}] Factory: failed to read properties config for {1}", dc.Key, dc.Name); + return null; + } + + // build the plugin device comms (for all other comms methods) & check for null + var comms = CommFactory.CreateCommForDevice(dc); + if (comms != null) return new AcuityFrescoDevice(dc.Key, dc.Name, propertiesConfig, comms); + Debug.Console(0, "[{0}] Factory: failed to create comm for {1}", dc.Key, dc.Name); + return null; + } + catch (Exception ex) + { + Debug.Console(0, "[{0}] Factory BuildDevice Exception: {1}", dc.Key, ex); + return null; + } + } + } +} \ No newline at end of file diff --git a/src/AcuityFrescoPropertiesConfig.cs b/src/AcuityFrescoPropertiesConfig.cs new file mode 100644 index 0000000..0a7f8c3 --- /dev/null +++ b/src/AcuityFrescoPropertiesConfig.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Lighting; + +namespace PepperDashPluginAcuityFresco +{ + /// + /// Plugin device configuration object + /// + [ConfigSnippet("{\"devices\":[]}")] + public class AcuityFrescoPropertiesConfig + { + /// + /// JSON control object + /// + [JsonProperty("control")] + public EssentialsControlPropertiesConfig Control { get; set; } + + /// + /// Serializes the poll time value + /// + [JsonProperty("pollTimeMs")] + public long PollTimeMs { get; set; } + + /// + /// Serializes the warning timeout value + /// + [JsonProperty("warningTimeoutMs")] + public long WarningTimeoutMs { get; set; } + + /// + /// Serializes the error timeout value + /// + [JsonProperty("errorTimeoutMs")] + public long ErrorTimeoutMs { get; set; } + + /// + /// Scenes + /// + [JsonProperty("scenes")] + public List Scenes { get; set; } + } + + /// + /// + /// + public class AcuityFrescoScene : LightingScene + { + /// + /// Room ID used for scene recall + /// + [JsonProperty("roomId")] + public string RoomId { get; set; } + + /// + /// Optional - can be used to recall a scene with the 0-100% state + /// + [JsonProperty("level")] + public uint Level { get; set; } + } +} \ No newline at end of file diff --git a/EssentialsPluginTemplate/EssentialsPluginTemplate.csproj b/src/PepperDashPluginAcuityFresco.csproj similarity index 88% rename from EssentialsPluginTemplate/EssentialsPluginTemplate.csproj rename to src/PepperDashPluginAcuityFresco.csproj index ffdcdac..7d9ccaf 100644 --- a/EssentialsPluginTemplate/EssentialsPluginTemplate.csproj +++ b/src/PepperDashPluginAcuityFresco.csproj @@ -7,8 +7,8 @@ {9D249E47-8F95-4437-A6BB-563510287AD1} Library Properties - EssentialsPluginTemplate - EssentialsPluginTemplate + PepperDashPluginAcuityFresco + PepperDashPluginAcuityFresco {0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} WindowsCE E2BECB1F-8C8C-41ba-B736-9BE7D946A398 @@ -32,7 +32,7 @@ true true off - bin\Debug\EssentialsPluginTemplate.xml + bin\Debug\PepperDashPluginAcuityFresco.xml .allowedReferenceRelatedFileExtensions @@ -84,7 +84,7 @@ False - ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + ..\packages\PepperDashEssentials\lib\net35\SimplSharpNewtonsoft.dll False @@ -100,12 +100,10 @@ - - - - - - + + + + diff --git a/EssentialsPluginTemplate/EssentialsPluginTemplate.nuspec b/src/PepperDashPluginAcuityFresco.nuspec similarity index 62% rename from EssentialsPluginTemplate/EssentialsPluginTemplate.nuspec rename to src/PepperDashPluginAcuityFresco.nuspec index 92c44d5..0de10aa 100644 --- a/EssentialsPluginTemplate/EssentialsPluginTemplate.nuspec +++ b/src/PepperDashPluginAcuityFresco.nuspec @@ -2,22 +2,22 @@ - EssentialsPluginTemplate + PepperDash.Essentials.Plugin.Lighting.Acuity.Fresco 1.0.0 - Essentials Plugin Template + PepperDash Essentials Acuity Fresco Lighting Plugin PepperDash Technologies pepperdash false MIT - https://github.com/PepperDash-Engineering/template-essentials_plugin - Copyright 2020 + https://github.com/PepperDash-Engineering/ + Copyright 2022 - Essentials Plugin Template is a Crestron SIMPL# plugin used with SIMPL# Pro applications such as Essentials to expose functionality in SIMPL Windows programs using Bridges. + PepeprDash Essentials Acuity Fresco Lighting Plugin to expose functionality in SIMPL Windows programs using Bridges. crestron 3series 4series - + diff --git a/EssentialsPluginTemplate/EssentialsPluginTemplate.sln b/src/PepperDashPluginAcuityFresco.sln similarity index 82% rename from EssentialsPluginTemplate/EssentialsPluginTemplate.sln rename to src/PepperDashPluginAcuityFresco.sln index 3919262..bd067c9 100644 --- a/EssentialsPluginTemplate/EssentialsPluginTemplate.sln +++ b/src/PepperDashPluginAcuityFresco.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EssentialsPluginTemplate", "EssentialsPluginTemplate.csproj", "{9D249E47-8F95-4437-A6BB-563510287AD1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDashPluginAcuityFresco", "PepperDashPluginAcuityFresco.csproj", "{9D249E47-8F95-4437-A6BB-563510287AD1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/EssentialsPluginTemplate/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs similarity index 62% rename from EssentialsPluginTemplate/Properties/AssemblyInfo.cs rename to src/Properties/AssemblyInfo.cs index d80b3c1..0d8b029 100644 --- a/EssentialsPluginTemplate/Properties/AssemblyInfo.cs +++ b/src/Properties/AssemblyInfo.cs @@ -1,8 +1,8 @@ using System.Reflection; -[assembly: AssemblyTitle("EssentialsPluginTemplate")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("EssentialsPluginTemplate")] +[assembly: AssemblyTitle("PepperDashPluginAcuityFresco")] +[assembly: AssemblyCompany("PepperDash")] +[assembly: AssemblyProduct("PepperDashPluginAcuityFresco")] [assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyVersion("1.0.0.*")] [assembly: AssemblyInformationalVersion("0.0.0-buildType-build#")] diff --git a/EssentialsPluginTemplate/Properties/ControlSystem.cfg b/src/Properties/ControlSystem.cfg similarity index 100% rename from EssentialsPluginTemplate/Properties/ControlSystem.cfg rename to src/Properties/ControlSystem.cfg From 2a980699930d564ad4af279315fb14deb78f61b2 Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Wed, 20 Apr 2022 16:32:15 -0500 Subject: [PATCH 02/16] fix: updated nuspec file with repo URL --- src/PepperDashPluginAcuityFresco.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PepperDashPluginAcuityFresco.nuspec b/src/PepperDashPluginAcuityFresco.nuspec index 0de10aa..8686913 100644 --- a/src/PepperDashPluginAcuityFresco.nuspec +++ b/src/PepperDashPluginAcuityFresco.nuspec @@ -11,13 +11,13 @@ false MIT - https://github.com/PepperDash-Engineering/ + https://github.com/PepperDash-Engineering/epi-lighting-acuity-freso Copyright 2022 PepeprDash Essentials Acuity Fresco Lighting Plugin to expose functionality in SIMPL Windows programs using Bridges. crestron 3series 4series - + From 9a44705ed35f38be78c13d0d7325221e7c099324 Mon Sep 17 00:00:00 2001 From: jdevito Date: Thu, 21 Apr 2022 09:17:13 -0500 Subject: [PATCH 03/16] Update PepperDashPluginAcuityFresco.nuspec Fixed type. --- src/PepperDashPluginAcuityFresco.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PepperDashPluginAcuityFresco.nuspec b/src/PepperDashPluginAcuityFresco.nuspec index 8686913..261eff4 100644 --- a/src/PepperDashPluginAcuityFresco.nuspec +++ b/src/PepperDashPluginAcuityFresco.nuspec @@ -14,7 +14,7 @@ https://github.com/PepperDash-Engineering/epi-lighting-acuity-freso Copyright 2022 - PepeprDash Essentials Acuity Fresco Lighting Plugin to expose functionality in SIMPL Windows programs using Bridges. + PepperDash Essentials Acuity Fresco Lighting Plugin to expose functionality in SIMPL Windows programs using Bridges. crestron 3series 4series @@ -23,4 +23,4 @@ - \ No newline at end of file + From 626f64db16088fbfc07033e82352e7939623bdf9 Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Thu, 5 May 2022 12:44:23 -0500 Subject: [PATCH 04/16] fix: updated nuget source commands in workflow files to reference proper org --- .github/workflows/essentialsplugins-betabuilds.yml | 4 ++-- .github/workflows/essentialsplugins-releasebuilds.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/essentialsplugins-betabuilds.yml b/.github/workflows/essentialsplugins-betabuilds.yml index 9ad00cb..5f1f2db 100644 --- a/.github/workflows/essentialsplugins-betabuilds.yml +++ b/.github/workflows/essentialsplugins-betabuilds.yml @@ -123,7 +123,7 @@ jobs: Write-Output "Unable to apply version to AssemblyInfo.cs files"; } - name: add PepperDash Eng Feed - run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash-engineering/index.json -username ${{ secrets.GH_PACKAGE_USER }} -password ${{ secrets.GH_PACKAGE_PASSWORD }} + run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }} - name: restore Nuget Packages run: nuget install .\packages.config -OutputDirectory .\packages -ExcludeVersion # Set the SOLUTION_PATH @@ -245,7 +245,7 @@ jobs: - name: Add nuget.exe uses: nuget/setup-nuget@v1 - name: Add Github Packages source - run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash-engineering/index.json -username ${{ secrets.GH_PACKAGE_USER }} -password ${{ secrets.GH_PACKAGE_PASSWORD }} + run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }} # Pushes to nuget, not needed unless publishing publicly #- name: Add nuget.org API Key # run: nuget setApiKey ${{ secrets.NUGET_API_KEY }} diff --git a/.github/workflows/essentialsplugins-releasebuilds.yml b/.github/workflows/essentialsplugins-releasebuilds.yml index f5606d9..3972b9b 100644 --- a/.github/workflows/essentialsplugins-releasebuilds.yml +++ b/.github/workflows/essentialsplugins-releasebuilds.yml @@ -78,7 +78,7 @@ jobs: Write-Output "Unable to apply version to AssemblyInfo.cs files"; } - name: add PepperDash Eng Feed - run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash-engineering/index.json -username ${{ secrets.GH_PACKAGE_USER }} -password ${{ secrets.GH_PACKAGE_PASSWORD }} + run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }} - name: restore Nuget Packages run: nuget install .\packages.config -OutputDirectory .\packages -ExcludeVersion # Set the SOLUTION_PATH @@ -190,7 +190,7 @@ jobs: - name: Add nuget.exe uses: nuget/setup-nuget@v1 - name: Add Github Packages source - run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash-engineering/index.json -username ${{ secrets.GH_PACKAGE_USER }} -password ${{ secrets.GH_PACKAGE_PASSWORD }} + run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }} # Pushes to nuget, not needed unless publishing publicly #- name: Add nuget.org API Key # run: nuget setApiKey ${{ secrets.NUGET_API_KEY }} From df37a0cc01abe44e495dc71ffc200e2d60ae687c Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Thu, 5 May 2022 13:12:00 -0500 Subject: [PATCH 05/16] fix: updated nuspec URL reference to correct org --- src/PepperDashPluginAcuityFresco.nuspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PepperDashPluginAcuityFresco.nuspec b/src/PepperDashPluginAcuityFresco.nuspec index 261eff4..d421be6 100644 --- a/src/PepperDashPluginAcuityFresco.nuspec +++ b/src/PepperDashPluginAcuityFresco.nuspec @@ -11,13 +11,13 @@ false MIT - https://github.com/PepperDash-Engineering/epi-lighting-acuity-freso + https://github.com/PepperDash/epi-lighting-acuity-fresco Copyright 2022 PepperDash Essentials Acuity Fresco Lighting Plugin to expose functionality in SIMPL Windows programs using Bridges. crestron 3series 4series - - + + From f90c5d8b83c62c30cfcb7ff640cba7201bf69f26 Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Thu, 5 May 2022 16:41:35 -0500 Subject: [PATCH 06/16] fix: updated _scenes property to public --- src/AcuityFrescoDevice.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index ee5adc9..adb2f7d 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -15,7 +15,7 @@ namespace PepperDashPluginAcuityFresco public class AcuityFrescoDevice : LightingBase { private const string CommsDelimiter = "\n"; - private readonly List _scenes; + public readonly List _scenes; private readonly IBasicCommunication _comms; private readonly GenericCommunicationMonitor _commsMonitor; @@ -153,6 +153,9 @@ public void Poll(uint index, string roomId) /// public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) { + Debug.Console(DebugLevel, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + Debug.Console(DebugLevel, this, "Linking to Bridge Type {0}", GetType().Name); + LinkLightingToApi(this, trilist, joinStart, joinMapKey, bridge); } From 62e4045b9a51200f4f72ea2c979caae6ba5ee6ef Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Thu, 5 May 2022 22:33:54 -0500 Subject: [PATCH 07/16] refactor: removed inheritance of lighting base and implemented logic directly --- src/AcuityFrescoBridgeJoinMap.cs | 103 +++++- src/AcuityFrescoDevice.cs | 477 +++++++++++++++++++--------- src/AcuityFrescoPropertiesConfig.cs | 20 +- 3 files changed, 441 insertions(+), 159 deletions(-) diff --git a/src/AcuityFrescoBridgeJoinMap.cs b/src/AcuityFrescoBridgeJoinMap.cs index a3c949d..036c96c 100644 --- a/src/AcuityFrescoBridgeJoinMap.cs +++ b/src/AcuityFrescoBridgeJoinMap.cs @@ -1,11 +1,12 @@ -using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; namespace PepperDashPluginAcuityFresco { /// /// Plugin device Bridge Join Map /// - public class AcuityFrescoBridgeJoinMap : GenericLightingJoinMap + public class AcuityFrescoBridgeJoinMap : JoinMapBaseAdvanced { /* GenericLightingJoinMap @@ -44,19 +45,113 @@ public class AcuityFrescoBridgeJoinMap : GenericLightingJoinMap #region Digital - + [JoinName("IsOnline")] + public JoinDataComplete IsOnline = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device is online feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("SceneSelectDirect")] + public JoinDataComplete SceneSelectDirect = new JoinDataComplete( + new JoinData + { + JoinNumber = 11, + JoinSpan = 10 + }, + new JoinMetadata + { + Description = "Device direct scene select, feedback, and names", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.DigitalSerial + }); + + [JoinName("SceneButtonVisibility")] + public JoinDataComplete SceneButtonVisibility = new JoinDataComplete( + new JoinData + { + JoinNumber = 41, + JoinSpan = 10 + }, + new JoinMetadata + { + Description = "Device scene button visibility feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); #endregion #region Analog + [JoinName("CommunicationMonitorStatus")] + public JoinDataComplete CommunicationMonitorStatus = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device communication monitor status feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("SocketStatus")] + public JoinDataComplete SocketStatus = new JoinDataComplete( + new JoinData + { + JoinNumber = 2, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device socket status feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Analog + }); + + [JoinName("SceneSelect")] + public JoinDataComplete SceneSelect = new JoinDataComplete( + new JoinData + { + JoinNumber = 10, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device scene select by index with feedback", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Analog + }); #endregion #region Serial - + + [JoinName("DeviceName")] + public JoinDataComplete DeviceName = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Device Name", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Serial + }); #endregion diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index adb2f7d..eca74e1 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Crestron.SimplSharp; using Crestron.SimplSharpPro.DeviceSupport; using PepperDash.Core; @@ -9,103 +10,294 @@ namespace PepperDashPluginAcuityFresco { - /// - /// Plugin device - /// - public class AcuityFrescoDevice : LightingBase - { - private const string CommsDelimiter = "\n"; - public readonly List _scenes; - private readonly IBasicCommunication _comms; - private readonly GenericCommunicationMonitor _commsMonitor; - - /// - /// Plugin device constructor - /// - /// device key - /// device name - /// device configuration object - /// device communication as IBasicCommunication - /// - /// - public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig config, IBasicCommunication comms) - : base(key, name) - { - Debug.Console(TraceLevel, this, "Constructing new {0} instance", name); - - _scenes = config.Scenes; - - _comms = comms; - _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); - - var commsGather = new CommunicationGather(_comms, CommsDelimiter); - commsGather.LineReceived += Handle_LineRecieved; - - Debug.Console(TraceLevel, this, "Constructing new {0} instance complete", name); - Debug.Console(TraceLevel, new string('*', 80)); - Debug.Console(TraceLevel, new string('*', 80)); - } - - /// - /// Use the custom activiate to connect the device and start the comms monitor. - /// This method will be called when the device is built. - /// - /// - public override bool CustomActivate() - { - // Essentials will handle the connect method to the device - _comms.Connect(); - // Essentialss will handle starting the comms monitor - _commsMonitor.Start(); - - return base.CustomActivate(); - } - - // commonly used with ASCII based API's with a defined delimiter - private void Handle_LineRecieved(object sender, GenericCommMethodReceiveTextArgs args) - { - if (args == null || string.IsNullOrEmpty(args.Text)) - { + /// + /// Plugin device + /// + public class AcuityFrescoDevice : EssentialsBridgeableDevice + { + private const string CommsDelimiter = "\n"; + private readonly IBasicCommunication _comms; + private readonly GenericCommunicationMonitor _commsMonitor; + + /// + /// Communication status monitor + /// + public StatusMonitorBase CommunicationMonitor { get { return _commsMonitor; } } + + /// + /// Online feedback + /// + public BoolFeedback OnlineFeedback { get; private set; } + + /// + /// Communication monitor feedback + /// + public IntFeedback CommunicationMonitorFeedback { get; private set; } + + /// + /// Socket status feedback + /// + public IntFeedback SocketStatusFeedback { get; private set; } + + /// + /// List of configured scenes + /// + public readonly List Scenes; + + private uint _activeScene; + + /// + /// Stores the active scene index + /// + public uint ActiveScene + { + get { return _activeScene; } + private set + { + if (_activeScene == value) return; + _activeScene = value; + + SceneSelectFeedback.FireUpdate(); + + for (var i = 0; i <= Scenes.Count; i++) + { + Scenes[i].IsActive = (i == _activeScene); + SceneSelectDirectFeebacks[i].FireUpdate(); + } + } + } + + /// + /// Scene select direct feedback + /// + public Dictionary SceneSelectDirectFeebacks { get; private set; } + + /// + /// Scene select index feedback + /// + public IntFeedback SceneSelectFeedback { get; private set; } + + + /// + /// Plugin device constructor + /// + /// device key + /// device name + /// device configuration object + /// device communication as IBasicCommunication + /// + /// + public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig config, IBasicCommunication comms) + : base(key, name) + { + Debug.Console(TraceLevel, this, "Constructing new {0} instance", name); + + Scenes = config.Scenes; + + SceneSelectDirectFeebacks = new Dictionary(); + SceneSelectFeedback = new IntFeedback(() => (int)_activeScene); + + _comms = comms; + _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); + DeviceManager.AddDevice(_commsMonitor); + + OnlineFeedback = _commsMonitor.IsOnlineFeedback; + CommunicationMonitorFeedback = new IntFeedback(() => (int)_commsMonitor.Status); + + var commsGather = new CommunicationGather(_comms, CommsDelimiter); + commsGather.LineReceived += Handle_LineRecieved; + + var socket = _comms as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += OnSocketConnectionChange; + SocketStatusFeedback = new IntFeedback(() => (int)socket.ClientStatus); + } + + Debug.Console(TraceLevel, this, "Constructing new {0} instance complete", name); + Debug.Console(TraceLevel, new string('*', 80)); + Debug.Console(TraceLevel, new string('*', 80)); + } + + /// + /// Use the custom activiate to connect the device and start the comms monitor. + /// This method will be called when the device is built. + /// + /// + //public override bool CustomActivate() + //{ + // // Essentials will handle the connect method to the device + // _comms.Connect(); + // // Essentialss will handle starting the comms monitor + // _commsMonitor.Start(); + + // return base.CustomActivate(); + //} + + /// + /// Initialize plugin device + /// + public override void Initialize() + { + // Essentials will handle the connect method to the device + _comms.Connect(); + // Essentialss will handle starting the comms monitor + _commsMonitor.Start(); + } + + private void OnSocketConnectionChange(object sender, GenericSocketStatusChageEventArgs args) + { + Debug.Console(DebugLevel, this, "Socket Status: {0}", args.Client.ClientStatus.ToString()); + + if (OnlineFeedback != null) + OnlineFeedback.FireUpdate(); + + if (SocketStatusFeedback != null) + SocketStatusFeedback.FireUpdate(); + + //if (!args.Client.IsConnected) return; + } + + #region Overrides of EssentialsBridgeableDevice + + /// + /// Links the plugin device to the EISC bridge + /// + /// + /// + /// + /// + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new AcuityFrescoBridgeJoinMap(joinStart); + + // This adds the join map to the collection on the bridge + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); + } + + Debug.Console(0, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + Debug.Console(0, "Linking to Bridge Type {0}", GetType().Name); + + // link joins to bridge + trilist.SetString(joinMap.DeviceName.JoinNumber, Name); + + LinkScenesToApi(trilist, joinMap); + LinkFeedbacksToApi(trilist, joinMap); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + trilist.SetString(joinMap.DeviceName.JoinNumber, Name); + }; + } + + private void LinkScenesToApi(BasicTriList trilist, AcuityFrescoBridgeJoinMap joinMap) + { + trilist.SetUShortSigAction(joinMap.SceneSelect.JoinNumber, value => SelectScene(value)); + SceneSelectFeedback.LinkInputSig(trilist.UShortInput[joinMap.SceneSelect.JoinNumber]); + + for (var i = 0; i <= Scenes.Count; i++) + { + var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + i); + var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + i); + var name = Scenes[i].Name; + var id = Scenes[i].Id; + + trilist.SetString(sceneSelectJoin, string.IsNullOrEmpty(name) ? string.Empty : name); + trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name)); + + trilist.SetSigTrueAction(sceneSelectJoin, () => SelectScene(id)); + SceneSelectDirectFeebacks[i].LinkInputSig(trilist.BooleanInput[sceneSelectJoin]); + } + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + for (var i = 0; i <= Scenes.Count; i++) + { + var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + i); + var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + i); + var name = Scenes[i].Name; + + trilist.SetString(sceneSelectJoin, string.IsNullOrEmpty(name) ? string.Empty : name); + trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name)); + } + }; + } + + private void LinkFeedbacksToApi(BasicTriList trilist, AcuityFrescoBridgeJoinMap joinMap) + { + OnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + CommunicationMonitorFeedback.LinkInputSig(trilist.UShortInput[joinMap.CommunicationMonitorStatus.JoinNumber]); + SocketStatusFeedback.LinkInputSig(trilist.UShortInput[joinMap.SocketStatus.JoinNumber]); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + OnlineFeedback.FireUpdate(); + CommunicationMonitorFeedback.FireUpdate(); + SocketStatusFeedback.FireUpdate(); + }; + } + + #endregion Overrides of EssentialsBridgeableDevice + + // commonly used with ASCII based API's with a defined delimiter + private void Handle_LineRecieved(object sender, GenericCommMethodReceiveTextArgs args) + { + if (args == null || string.IsNullOrEmpty(args.Text)) + { Debug.Console(DebugLevel, this, "Handle_LineReceived args is null or args.Text is null or empty"); - return; - } + return; + } Debug.Console(DebugLevel, this, "Handle_LineReceived args.Text: {0}", args.Text); // TODO [ ] Process device response - } - - /// - /// Sends text to the device plugin comms - /// - /// - /// 'scene {Scene ID} {Level} [0 {room ID}]' - /// '{}' are required params - /// '[]' are optional params - /// - public void SendText(string text) - { - if (string.IsNullOrEmpty(text)) return; - - var cmd = string.IsNullOrEmpty(CommsDelimiter) - ? string.Format("{0}", text) + } + + /// + /// Sends text to the device plugin comms + /// + /// + /// 'scene {Scene ID} {Level} [0 {room ID}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void SendText(string text) + { + if (string.IsNullOrEmpty(text)) return; + + var cmd = string.IsNullOrEmpty(CommsDelimiter) + ? string.Format("{0}", text) : string.Format("{0}{1}", text, CommsDelimiter); - - _comms.SendText(cmd); - } - - /// - /// Polls the device scene status for all - /// - /// + + _comms.SendText(cmd); + } + + /// + /// Polls the device scene status for all + /// + /// /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' /// '{}' are required params /// '[]' are optional params - /// - public void Poll() - { - Poll(0, null); - } + /// + public void Poll() + { + Poll(0, null); + } /// /// Polls the device scene status for the scene ID @@ -115,10 +307,10 @@ public void Poll() /// '{}' are required params /// '[]' are optional params /// - public void Poll(uint index) + public void Poll(uint index) { Poll(index, null); - } + } /// /// Polls the device scene status for the scene ID and room ID @@ -128,7 +320,7 @@ public void Poll(uint index) /// '{}' are required params /// '[]' are optional params /// - public void Poll(uint index, string roomId) + public void Poll(uint index, string roomId) { if (index == 0 && string.IsNullOrEmpty(roomId)) { @@ -138,29 +330,10 @@ public void Poll(uint index, string roomId) { SendText(string.IsNullOrEmpty(roomId) ? string.Format("status scene {0}", index) - : string.Format("status scene {0} 0 {1}", index, roomId)); - } + : string.Format("status scene {0} 0 {1}", index, roomId)); + } } - #region Overrides of EssentialsBridgeableDevice - - /// - /// Links the plugin device to the EISC bridge - /// - /// - /// - /// - /// - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - Debug.Console(DebugLevel, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(DebugLevel, this, "Linking to Bridge Type {0}", GetType().Name); - - LinkLightingToApi(this, trilist, joinStart, joinMapKey, bridge); - } - - #endregion Overrides of EssentialsBridgeableDevice - /// /// Scene select /// @@ -169,22 +342,20 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join /// '{}' are required params /// '[]' are optional params /// - public override void SelectScene(LightingScene scene) + public void SelectScene(uint id) { + var scene = Scenes.FirstOrDefault(s => s.Id.Equals(id)); if (scene == null) { - Debug.Console(DebugLevel, this, "scene is null"); + Debug.Console(DebugLevel, this, "SelectScene: invalid id-'{0}'", id); return; } + + var cmd = (string.IsNullOrEmpty(scene.RoomId)) + ? string.Format("scene {0} {1}", scene.Id, scene.Level) + : string.Format("scene {0} {1} {2}", scene.Id, scene.Level, scene.RoomId); - var s = scene as AcuityFrescoScene; - if(s == null) return; - - var cmd = (string.IsNullOrEmpty(s.RoomId)) - ? string.Format("scene {0} {1}", s.ID, s.Level) - : string.Format("scene {0} {1} {2}", s.ID, s.Level, s.RoomId); - - SendText(cmd); + SendText(cmd); } /// @@ -193,40 +364,38 @@ public override void SelectScene(LightingScene scene) /// /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"GetScenes", "params":[]} /// - public void GetScenes() - { + public void GetScenes() + { Debug.Console(TraceLevel, this, new string('*', 80)); Debug.Console(TraceLevel, this, "Scene List:"); - var index = 0; - foreach (var scene in _scenes) + for(var i = 0; i <= Scenes.Count; i++) { - Debug.Console(TraceLevel, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", index, scene.ID, scene.Level, scene.RoomId); - index ++; + Debug.Console(TraceLevel, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", i, Scenes[i].Id, Scenes[i].Level, Scenes[i].RoomId); } Debug.Console(TraceLevel, this, new string('*', 80)); - } + } #region DebugLevels - /// - /// Trace level (0) - /// - public uint TraceLevel = 0; + /// + /// Trace level (0) + /// + public uint TraceLevel = 0; - /// - /// Debug level (1) - /// - public uint DebugLevel = 1; + /// + /// Debug level (1) + /// + public uint DebugLevel = 1; - /// - /// Error Level (2) - /// - public uint ErrorLevel = 2; + /// + /// Error Level (2) + /// + public uint ErrorLevel = 2; - private CTimer _debugTimer; - private bool _debugTimerActive; + private CTimer _debugTimer; + private bool _debugTimerActive; /// /// Resets debug levels for this device instancee @@ -234,16 +403,16 @@ public void GetScenes() /// /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"ResetDebugLevels", "params":[]} /// - public void ResetDebugLevels() + public void ResetDebugLevels() { TraceLevel = 0; DebugLevel = 1; ErrorLevel = 2; - if (_debugTimerActive) - _debugTimer.Stop(); - - if(!_debugTimer.Disposed) + if (_debugTimerActive) + _debugTimer.Stop(); + + if (!_debugTimer.Disposed) _debugTimer.Dispose(); _debugTimerActive = _debugTimer != null; @@ -256,21 +425,21 @@ public void ResetDebugLevels() /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"SetDebugLevels", "params":[{level, 0-2}]} /// /// - public void SetDebugLevels(uint level) - { + public void SetDebugLevels(uint level) + { TraceLevel = level; DebugLevel = level; ErrorLevel = level; - if(_debugTimer == null) + if (_debugTimer == null) _debugTimer = new CTimer(dt => ResetDebugLevels(), 900000); // 900,000 = 15-mins else _debugTimer.Reset(); _debugTimerActive = _debugTimer != null; - } + } - #endregion - } + #endregion + } } diff --git a/src/AcuityFrescoPropertiesConfig.cs b/src/AcuityFrescoPropertiesConfig.cs index 0a7f8c3..d0d7528 100644 --- a/src/AcuityFrescoPropertiesConfig.cs +++ b/src/AcuityFrescoPropertiesConfig.cs @@ -45,8 +45,20 @@ public class AcuityFrescoPropertiesConfig /// /// /// - public class AcuityFrescoScene : LightingScene + public class AcuityFrescoScene { + /// + /// Scene name + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Scene ID usigned integer + /// + [JsonProperty("id")] + public uint Id { get; set; } + /// /// Room ID used for scene recall /// @@ -58,5 +70,11 @@ public class AcuityFrescoScene : LightingScene /// [JsonProperty("level")] public uint Level { get; set; } + + /// + /// Tracks if scene is active + /// + [JsonProperty("isActive")] + public bool IsActive { get; set; } } } \ No newline at end of file From f0262055e564c8c769bd3dcfcc9cbe2d7e3ef902 Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Fri, 6 May 2022 18:30:37 -0500 Subject: [PATCH 08/16] refactor: updated plugin, bench tested to verify bridge implementation --- README.md | 14 +- src/AcuityFrescoBridgeJoinMap.cs | 1 - src/AcuityFrescoDevice.cs | 181 ++++++++++++++---------- src/PepperDashPluginAcuityFresco.csproj | 2 +- 4 files changed, 113 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index c88e298..d06c1e4 100644 --- a/README.md +++ b/README.md @@ -144,15 +144,17 @@ Update the bridge configuration object as needed for the plugin being developed. #### Analogs -| an_o (Input/Triggers) | I/O | an_i (Feedback) | -| --------------------- | --- | -------------------- | -| Select Scene by Index | 1 | Scene Index Feedback | +| an_o (Input/Triggers) | I/O | an_i (Feedback) | +| --------------------- | --- | ------------------------------------- | +| | | Communication Monitor Status Feedback | +| | | Socket Status Feedback | +| Select Scene by Index | 10 | Scene Index Feedback | #### Serials -| serial-o (Input/Triggers) | I/O | serial-i (Feedback) | -| ----------------------------- | --- | ------------------- | -| Integration ID Set (NOT USED) | 1 | | +| serial-o (Input/Triggers) | I/O | serial-i (Feedback) | +| ------------------------- | --- | ------------------- | +| | 1 | Device Name | ### DEVJSON Commands diff --git a/src/AcuityFrescoBridgeJoinMap.cs b/src/AcuityFrescoBridgeJoinMap.cs index 036c96c..f8acc4a 100644 --- a/src/AcuityFrescoBridgeJoinMap.cs +++ b/src/AcuityFrescoBridgeJoinMap.cs @@ -1,5 +1,4 @@ using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; namespace PepperDashPluginAcuityFresco { diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index eca74e1..ac74c42 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -1,11 +1,9 @@ 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 PepperDash.Essentials.Core.Lighting; namespace PepperDashPluginAcuityFresco @@ -56,13 +54,15 @@ private set { if (_activeScene == value) return; _activeScene = value; - + SceneSelectFeedback.FireUpdate(); - for (var i = 0; i <= Scenes.Count; i++) + var index = 0; + foreach (var scene in Scenes) { - Scenes[i].IsActive = (i == _activeScene); - SceneSelectDirectFeebacks[i].FireUpdate(); + scene.IsActive = (index == _activeScene); + SceneSelectDirectFeebacks[index].FireUpdate(); + index++; } } } @@ -97,9 +97,12 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig SceneSelectDirectFeebacks = new Dictionary(); SceneSelectFeedback = new IntFeedback(() => (int)_activeScene); + InitializeSceneSelectDirectFeedback(Scenes); + _comms = comms; _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); - DeviceManager.AddDevice(_commsMonitor); + _commsMonitor.StatusChange += OnCommunicationMonitorStatusChange; + //DeviceManager.AddDevice(_commsMonitor); OnlineFeedback = _commsMonitor.IsOnlineFeedback; CommunicationMonitorFeedback = new IntFeedback(() => (int)_commsMonitor.Status); @@ -124,36 +127,39 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig /// This method will be called when the device is built. /// /// - //public override bool CustomActivate() + public override bool CustomActivate() + { + // Essentials will handle the connect method to the device + _comms.Connect(); + // Essentialss will handle starting the comms monitor + _commsMonitor.Start(); + + return base.CustomActivate(); + } + + /// + /// Initialize plugin device + /// + //public override void Initialize() //{ // // Essentials will handle the connect method to the device // _comms.Connect(); + // _commsMonitor.StatusChange += + // (sender, args) => Debug.Console(DebugLevel, this, "Communication monitor state: {0}", _commsMonitor.Status); // // Essentialss will handle starting the comms monitor // _commsMonitor.Start(); - - // return base.CustomActivate(); //} - /// - /// Initialize plugin device - /// - public override void Initialize() + private void OnCommunicationMonitorStatusChange(object sender, MonitorStatusChangeEventArgs args) { - // Essentials will handle the connect method to the device - _comms.Connect(); - // Essentialss will handle starting the comms monitor - _commsMonitor.Start(); + Debug.Console(DebugLevel, this, "Communication Status: ({0}) {1}, {2}", args.Status, args.Status.ToString(), args.Message); } private void OnSocketConnectionChange(object sender, GenericSocketStatusChageEventArgs args) { - Debug.Console(DebugLevel, this, "Socket Status: {0}", args.Client.ClientStatus.ToString()); - - if (OnlineFeedback != null) - OnlineFeedback.FireUpdate(); + Debug.Console(DebugLevel, this, "Socket Status: ({0}) {1}", args.Client.ClientStatus, args.Client.ClientStatus.ToString()); - if (SocketStatusFeedback != null) - SocketStatusFeedback.FireUpdate(); + UpdateFeedbacks(); //if (!args.Client.IsConnected) return; } @@ -183,75 +189,68 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join joinMap.SetCustomJoinData(customJoins); } - Debug.Console(0, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(0, "Linking to Bridge Type {0}", GetType().Name); + Debug.Console(TraceLevel, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + Debug.Console(TraceLevel, "Linking to Bridge Type {0}", GetType().Name); // link joins to bridge trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - LinkScenesToApi(trilist, joinMap); - LinkFeedbacksToApi(trilist, joinMap); - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - }; - } + OnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + CommunicationMonitorFeedback.LinkInputSig(trilist.UShortInput[joinMap.CommunicationMonitorStatus.JoinNumber]); + if (SocketStatusFeedback != null) + SocketStatusFeedback.LinkInputSig(trilist.UShortInput[joinMap.SocketStatus.JoinNumber]); - private void LinkScenesToApi(BasicTriList trilist, AcuityFrescoBridgeJoinMap joinMap) - { - trilist.SetUShortSigAction(joinMap.SceneSelect.JoinNumber, value => SelectScene(value)); + trilist.SetUShortSigAction(joinMap.SceneSelect.JoinNumber, index => SelectScene(index)); SceneSelectFeedback.LinkInputSig(trilist.UShortInput[joinMap.SceneSelect.JoinNumber]); - for (var i = 0; i <= Scenes.Count; i++) + var sceneIndex = 0; + foreach (var scene in Scenes) { - var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + i); - var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + i); - var name = Scenes[i].Name; - var id = Scenes[i].Id; + var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + sceneIndex); + var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + sceneIndex); + var name = scene.Name; trilist.SetString(sceneSelectJoin, string.IsNullOrEmpty(name) ? string.Empty : name); trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name)); - trilist.SetSigTrueAction(sceneSelectJoin, () => SelectScene(id)); - SceneSelectDirectFeebacks[i].LinkInputSig(trilist.BooleanInput[sceneSelectJoin]); + trilist.SetSigTrueAction(sceneSelectJoin, () => SelectScene(sceneIndex)); + SceneSelectDirectFeebacks[sceneIndex].LinkInputSig(trilist.BooleanInput[sceneSelectJoin]); + + sceneIndex++; } + UpdateFeedbacks(); + trilist.OnlineStatusChange += (device, args) => { if (!args.DeviceOnLine) return; - for (var i = 0; i <= Scenes.Count; i++) - { - var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + i); - var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + i); - var name = Scenes[i].Name; + trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - trilist.SetString(sceneSelectJoin, string.IsNullOrEmpty(name) ? string.Empty : name); - trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name)); - } + UpdateFeedbacks(); }; } - private void LinkFeedbacksToApi(BasicTriList trilist, AcuityFrescoBridgeJoinMap joinMap) + #endregion Overrides of EssentialsBridgeableDevice + + private void UpdateFeedbacks() { - OnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - CommunicationMonitorFeedback.LinkInputSig(trilist.UShortInput[joinMap.CommunicationMonitorStatus.JoinNumber]); - SocketStatusFeedback.LinkInputSig(trilist.UShortInput[joinMap.SocketStatus.JoinNumber]); + OnlineFeedback.FireUpdate(); + CommunicationMonitorFeedback.FireUpdate(); + if (SocketStatusFeedback != null) + SocketStatusFeedback.FireUpdate(); - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; + if (SceneSelectFeedback != null) + SceneSelectFeedback.FireUpdate(); - OnlineFeedback.FireUpdate(); - CommunicationMonitorFeedback.FireUpdate(); - SocketStatusFeedback.FireUpdate(); - }; - } + if (SceneSelectDirectFeebacks == null) return; - #endregion Overrides of EssentialsBridgeableDevice + foreach (var item in SceneSelectDirectFeebacks) + { + Debug.Console(VerboseLevel, this, "UpdateFeedbacks SceneSelectDirectFeedbacks-'{0}' Value: {1}", item.Key, item.Value.BoolValue); + item.Value.FireUpdate(); + } + } // commonly used with ASCII based API's with a defined delimiter private void Handle_LineRecieved(object sender, GenericCommMethodReceiveTextArgs args) @@ -334,6 +333,28 @@ public void Poll(uint index, string roomId) } } + /// + /// Initializes scene select direct feedback + /// + /// + public void InitializeSceneSelectDirectFeedback(List scenes) + { + if (scenes == null) return; + + Debug.Console(TraceLevel, this, "InitiliazeSceneSelectDirectFeedback: {0} has {1} scenes configured", Key, scenes.Count); + + foreach (var scene in scenes) + { + var item = scene; + var index = scenes.FindIndex(s => s.Id.Equals(item.Id)); + + Debug.Console(VerboseLevel, this, "Scene-{0} Name: {1}, Id: {2}, RoomId: {3}, Level: {4}, IsActive: {5} ", + index, item.Name, item.Id, item.RoomId, item.Level, item.IsActive); + + SceneSelectDirectFeebacks.Add(index, new BoolFeedback(() => item.IsActive)); + } + } + /// /// Scene select /// @@ -342,19 +363,25 @@ public void Poll(uint index, string roomId) /// '{}' are required params /// '[]' are optional params /// - public void SelectScene(uint id) + public void SelectScene(int index) { - var scene = Scenes.FirstOrDefault(s => s.Id.Equals(id)); + if (index < 0 || index > Scenes.Count) return; + + Debug.Console(VerboseLevel, this, "SelectScene: index-'{0}'", index); + + var scene = Scenes[index]; if (scene == null) { - Debug.Console(DebugLevel, this, "SelectScene: invalid id-'{0}'", id); + Debug.Console(DebugLevel, this, "SelectScene: invalid scene index-'{0}'", index); return; } - + var cmd = (string.IsNullOrEmpty(scene.RoomId)) ? string.Format("scene {0} {1}", scene.Id, scene.Level) : string.Format("scene {0} {1} {2}", scene.Id, scene.Level, scene.RoomId); + Debug.Console(VerboseLevel, this, "SelectScene: cmd-'{0}'", cmd); + SendText(cmd); } @@ -368,9 +395,9 @@ public void GetScenes() { Debug.Console(TraceLevel, this, new string('*', 80)); Debug.Console(TraceLevel, this, "Scene List:"); - for(var i = 0; i <= Scenes.Count; i++) + for (var i = 0; i <= Scenes.Count; i++) { - Debug.Console(TraceLevel, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", i, Scenes[i].Id, Scenes[i].Level, Scenes[i].RoomId); + Debug.Console(TraceLevel, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", i, Scenes[i].Id, Scenes[i].Level, Scenes[i].RoomId); } Debug.Console(TraceLevel, this, new string('*', 80)); @@ -390,9 +417,9 @@ public void GetScenes() public uint DebugLevel = 1; /// - /// Error Level (2) + /// Verbose Level (2) /// - public uint ErrorLevel = 2; + public uint VerboseLevel = 2; private CTimer _debugTimer; private bool _debugTimerActive; @@ -407,7 +434,7 @@ public void ResetDebugLevels() { TraceLevel = 0; DebugLevel = 1; - ErrorLevel = 2; + VerboseLevel = 2; if (_debugTimerActive) _debugTimer.Stop(); @@ -429,7 +456,7 @@ public void SetDebugLevels(uint level) { TraceLevel = level; DebugLevel = level; - ErrorLevel = level; + VerboseLevel = level; if (_debugTimer == null) _debugTimer = new CTimer(dt => ResetDebugLevels(), 900000); // 900,000 = 15-mins diff --git a/src/PepperDashPluginAcuityFresco.csproj b/src/PepperDashPluginAcuityFresco.csproj index 7d9ccaf..7f03d82 100644 --- a/src/PepperDashPluginAcuityFresco.csproj +++ b/src/PepperDashPluginAcuityFresco.csproj @@ -84,7 +84,7 @@ False - ..\packages\PepperDashEssentials\lib\net35\SimplSharpNewtonsoft.dll + ..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll False From 0384ca69a0de8dbf5c09989d698f103cbbbba532 Mon Sep 17 00:00:00 2001 From: jdevito Date: Tue, 3 Jan 2023 13:37:48 -0600 Subject: [PATCH 09/16] fix: updated SelectScene method based on notes provided by DSI in testing with Acuity --- README.md | 13 + src/AcuityFrescoDevice.cs | 919 +++++++++++++++++++------------------- 2 files changed, 472 insertions(+), 460 deletions(-) diff --git a/README.md b/README.md index d06c1e4..1007aee 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,19 @@ Update the communication settings as needed for the plugin being developed. Com ``` +#### Example API Commands + +API Command + +```c# +'scene {sceneId} {level} 0 {roomId}' +``` + +`sceneId` valid values are 0-36, `sceneId` 0 represents off +`level` valid values are 0-100, values < 100 will be applied relative to the current lighting level value +`roomId` valid values are A-X, `roomId`'s can be concatenated, for example `ABC` is avalid room ID for combined spaces + + ### Plugin Configuration Object Update the configuration object as needed for the plugin being developed. diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index ac74c42..6f825d8 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -8,465 +8,464 @@ namespace PepperDashPluginAcuityFresco { - /// - /// Plugin device - /// - public class AcuityFrescoDevice : EssentialsBridgeableDevice - { - private const string CommsDelimiter = "\n"; - private readonly IBasicCommunication _comms; - private readonly GenericCommunicationMonitor _commsMonitor; - - /// - /// Communication status monitor - /// - public StatusMonitorBase CommunicationMonitor { get { return _commsMonitor; } } - - /// - /// Online feedback - /// - public BoolFeedback OnlineFeedback { get; private set; } - - /// - /// Communication monitor feedback - /// - public IntFeedback CommunicationMonitorFeedback { get; private set; } - - /// - /// Socket status feedback - /// - public IntFeedback SocketStatusFeedback { get; private set; } - - /// - /// List of configured scenes - /// - public readonly List Scenes; - - private uint _activeScene; - - /// - /// Stores the active scene index - /// - public uint ActiveScene - { - get { return _activeScene; } - private set - { - if (_activeScene == value) return; - _activeScene = value; - - SceneSelectFeedback.FireUpdate(); - - var index = 0; - foreach (var scene in Scenes) - { - scene.IsActive = (index == _activeScene); - SceneSelectDirectFeebacks[index].FireUpdate(); - index++; - } - } - } - - /// - /// Scene select direct feedback - /// - public Dictionary SceneSelectDirectFeebacks { get; private set; } - - /// - /// Scene select index feedback - /// - public IntFeedback SceneSelectFeedback { get; private set; } - - - /// - /// Plugin device constructor - /// - /// device key - /// device name - /// device configuration object - /// device communication as IBasicCommunication - /// - /// - public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig config, IBasicCommunication comms) - : base(key, name) - { - Debug.Console(TraceLevel, this, "Constructing new {0} instance", name); - - Scenes = config.Scenes; - - SceneSelectDirectFeebacks = new Dictionary(); - SceneSelectFeedback = new IntFeedback(() => (int)_activeScene); - - InitializeSceneSelectDirectFeedback(Scenes); - - _comms = comms; - _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); - _commsMonitor.StatusChange += OnCommunicationMonitorStatusChange; - //DeviceManager.AddDevice(_commsMonitor); - - OnlineFeedback = _commsMonitor.IsOnlineFeedback; - CommunicationMonitorFeedback = new IntFeedback(() => (int)_commsMonitor.Status); - - var commsGather = new CommunicationGather(_comms, CommsDelimiter); - commsGather.LineReceived += Handle_LineRecieved; - - var socket = _comms as ISocketStatus; - if (socket != null) - { - socket.ConnectionChange += OnSocketConnectionChange; - SocketStatusFeedback = new IntFeedback(() => (int)socket.ClientStatus); - } - - Debug.Console(TraceLevel, this, "Constructing new {0} instance complete", name); - Debug.Console(TraceLevel, new string('*', 80)); - Debug.Console(TraceLevel, new string('*', 80)); - } - - /// - /// Use the custom activiate to connect the device and start the comms monitor. - /// This method will be called when the device is built. - /// - /// - public override bool CustomActivate() - { - // Essentials will handle the connect method to the device - _comms.Connect(); - // Essentialss will handle starting the comms monitor - _commsMonitor.Start(); - - return base.CustomActivate(); - } - - /// - /// Initialize plugin device - /// - //public override void Initialize() - //{ - // // Essentials will handle the connect method to the device - // _comms.Connect(); - // _commsMonitor.StatusChange += - // (sender, args) => Debug.Console(DebugLevel, this, "Communication monitor state: {0}", _commsMonitor.Status); - // // Essentialss will handle starting the comms monitor - // _commsMonitor.Start(); - //} - - private void OnCommunicationMonitorStatusChange(object sender, MonitorStatusChangeEventArgs args) - { - Debug.Console(DebugLevel, this, "Communication Status: ({0}) {1}, {2}", args.Status, args.Status.ToString(), args.Message); - } - - private void OnSocketConnectionChange(object sender, GenericSocketStatusChageEventArgs args) - { - Debug.Console(DebugLevel, this, "Socket Status: ({0}) {1}", args.Client.ClientStatus, args.Client.ClientStatus.ToString()); - - UpdateFeedbacks(); - - //if (!args.Client.IsConnected) return; - } - - #region Overrides of EssentialsBridgeableDevice - - /// - /// Links the plugin device to the EISC bridge - /// - /// - /// - /// - /// - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = new AcuityFrescoBridgeJoinMap(joinStart); - - // This adds the join map to the collection on the bridge - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - - var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); - if (customJoins != null) - { - joinMap.SetCustomJoinData(customJoins); - } - - Debug.Console(TraceLevel, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(TraceLevel, "Linking to Bridge Type {0}", GetType().Name); - - // link joins to bridge - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - - OnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - CommunicationMonitorFeedback.LinkInputSig(trilist.UShortInput[joinMap.CommunicationMonitorStatus.JoinNumber]); - if (SocketStatusFeedback != null) - SocketStatusFeedback.LinkInputSig(trilist.UShortInput[joinMap.SocketStatus.JoinNumber]); - - trilist.SetUShortSigAction(joinMap.SceneSelect.JoinNumber, index => SelectScene(index)); - SceneSelectFeedback.LinkInputSig(trilist.UShortInput[joinMap.SceneSelect.JoinNumber]); - - var sceneIndex = 0; - foreach (var scene in Scenes) - { - var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + sceneIndex); - var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + sceneIndex); - var name = scene.Name; - - trilist.SetString(sceneSelectJoin, string.IsNullOrEmpty(name) ? string.Empty : name); - trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name)); - - trilist.SetSigTrueAction(sceneSelectJoin, () => SelectScene(sceneIndex)); - SceneSelectDirectFeebacks[sceneIndex].LinkInputSig(trilist.BooleanInput[sceneSelectJoin]); - - sceneIndex++; - } - - UpdateFeedbacks(); - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - trilist.SetString(joinMap.DeviceName.JoinNumber, Name); - - UpdateFeedbacks(); - }; - } - - #endregion Overrides of EssentialsBridgeableDevice - - private void UpdateFeedbacks() - { - OnlineFeedback.FireUpdate(); - CommunicationMonitorFeedback.FireUpdate(); - if (SocketStatusFeedback != null) - SocketStatusFeedback.FireUpdate(); - - if (SceneSelectFeedback != null) - SceneSelectFeedback.FireUpdate(); - - if (SceneSelectDirectFeebacks == null) return; - - foreach (var item in SceneSelectDirectFeebacks) - { - Debug.Console(VerboseLevel, this, "UpdateFeedbacks SceneSelectDirectFeedbacks-'{0}' Value: {1}", item.Key, item.Value.BoolValue); - item.Value.FireUpdate(); - } - } - - // commonly used with ASCII based API's with a defined delimiter - private void Handle_LineRecieved(object sender, GenericCommMethodReceiveTextArgs args) - { - if (args == null || string.IsNullOrEmpty(args.Text)) - { - Debug.Console(DebugLevel, this, "Handle_LineReceived args is null or args.Text is null or empty"); - return; - } - - Debug.Console(DebugLevel, this, "Handle_LineReceived args.Text: {0}", args.Text); - - // TODO [ ] Process device response - } - - /// - /// Sends text to the device plugin comms - /// - /// - /// 'scene {Scene ID} {Level} [0 {room ID}]' - /// '{}' are required params - /// '[]' are optional params - /// - public void SendText(string text) - { - if (string.IsNullOrEmpty(text)) return; - - var cmd = string.IsNullOrEmpty(CommsDelimiter) - ? string.Format("{0}", text) - : string.Format("{0}{1}", text, CommsDelimiter); - - _comms.SendText(cmd); - } - - /// - /// Polls the device scene status for all - /// - /// - /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' - /// '{}' are required params - /// '[]' are optional params - /// - public void Poll() - { - Poll(0, null); - } - - /// - /// Polls the device scene status for the scene ID - /// - /// - /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' - /// '{}' are required params - /// '[]' are optional params - /// - public void Poll(uint index) - { - Poll(index, null); - } - - /// - /// Polls the device scene status for the scene ID and room ID - /// - /// - /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' - /// '{}' are required params - /// '[]' are optional params - /// - public void Poll(uint index, string roomId) - { - if (index == 0 && string.IsNullOrEmpty(roomId)) - { - SendText("status scene ALL"); - } - else - { - SendText(string.IsNullOrEmpty(roomId) - ? string.Format("status scene {0}", index) - : string.Format("status scene {0} 0 {1}", index, roomId)); - } - } - - /// - /// Initializes scene select direct feedback - /// - /// - public void InitializeSceneSelectDirectFeedback(List scenes) - { - if (scenes == null) return; - - Debug.Console(TraceLevel, this, "InitiliazeSceneSelectDirectFeedback: {0} has {1} scenes configured", Key, scenes.Count); - - foreach (var scene in scenes) - { - var item = scene; - var index = scenes.FindIndex(s => s.Id.Equals(item.Id)); - - Debug.Console(VerboseLevel, this, "Scene-{0} Name: {1}, Id: {2}, RoomId: {3}, Level: {4}, IsActive: {5} ", - index, item.Name, item.Id, item.RoomId, item.Level, item.IsActive); - - SceneSelectDirectFeebacks.Add(index, new BoolFeedback(() => item.IsActive)); - } - } - - /// - /// Scene select - /// - /// - /// 'scene {Scene ID, 1-36} {Level, 0-100%} [0 {room ID, A-X}]' - /// '{}' are required params - /// '[]' are optional params - /// - public void SelectScene(int index) - { - if (index < 0 || index > Scenes.Count) return; - - Debug.Console(VerboseLevel, this, "SelectScene: index-'{0}'", index); - - var scene = Scenes[index]; - if (scene == null) - { - Debug.Console(DebugLevel, this, "SelectScene: invalid scene index-'{0}'", index); - return; - } - - var cmd = (string.IsNullOrEmpty(scene.RoomId)) - ? string.Format("scene {0} {1}", scene.Id, scene.Level) - : string.Format("scene {0} {1} {2}", scene.Id, scene.Level, scene.RoomId); - - Debug.Console(VerboseLevel, this, "SelectScene: cmd-'{0}'", cmd); - - SendText(cmd); - } - - /// - /// Prints the list of scenes to console - /// - /// - /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"GetScenes", "params":[]} - /// - public void GetScenes() - { - Debug.Console(TraceLevel, this, new string('*', 80)); - Debug.Console(TraceLevel, this, "Scene List:"); - for (var i = 0; i <= Scenes.Count; i++) - { - Debug.Console(TraceLevel, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", i, Scenes[i].Id, Scenes[i].Level, Scenes[i].RoomId); - } - - Debug.Console(TraceLevel, this, new string('*', 80)); - } - - - #region DebugLevels - - /// - /// Trace level (0) - /// - public uint TraceLevel = 0; - - /// - /// Debug level (1) - /// - public uint DebugLevel = 1; - - /// - /// Verbose Level (2) - /// - public uint VerboseLevel = 2; - - private CTimer _debugTimer; - private bool _debugTimerActive; - - /// - /// Resets debug levels for this device instancee - /// - /// - /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"ResetDebugLevels", "params":[]} - /// - public void ResetDebugLevels() - { - TraceLevel = 0; - DebugLevel = 1; - VerboseLevel = 2; - - if (_debugTimerActive) - _debugTimer.Stop(); - - if (!_debugTimer.Disposed) - _debugTimer.Dispose(); - - _debugTimerActive = _debugTimer != null; - } - - /// - /// Sets the debug levels for this device instance - /// - /// - /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"SetDebugLevels", "params":[{level, 0-2}]} - /// - /// - public void SetDebugLevels(uint level) - { - TraceLevel = level; - DebugLevel = level; - VerboseLevel = level; - - if (_debugTimer == null) - _debugTimer = new CTimer(dt => ResetDebugLevels(), 900000); // 900,000 = 15-mins - else - _debugTimer.Reset(); - - _debugTimerActive = _debugTimer != null; - } - - #endregion - } + /// + /// Plugin device + /// + public class AcuityFrescoDevice : EssentialsBridgeableDevice + { + private const string CommsDelimiter = "\n"; + private readonly IBasicCommunication _comms; + private readonly GenericCommunicationMonitor _commsMonitor; + + /// + /// Communication status monitor + /// + public StatusMonitorBase CommunicationMonitor { get { return _commsMonitor; } } + + /// + /// Online feedback + /// + public BoolFeedback OnlineFeedback { get; private set; } + + /// + /// Communication monitor feedback + /// + public IntFeedback CommunicationMonitorFeedback { get; private set; } + + /// + /// Socket status feedback + /// + public IntFeedback SocketStatusFeedback { get; private set; } + + /// + /// List of configured scenes + /// + public readonly List Scenes; + + private uint _activeScene; + + /// + /// Stores the active scene index + /// + public uint ActiveScene + { + get { return _activeScene; } + private set + { + if (_activeScene == value) return; + _activeScene = value; + + SceneSelectFeedback.FireUpdate(); + + var index = 0; + foreach (var scene in Scenes) + { + scene.IsActive = (index == _activeScene); + SceneSelectDirectFeebacks[index].FireUpdate(); + index++; + } + } + } + + /// + /// Scene select direct feedback + /// + public Dictionary SceneSelectDirectFeebacks { get; private set; } + + /// + /// Scene select index feedback + /// + public IntFeedback SceneSelectFeedback { get; private set; } + + + /// + /// Plugin device constructor + /// + /// device key + /// device name + /// device configuration object + /// device communication as IBasicCommunication + /// + /// + public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig config, IBasicCommunication comms) + : base(key, name) + { + Debug.Console(DebugTrace, this, "Constructing new {0} instance", name); + + Scenes = config.Scenes; + + SceneSelectDirectFeebacks = new Dictionary(); + SceneSelectFeedback = new IntFeedback(() => (int)_activeScene); + + InitializeSceneSelectDirectFeedback(Scenes); + + _comms = comms; + _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); + _commsMonitor.StatusChange += OnCommunicationMonitorStatusChange; + //DeviceManager.AddDevice(_commsMonitor); + + OnlineFeedback = _commsMonitor.IsOnlineFeedback; + CommunicationMonitorFeedback = new IntFeedback(() => (int)_commsMonitor.Status); + + var commsGather = new CommunicationGather(_comms, CommsDelimiter); + commsGather.LineReceived += Handle_LineRecieved; + + var socket = _comms as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += OnSocketConnectionChange; + SocketStatusFeedback = new IntFeedback(() => (int)socket.ClientStatus); + } + + Debug.Console(DebugTrace, this, "Constructing new {0} instance complete", name); + Debug.Console(DebugTrace, new string('*', 80)); + Debug.Console(DebugTrace, new string('*', 80)); + } + + /// + /// Use the custom activiate to connect the device and start the comms monitor. + /// This method will be called when the device is built. + /// + /// + //public override bool CustomActivate() + //{ + // // Essentials will handle the connect method to the device + // _comms.Connect(); + // // Essentialss will handle starting the comms monitor + // _commsMonitor.Start(); + + // return base.CustomActivate(); + //} + + /// + /// Initialize plugin device + /// + public override void Initialize() + { + // Essentials will handle the connect method to the device + _comms.Connect(); + _commsMonitor.StatusChange += OnCommunicationMonitorStatusChange; + // Essentialss will handle starting the comms monitor + _commsMonitor.Start(); + } + + private void OnCommunicationMonitorStatusChange(object sender, MonitorStatusChangeEventArgs args) + { + Debug.Console(DebugInfo, this, "Communication Status: ({0}) {1}, {2}", args.Status, args.Status.ToString(), args.Message); + } + + private void OnSocketConnectionChange(object sender, GenericSocketStatusChageEventArgs args) + { + Debug.Console(DebugInfo, this, "Socket Status: ({0}) {1}", args.Client.ClientStatus, args.Client.ClientStatus.ToString()); + + UpdateFeedbacks(); + + //if (!args.Client.IsConnected) return; + } + + #region Overrides of EssentialsBridgeableDevice + + /// + /// Links the plugin device to the EISC bridge + /// + /// + /// + /// + /// + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new AcuityFrescoBridgeJoinMap(joinStart); + + // This adds the join map to the collection on the bridge + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); + } + + Debug.Console(DebugTrace, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + Debug.Console(DebugTrace, "Linking to Bridge Type {0}", GetType().Name); + + // link joins to bridge + trilist.SetString(joinMap.DeviceName.JoinNumber, Name); + + OnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + CommunicationMonitorFeedback.LinkInputSig(trilist.UShortInput[joinMap.CommunicationMonitorStatus.JoinNumber]); + if (SocketStatusFeedback != null) + SocketStatusFeedback.LinkInputSig(trilist.UShortInput[joinMap.SocketStatus.JoinNumber]); + + trilist.SetUShortSigAction(joinMap.SceneSelect.JoinNumber, index => SelectScene(index)); + SceneSelectFeedback.LinkInputSig(trilist.UShortInput[joinMap.SceneSelect.JoinNumber]); + + var sceneIndex = 0; + foreach (var scene in Scenes) + { + var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + sceneIndex); + var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + sceneIndex); + var name = scene.Name; + + trilist.SetString(sceneSelectJoin, string.IsNullOrEmpty(name) ? string.Empty : name); + trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name)); + + trilist.SetSigTrueAction(sceneSelectJoin, () => SelectScene(sceneIndex)); + SceneSelectDirectFeebacks[sceneIndex].LinkInputSig(trilist.BooleanInput[sceneSelectJoin]); + + sceneIndex++; + } + + UpdateFeedbacks(); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + trilist.SetString(joinMap.DeviceName.JoinNumber, Name); + + UpdateFeedbacks(); + }; + } + + #endregion Overrides of EssentialsBridgeableDevice + + private void UpdateFeedbacks() + { + OnlineFeedback.FireUpdate(); + CommunicationMonitorFeedback.FireUpdate(); + if (SocketStatusFeedback != null) + SocketStatusFeedback.FireUpdate(); + + if (SceneSelectFeedback != null) + SceneSelectFeedback.FireUpdate(); + + if (SceneSelectDirectFeebacks == null) return; + + foreach (var item in SceneSelectDirectFeebacks) + { + Debug.Console(DebugVerbose, this, "UpdateFeedbacks SceneSelectDirectFeedbacks-'{0}' Value: {1}", item.Key, item.Value.BoolValue); + item.Value.FireUpdate(); + } + } + + // commonly used with ASCII based API's with a defined delimiter + private void Handle_LineRecieved(object sender, GenericCommMethodReceiveTextArgs args) + { + if (args == null || string.IsNullOrEmpty(args.Text)) + { + Debug.Console(DebugInfo, this, "Handle_LineReceived args is null or args.Text is null or empty"); + return; + } + + Debug.Console(DebugInfo, this, "Handle_LineReceived args.Text: {0}", args.Text); + + // TODO [ ] Process device response + } + + /// + /// Sends text to the device plugin comms + /// + /// + /// 'scene {Scene ID} {Level} [0 {room ID}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void SendText(string text) + { + if (string.IsNullOrEmpty(text)) return; + + var cmd = string.IsNullOrEmpty(CommsDelimiter) + ? string.Format("{0}", text) + : string.Format("{0}{1}", text, CommsDelimiter); + + _comms.SendText(cmd); + } + + /// + /// Polls the device scene status for all + /// + /// + /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void Poll() + { + Poll(0, null); + } + + /// + /// Polls the device scene status for the scene ID + /// + /// + /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void Poll(uint index) + { + Poll(index, null); + } + + /// + /// Polls the device scene status for the scene ID and room ID + /// + /// + /// 'status scene {scene ID, 1-36 || ALL}' [0 {room ID, A-X}]' + /// '{}' are required params + /// '[]' are optional params + /// + public void Poll(uint index, string roomId) + { + if (index == 0 && string.IsNullOrEmpty(roomId)) + { + SendText("status scene ALL"); + } + else + { + SendText(string.IsNullOrEmpty(roomId) + ? string.Format("status scene {0}", index) + : string.Format("status scene {0} 0 {1}", index, roomId)); + } + } + + /// + /// Initializes scene select direct feedback + /// + /// + public void InitializeSceneSelectDirectFeedback(List scenes) + { + if (scenes == null) return; + + Debug.Console(DebugTrace, this, "InitiliazeSceneSelectDirectFeedback: {0} has {1} scenes configured", Key, scenes.Count); + + foreach (var scene in scenes) + { + var item = scene; + var index = scenes.FindIndex(s => s.Id.Equals(item.Id)); + + Debug.Console(DebugVerbose, this, "Scene-{0} Name: {1}, Id: {2}, RoomId: {3}, Level: {4}, IsActive: {5} ", + index, item.Name, item.Id, item.RoomId, item.Level, item.IsActive); + + SceneSelectDirectFeebacks.Add(index, new BoolFeedback(() => item.IsActive)); + } + } + + /// + /// Scene select + /// + /// + /// 'scene {SceneId, 1-36} {Level, 0-100%} 0 {RoomId, A-X}' + /// + /// + /// 'scene 36 100 0 X' + /// 'scene 2 100 0 AB' + /// 'scene 5 50 0 ABC' + /// 'scene 0 100 0 A' // lights off + /// + public void SelectScene(int index) + { + if (index < 0 || index > Scenes.Count) return; + + Debug.Console(DebugVerbose, this, "SelectScene: index-'{0}'", index); + + var scene = Scenes[index]; + if (scene == null) + { + Debug.Console(DebugInfo, this, "SelectScene: invalid scene index-'{0}'", index); + return; + } + + if (scene.Id > 36) + { + Debug.Console(DebugInfo, this, "SelectScene: scene index-'{0}' sceneId-'{1}' is out of range (valid values 1-36)", index, scene.Id); + return; + } + + if (scene.Level > 100) + { + Debug.Console(DebugInfo, this, "SelectScene: scene index-'{0}' level-'{1}' is out of range (valid values 1-100)", index, scene.Level); + return; + } + + if (string.IsNullOrEmpty(scene.RoomId)) + { + Debug.Console(DebugInfo, this, "SelectScene: scene index-'{0}' roomId is null or empty", index); + return; + } + + var cmd = string.Format("scene {0} {1} 0 {2}", scene.Id, scene.Level, scene.RoomId); + Debug.Console(DebugVerbose, this, "SelectScene: cmd-'{0}'", cmd); + SendText(cmd); + } + + /// + /// Prints the list of scenes to console + /// + /// + /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"GetScenes", "params":[]} + /// + public void GetScenes() + { + Debug.Console(DebugTrace, this, new string('*', 80)); + Debug.Console(DebugTrace, this, "Scene List:"); + for (var i = 0; i <= Scenes.Count; i++) + { + Debug.Console(DebugTrace, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", i, Scenes[i].Id, Scenes[i].Level, Scenes[i].RoomId); + } + + Debug.Console(DebugTrace, this, new string('*', 80)); + } + + + #region DebugLevels + + /// + /// Trace level (0) + /// + public uint DebugTrace = 0; + + /// + /// Debug level (1) + /// + public uint DebugInfo = 1; + + /// + /// Verbose Level (2) + /// + public uint DebugVerbose = 2; + + /// + /// Resets debug levels for this device instancee + /// + /// + /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"ResetDebugLevels", "params":[]} + /// + public void ResetDebugLevels() + { + DebugTrace = 0; + DebugInfo = 1; + DebugVerbose = 2; + } + + /// + /// Sets the debug levels for this device instance + /// + /// + /// devjson:1 {"deviceKey":"{deviceKey}", "methodName":"SetDebugLevels", "params":[{level, 0-2}]} + /// + /// + public void SetDebugLevels(uint level) + { + DebugTrace = level; + DebugInfo = level; + DebugVerbose = level; + } + + #endregion + } } From 6270539715c8be277c597761ed1ac78aef09d2e6 Mon Sep 17 00:00:00 2001 From: jdevito Date: Tue, 3 Jan 2023 13:43:33 -0600 Subject: [PATCH 10/16] feat: replaced customActivate with base initialize method --- src/AcuityFrescoDevice.cs | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index 6f825d8..65cb027 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -92,6 +92,8 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig { Debug.Console(DebugTrace, this, "Constructing new {0} instance", name); + ResetDebugLevels(); + Scenes = config.Scenes; SceneSelectDirectFeebacks = new Dictionary(); @@ -102,8 +104,7 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig _comms = comms; _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); _commsMonitor.StatusChange += OnCommunicationMonitorStatusChange; - //DeviceManager.AddDevice(_commsMonitor); - + OnlineFeedback = _commsMonitor.IsOnlineFeedback; CommunicationMonitorFeedback = new IntFeedback(() => (int)_commsMonitor.Status); @@ -122,21 +123,6 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig Debug.Console(DebugTrace, new string('*', 80)); } - /// - /// Use the custom activiate to connect the device and start the comms monitor. - /// This method will be called when the device is built. - /// - /// - //public override bool CustomActivate() - //{ - // // Essentials will handle the connect method to the device - // _comms.Connect(); - // // Essentialss will handle starting the comms monitor - // _commsMonitor.Start(); - - // return base.CustomActivate(); - //} - /// /// Initialize plugin device /// @@ -144,7 +130,6 @@ public override void Initialize() { // Essentials will handle the connect method to the device _comms.Connect(); - _commsMonitor.StatusChange += OnCommunicationMonitorStatusChange; // Essentialss will handle starting the comms monitor _commsMonitor.Start(); } @@ -426,17 +411,17 @@ public void GetScenes() /// /// Trace level (0) /// - public uint DebugTrace = 0; + public uint DebugTrace { get; set; } /// /// Debug level (1) /// - public uint DebugInfo = 1; + public uint DebugInfo { get; set; } /// /// Verbose Level (2) /// - public uint DebugVerbose = 2; + public uint DebugVerbose { get; set; } /// /// Resets debug levels for this device instancee From e133c2431e1bc242d13507610c786320c9061d8f Mon Sep 17 00:00:00 2001 From: jdevito Date: Tue, 3 Jan 2023 16:28:03 -0600 Subject: [PATCH 11/16] fix: updated LinkToApi to resolve exception with scenes foreach loop --- src/AcuityFrescoDevice.cs | 125 ++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 39 deletions(-) diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index 65cb027..4704b7d 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -1,9 +1,11 @@ -using System.Collections.Generic; -using Crestron.SimplSharp; +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; using Crestron.SimplSharpPro.DeviceSupport; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Queues; namespace PepperDashPluginAcuityFresco @@ -16,6 +18,7 @@ public class AcuityFrescoDevice : EssentialsBridgeableDevice private const string CommsDelimiter = "\n"; private readonly IBasicCommunication _comms; private readonly GenericCommunicationMonitor _commsMonitor; + private GenericQueue _commsRxQueue; /// /// Communication status monitor @@ -90,7 +93,7 @@ private set public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig config, IBasicCommunication comms) : base(key, name) { - Debug.Console(DebugTrace, this, "Constructing new {0} instance", name); + Debug.Console(TraceLevel, this, "Constructing new {0} instance", name); ResetDebugLevels(); @@ -104,6 +107,7 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig _comms = comms; _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); _commsMonitor.StatusChange += OnCommunicationMonitorStatusChange; + _commsRxQueue = new GenericQueue(key + "-queue"); OnlineFeedback = _commsMonitor.IsOnlineFeedback; CommunicationMonitorFeedback = new IntFeedback(() => (int)_commsMonitor.Status); @@ -118,9 +122,9 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig SocketStatusFeedback = new IntFeedback(() => (int)socket.ClientStatus); } - Debug.Console(DebugTrace, this, "Constructing new {0} instance complete", name); - Debug.Console(DebugTrace, new string('*', 80)); - Debug.Console(DebugTrace, new string('*', 80)); + Debug.Console(TraceLevel, this, "Constructing new {0} instance complete", name); + Debug.Console(TraceLevel, new string('*', 80)); + Debug.Console(TraceLevel, new string('*', 80)); } /// @@ -136,12 +140,12 @@ public override void Initialize() private void OnCommunicationMonitorStatusChange(object sender, MonitorStatusChangeEventArgs args) { - Debug.Console(DebugInfo, this, "Communication Status: ({0}) {1}, {2}", args.Status, args.Status.ToString(), args.Message); + Debug.Console(DebugLevel, this, "Communication Status: ({0}) {1}, {2}", args.Status, args.Status.ToString(), args.Message); } private void OnSocketConnectionChange(object sender, GenericSocketStatusChageEventArgs args) { - Debug.Console(DebugInfo, this, "Socket Status: ({0}) {1}", args.Client.ClientStatus, args.Client.ClientStatus.ToString()); + Debug.Console(DebugLevel, this, "Socket Status: ({0}) {1}", args.Client.ClientStatus, args.Client.ClientStatus.ToString()); UpdateFeedbacks(); @@ -173,8 +177,8 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join joinMap.SetCustomJoinData(customJoins); } - Debug.Console(DebugTrace, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(DebugTrace, "Linking to Bridge Type {0}", GetType().Name); + Debug.Console(TraceLevel, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + Debug.Console(TraceLevel, "Linking to Bridge Type {0}", GetType().Name); // link joins to bridge trilist.SetString(joinMap.DeviceName.JoinNumber, Name); @@ -190,14 +194,15 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join var sceneIndex = 0; foreach (var scene in Scenes) { - var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + sceneIndex); - var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + sceneIndex); + var index = sceneIndex; + var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + index); + var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + index); var name = scene.Name; trilist.SetString(sceneSelectJoin, string.IsNullOrEmpty(name) ? string.Empty : name); - trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name)); + trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name) == false); - trilist.SetSigTrueAction(sceneSelectJoin, () => SelectScene(sceneIndex)); + trilist.SetSigTrueAction(sceneSelectJoin, () => SelectScene(index)); SceneSelectDirectFeebacks[sceneIndex].LinkInputSig(trilist.BooleanInput[sceneSelectJoin]); sceneIndex++; @@ -231,7 +236,7 @@ private void UpdateFeedbacks() foreach (var item in SceneSelectDirectFeebacks) { - Debug.Console(DebugVerbose, this, "UpdateFeedbacks SceneSelectDirectFeedbacks-'{0}' Value: {1}", item.Key, item.Value.BoolValue); + Debug.Console(VerboseLevel, this, "UpdateFeedbacks SceneSelectDirectFeedbacks-'{0}' Value: {1}", item.Key, item.Value.BoolValue); item.Value.FireUpdate(); } } @@ -241,13 +246,55 @@ private void Handle_LineRecieved(object sender, GenericCommMethodReceiveTextArgs { if (args == null || string.IsNullOrEmpty(args.Text)) { - Debug.Console(DebugInfo, this, "Handle_LineReceived args is null or args.Text is null or empty"); + Debug.Console(DebugLevel, this, "Handle_LineReceived args is null or args.Text is null or empty"); return; } - Debug.Console(DebugInfo, this, "Handle_LineReceived args.Text: {0}", args.Text); + Debug.Console(DebugLevel, this, "Handle_LineReceived args.Text: {0}", args.Text); + + try + { + Debug.Console(DebugLevel, this, "Handle_LineReceived args.Text: {0}", args.Text); + _commsRxQueue.Enqueue(new ProcessStringMessage(args.Text, ProcessResponse)); + } + catch(Exception ex) + { + Debug.Console(DebugLevel, this, Debug.ErrorLogLevel.Error, "HandleLineReceived Exception Message: {0}", ex.Message); + Debug.Console(VerboseLevel, this, Debug.ErrorLogLevel.Error, "HandleLineRecieved Exception Stack Trace: {0}", ex.StackTrace); + if (ex.InnerException != null) Debug.Console(DebugLevel, this, Debug.ErrorLogLevel.Error, "HandleLineReceived Inner Exception: '{0}'", ex.InnerException); + } + } + + // example response + // 'scene {sceneId} {level} 0 {roomId} + // 'scene 1 100 0 b' + // 'scene 2 100 0 ab' + // 'scene 0 100 0 abc' + private void ProcessResponse(string response) + { + if (string.IsNullOrEmpty(response)) + { + Debug.Console(VerboseLevel, this, "ProcessResponse: response '{0}' is null or empty", response); + return; + } + + var expression = new Regex(@"scene (?.*) (?.*) 0 (?.*)", RegexOptions.None); + var matches = expression.Match(response); + if (!matches.Success) + { + Debug.Console(DebugLevel, this, "ProcessResponse: unknown response '{0}', regex match failed", response); + return; + } + + var responseSceneId = matches.Groups["sceneId"].Value.ToLower(); + var responseLevel = matches.Groups["level"].Value; + var responseRoomId = matches.Groups["roomId"].Value.ToLower(); + + Debug.Console(VerboseLevel, this, "ProcessResponse: responseSceneId-'{0}', responseLevel-'{1}' responseRoomId-'{2}'", + responseSceneId, responseLevel, responseRoomId); + + // TODO [ ] Add feedback logic - // TODO [ ] Process device response } /// @@ -325,14 +372,14 @@ public void InitializeSceneSelectDirectFeedback(List scenes) { if (scenes == null) return; - Debug.Console(DebugTrace, this, "InitiliazeSceneSelectDirectFeedback: {0} has {1} scenes configured", Key, scenes.Count); + Debug.Console(TraceLevel, this, "InitiliazeSceneSelectDirectFeedback: {0} has {1} scenes configured", Key, scenes.Count); foreach (var scene in scenes) { var item = scene; var index = scenes.FindIndex(s => s.Id.Equals(item.Id)); - Debug.Console(DebugVerbose, this, "Scene-{0} Name: {1}, Id: {2}, RoomId: {3}, Level: {4}, IsActive: {5} ", + Debug.Console(VerboseLevel, this, "Scene-{0} Name: {1}, Id: {2}, RoomId: {3}, Level: {4}, IsActive: {5} ", index, item.Name, item.Id, item.RoomId, item.Level, item.IsActive); SceneSelectDirectFeebacks.Add(index, new BoolFeedback(() => item.IsActive)); @@ -355,35 +402,35 @@ public void SelectScene(int index) { if (index < 0 || index > Scenes.Count) return; - Debug.Console(DebugVerbose, this, "SelectScene: index-'{0}'", index); + Debug.Console(VerboseLevel, this, "SelectScene: index-'{0}'", index); var scene = Scenes[index]; if (scene == null) { - Debug.Console(DebugInfo, this, "SelectScene: invalid scene index-'{0}'", index); + Debug.Console(DebugLevel, this, "SelectScene: invalid scene index-'{0}'", index); return; } if (scene.Id > 36) { - Debug.Console(DebugInfo, this, "SelectScene: scene index-'{0}' sceneId-'{1}' is out of range (valid values 1-36)", index, scene.Id); + Debug.Console(DebugLevel, this, "SelectScene: scene index-'{0}' sceneId-'{1}' is out of range (valid values 1-36)", index, scene.Id); return; } if (scene.Level > 100) { - Debug.Console(DebugInfo, this, "SelectScene: scene index-'{0}' level-'{1}' is out of range (valid values 1-100)", index, scene.Level); + Debug.Console(DebugLevel, this, "SelectScene: scene index-'{0}' level-'{1}' is out of range (valid values 1-100)", index, scene.Level); return; } if (string.IsNullOrEmpty(scene.RoomId)) { - Debug.Console(DebugInfo, this, "SelectScene: scene index-'{0}' roomId is null or empty", index); + Debug.Console(DebugLevel, this, "SelectScene: scene index-'{0}' roomId is null or empty", index); return; } var cmd = string.Format("scene {0} {1} 0 {2}", scene.Id, scene.Level, scene.RoomId); - Debug.Console(DebugVerbose, this, "SelectScene: cmd-'{0}'", cmd); + Debug.Console(VerboseLevel, this, "SelectScene: cmd-'{0}'", cmd); SendText(cmd); } @@ -395,14 +442,14 @@ public void SelectScene(int index) /// public void GetScenes() { - Debug.Console(DebugTrace, this, new string('*', 80)); - Debug.Console(DebugTrace, this, "Scene List:"); + Debug.Console(TraceLevel, this, new string('*', 80)); + Debug.Console(TraceLevel, this, "Scene List:"); for (var i = 0; i <= Scenes.Count; i++) { - Debug.Console(DebugTrace, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", i, Scenes[i].Id, Scenes[i].Level, Scenes[i].RoomId); + Debug.Console(TraceLevel, this, "Scene '{0}': Id-'{1}', Level-'{2}', Room Id-'{3}'", i, Scenes[i].Id, Scenes[i].Level, Scenes[i].RoomId); } - Debug.Console(DebugTrace, this, new string('*', 80)); + Debug.Console(TraceLevel, this, new string('*', 80)); } @@ -411,17 +458,17 @@ public void GetScenes() /// /// Trace level (0) /// - public uint DebugTrace { get; set; } + public uint TraceLevel { get; set; } /// /// Debug level (1) /// - public uint DebugInfo { get; set; } + public uint DebugLevel { get; set; } /// /// Verbose Level (2) /// - public uint DebugVerbose { get; set; } + public uint VerboseLevel { get; set; } /// /// Resets debug levels for this device instancee @@ -431,9 +478,9 @@ public void GetScenes() /// public void ResetDebugLevels() { - DebugTrace = 0; - DebugInfo = 1; - DebugVerbose = 2; + TraceLevel = 0; + DebugLevel = 1; + VerboseLevel = 2; } /// @@ -445,9 +492,9 @@ public void ResetDebugLevels() /// public void SetDebugLevels(uint level) { - DebugTrace = level; - DebugInfo = level; - DebugVerbose = level; + TraceLevel = level; + DebugLevel = level; + VerboseLevel = level; } #endregion From ab18f6168005bbe658efb392de1775bcd1ae50bc Mon Sep 17 00:00:00 2001 From: jdevito Date: Tue, 3 Jan 2023 16:45:59 -0600 Subject: [PATCH 12/16] fix: updated selectscene index reference with -1 offset, allowing for scene recall 1-n --- src/AcuityFrescoDevice.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index 4704b7d..2514722 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -400,11 +400,15 @@ public void InitializeSceneSelectDirectFeedback(List scenes) /// public void SelectScene(int index) { - if (index < 0 || index > Scenes.Count) return; + if (index <= 0 || index > Scenes.Count) + { + Debug.Console(DebugLevel, this, "SelectScene: index-'{0}' is out of range (valid values 1-{1})", index, Scenes.Count); + return; + } Debug.Console(VerboseLevel, this, "SelectScene: index-'{0}'", index); - var scene = Scenes[index]; + var scene = Scenes[index - 1]; if (scene == null) { Debug.Console(DebugLevel, this, "SelectScene: invalid scene index-'{0}'", index); From 08beaa9baedb3f63b5116884a5e911b13d2a1f7c Mon Sep 17 00:00:00 2001 From: jdevito Date: Tue, 3 Jan 2023 17:42:39 -0600 Subject: [PATCH 13/16] fix: replaced custom activate with base initialize method, added debug messaging --- src/AcuityFrescoDevice.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index 2514722..89fe4a2 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -15,7 +15,7 @@ namespace PepperDashPluginAcuityFresco /// public class AcuityFrescoDevice : EssentialsBridgeableDevice { - private const string CommsDelimiter = "\n"; + private const string CommsDelimiter = "\r"; private readonly IBasicCommunication _comms; private readonly GenericCommunicationMonitor _commsMonitor; private GenericQueue _commsRxQueue; @@ -108,6 +108,8 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig _commsMonitor = new GenericCommunicationMonitor(this, _comms, config.PollTimeMs, config.WarningTimeoutMs, config.ErrorTimeoutMs, Poll); _commsMonitor.StatusChange += OnCommunicationMonitorStatusChange; _commsRxQueue = new GenericQueue(key + "-queue"); + + //DeviceManager.AllDevicesActivated += (sender, args) => Debug.Console(VerboseLevel, this, "All devices activated"); OnlineFeedback = _commsMonitor.IsOnlineFeedback; CommunicationMonitorFeedback = new IntFeedback(() => (int)_commsMonitor.Status); @@ -125,16 +127,15 @@ public AcuityFrescoDevice(string key, string name, AcuityFrescoPropertiesConfig Debug.Console(TraceLevel, this, "Constructing new {0} instance complete", name); Debug.Console(TraceLevel, new string('*', 80)); Debug.Console(TraceLevel, new string('*', 80)); - } + } /// /// Initialize plugin device /// public override void Initialize() { - // Essentials will handle the connect method to the device + Debug.Console(VerboseLevel, this, "Initializing {0}", Key); _comms.Connect(); - // Essentialss will handle starting the comms monitor _commsMonitor.Start(); } @@ -282,7 +283,7 @@ private void ProcessResponse(string response) var matches = expression.Match(response); if (!matches.Success) { - Debug.Console(DebugLevel, this, "ProcessResponse: unknown response '{0}', regex match failed", response); + Debug.Console(DebugLevel, this, "ProcessResponse: response '{0}', regex match failed", response.TrimEnd('\r')); return; } @@ -307,6 +308,12 @@ private void ProcessResponse(string response) /// public void SendText(string text) { + if (_comms.IsConnected == false) + { + Debug.Console(DebugLevel, this, Debug.ErrorLogLevel.Warning, "SendText: device comms not connected, unable to send text!"); + return; + } + if (string.IsNullOrEmpty(text)) return; var cmd = string.IsNullOrEmpty(CommsDelimiter) @@ -483,8 +490,8 @@ public void GetScenes() public void ResetDebugLevels() { TraceLevel = 0; - DebugLevel = 1; - VerboseLevel = 2; + DebugLevel = 0; + VerboseLevel = 0; } /// From 1a2aa49e0bc0a922ea245a80787d8c5f4488e4ee Mon Sep 17 00:00:00 2001 From: jdevito Date: Tue, 3 Jan 2023 17:45:15 -0600 Subject: [PATCH 14/16] fix: updated debug level properties --- src/AcuityFrescoDevice.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index 89fe4a2..5c463c0 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -490,8 +490,8 @@ public void GetScenes() public void ResetDebugLevels() { TraceLevel = 0; - DebugLevel = 0; - VerboseLevel = 0; + DebugLevel = 1; + VerboseLevel = 2; } /// From 788838dfceed1177724cc91bf511de1c17396d91 Mon Sep 17 00:00:00 2001 From: jdevito Date: Tue, 3 Jan 2023 18:05:08 -0600 Subject: [PATCH 15/16] fix: added additional messaging for degbugging plugin --- src/AcuityFrescoDevice.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index 5c463c0..cd8223d 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -407,9 +407,9 @@ public void InitializeSceneSelectDirectFeedback(List scenes) /// public void SelectScene(int index) { - if (index <= 0 || index > Scenes.Count) + if (index < 0 || index > Scenes.Count) { - Debug.Console(DebugLevel, this, "SelectScene: index-'{0}' is out of range (valid values 1-{1})", index, Scenes.Count); + Debug.Console(DebugLevel, this, "SelectScene: index-'{0}' is out of range (valid values 0-{1})", index, Scenes.Count); return; } From eabf5ad12c8885991247124c1bb1bb77e80c93d0 Mon Sep 17 00:00:00 2001 From: jdevito Date: Wed, 11 Jan 2023 15:37:20 -0600 Subject: [PATCH 16/16] fix: resolved issue with bridge indexing in LinkToApi that was causing offsets when a scene was selected --- README.md | 150 ++++++++++++++++++++------------------ src/AcuityFrescoDevice.cs | 10 ++- 2 files changed, 84 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 1007aee..f21d9bc 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Com #### Example API Commands -API Command +API Command Structure ```c# 'scene {sceneId} {level} 0 {roomId}' @@ -37,6 +37,12 @@ API Command `level` valid values are 0-100, values < 100 will be applied relative to the current lighting level value `roomId` valid values are A-X, `roomId`'s can be concatenated, for example `ABC` is avalid room ID for combined spaces +Example Command + +```c# +'scene 3 100 0 A' // select scene 3, 100%, in Room with ID 'A' +'scene 8 50 0 AB' // select scene 8, 50% (relative to current light level), in Rooms with ID 'A' & 'B' +``` ### Plugin Configuration Object @@ -44,53 +50,53 @@ Update the configuration object as needed for the plugin being developed. ```json { - "devices": [ - { - "key": "lights-1", - "name": "Acuity Fresco Lighting Scenes", - "type": "acuityfresco", - "group": "pluginDevices", - "properties": { - "control": { - "method": "com", - "controlPortDevKey": "processor", - "controlPortNumber": 1, - "comParams": { - "baudRate": 115200, - "dataBits": 8, - "stopBits": 1, - "parity": "None", - "protocol": "RS232", - "hardwareHandshake": "None", - "softwareHandshake": "None" - } - }, - "pollTimeMs": 30000, - "warningTimeoutMs": 180000, - "errorTimeoutMs": 300000, - "scenes": [ - { - "name": "Scene 1", - "id": 1, - "roomId": "A", - "level": 100 - }, - { - "name": "Scene 2", - "id": 5, - "roomId": "B", - "level": 50 - }, - { - "name": "Scene 3", - "id": 36, - "roomId": "X", - "level": 0 - } - ] - } - } - ] + "devices": [ + { + "key": "lights1", + "name": "Acuity Fresco Lighting Scenes", + "type": "acuityfresco", + "group": "pluginDevices", + "properties": { + "control": { + "method": "com", + "controlPortDevKey": "processor", + "controlPortNumber": 1, + "comParams": { + "baudRate": 115200, + "dataBits": 8, + "stopBits": 1, + "parity": "None", + "protocol": "RS232", + "hardwareHandshake": "None", + "softwareHandshake": "None" + } + }, + "pollTimeMs": 30000, + "warningTimeoutMs": 180000, + "errorTimeoutMs": 300000, + "scenes": [ + { + "name": "Scene 1", + "id": 1, + "roomId": "A", + "level": 100 + }, + { + "name": "Scene 2", + "id": 5, + "roomId": "B", + "level": 50 + }, + { + "name": "Scene 3", + "id": 36, + "roomId": "X", + "level": 0 + } + ] + } + } + ] } ``` @@ -100,30 +106,30 @@ Update the bridge configuration object as needed for the plugin being developed. ```json { - "devices": [ - { - "key": "lights-1-bridge", - "uid": 11, - "name": "Example Plugin Bridge", - "group": "api", - "type": "eiscApiAdvanced", - "properties": { - "control": { - "tcpSshProperties": { - "address": "127.0.0.2", - "port": 0 - }, - "ipid": "B1" - }, - "devices": [ - { - "deviceKey": "lights-1", - "joinStart": 1 - } - ] - } - } - ] + "devices": [ + { + "key": "lights1-bridge", + "uid": 11, + "name": "Example Plugin Bridge", + "group": "api", + "type": "eiscApiAdvanced", + "properties": { + "control": { + "tcpSshProperties": { + "address": "127.0.0.2", + "port": 0 + }, + "ipid": "B1" + }, + "devices": [ + { + "deviceKey": "lights1", + "joinStart": 1 + } + ] + } + } + ] } ``` diff --git a/src/AcuityFrescoDevice.cs b/src/AcuityFrescoDevice.cs index cd8223d..1f3ed09 100644 --- a/src/AcuityFrescoDevice.cs +++ b/src/AcuityFrescoDevice.cs @@ -194,12 +194,14 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join var sceneIndex = 0; foreach (var scene in Scenes) - { - var index = sceneIndex; + { + var index = sceneIndex; var sceneSelectJoin = (uint)(joinMap.SceneSelectDirect.JoinNumber + index); var sceneVisibleJoin = (uint)(joinMap.SceneButtonVisibility.JoinNumber + index); var name = scene.Name; + Debug.Console(VerboseLevel, this, "LinkToApi: SceneSelectJoin-'{2}' | SceneVisibleJoin-'{3}' | SceneIndex-'{0}' > '{1}'", index, name, sceneSelectJoin, sceneVisibleJoin); + trilist.SetString(sceneSelectJoin, string.IsNullOrEmpty(name) ? string.Empty : name); trilist.SetBool(sceneVisibleJoin, string.IsNullOrEmpty(name) == false); @@ -415,7 +417,7 @@ public void SelectScene(int index) Debug.Console(VerboseLevel, this, "SelectScene: index-'{0}'", index); - var scene = Scenes[index - 1]; + var scene = Scenes[index]; if (scene == null) { Debug.Console(DebugLevel, this, "SelectScene: invalid scene index-'{0}'", index); @@ -441,7 +443,7 @@ public void SelectScene(int index) } var cmd = string.Format("scene {0} {1} 0 {2}", scene.Id, scene.Level, scene.RoomId); - Debug.Console(VerboseLevel, this, "SelectScene: cmd-'{0}'", cmd); + Debug.Console(DebugLevel, this, "SelectScene: index-'{0}' cmd-'{1}'", index, cmd); SendText(cmd); }

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 literal 0 HcmV?d00001 diff --git a/images/logo_pdt_no_tagline_600.png b/images/logo_pdt_no_tagline_600.png new file mode 100644 index 0000000000000000000000000000000000000000..6fc11ff1413bf7b6d9ec00319930bc389c9c7317 GIT binary patch literal 350979 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