From f5d6a076ad0becd0d73488a7f3befb6a00beedb1 Mon Sep 17 00:00:00 2001 From: Nick Genovese Date: Wed, 12 Aug 2026 11:22:03 -0400 Subject: [PATCH] feat: Add Mpc4TouchpanelController for MPC4 processors Introduced Mpc4TouchpanelController to manage touchpanel behavior for MPC4 class processors, including button initialization, feedback, and event handling. Updated ControlSystem.cs to detect MPC4 models, deserialize button configs, and register the controller with DeviceManager. --- .../Touchpanels/Mpc4Touchpanel.cs | 307 ++++++++++++++++++ src/PepperDash.Essentials/ControlSystem.cs | 19 ++ 2 files changed, 326 insertions(+) create mode 100644 src/PepperDash.Essentials.Core/Touchpanels/Mpc4Touchpanel.cs diff --git a/src/PepperDash.Essentials.Core/Touchpanels/Mpc4Touchpanel.cs b/src/PepperDash.Essentials.Core/Touchpanels/Mpc4Touchpanel.cs new file mode 100644 index 00000000..cb9db34a --- /dev/null +++ b/src/PepperDash.Essentials.Core/Touchpanels/Mpc4Touchpanel.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Crestron.SimplSharpPro; +using PepperDash.Core; +using PepperDash.Core.Logging; +using Serilog.Events; + +namespace PepperDash.Essentials.Core.Touchpanels +{ + /// + /// A wrapper class for the touchpanel portion of an MPC4 class process to allow for configurable + /// behavior of the keypad buttons + /// + public class Mpc4TouchpanelController : Device + { + readonly MPC4x3XXBase _touchpanel; + + readonly Dictionary _buttons; + + /// + /// Constructor + /// + /// device key + /// device name + /// control system processor + /// dictionary of keypad buttons + public Mpc4TouchpanelController(string key, string name, CrestronControlSystem processor, Dictionary buttons) + : base(key, name) + { + _touchpanel = processor.ControllerTouchScreenSlotDevice as MPC4x3XXBase; + if (_touchpanel == null) + { + Debug.LogMessage(LogEventLevel.Debug, this, "Failed to construct MPC4 Touchpanel Controller with key {0}, check configuration, processor is type: {1}", key, processor.GetType().FullName); + return; + } + + if (_touchpanel.Registerable) + { + var registrationResponse = _touchpanel.Register(); + Debug.LogMessage(LogEventLevel.Information, this, "touchpanel registration response: {0}", registrationResponse); + } + + _touchpanel.BaseEvent += Touchpanel_BaseEvent; + _touchpanel.ButtonStateChange += Touchpanel_ButtonStateChange; + _touchpanel.PanelStateChange += Touchpanel_PanelStateChange; + + _buttons = buttons; + if (_buttons == null) + { + Debug.LogMessage(LogEventLevel.Debug, this, + "Button properties are null, failed to setup MPC4 Touch Controller, check configuration"); + return; + } + + AddPostActivationAction(() => + { + foreach (var button in _buttons) + { + var buttonKey = button.Key.ToLower(); + var buttonConfig = button.Value; + + InitializeButton(buttonKey, buttonConfig); + InitializeButtonFeedback(buttonKey, buttonConfig); + } + + ListButtons(); + }); + } + + /// + /// Enables/disables buttons based on event type configuration + /// + /// + /// + public void InitializeButton(string key, KeypadButton config) + { + if (config == null) + { + Debug.LogMessage(LogEventLevel.Debug, this, "Button '{0}' config is null, unable to initialize", key); + return; + } + + TryParseInt(key, out int buttonNumber); + + var buttonEventTypes = config.EventTypes; + BoolOutputSig enabledFb = null; + BoolOutputSig disabledFb = null; + + switch (key) + { + case ("power"): + { + if (buttonEventTypes == null || buttonEventTypes.Keys == null) + _touchpanel.DisablePowerButton(); + else + _touchpanel.EnablePowerButton(); + + + enabledFb = _touchpanel.PowerButtonEnabledFeedBack; + disabledFb = _touchpanel.PowerButtonDisabledFeedBack; + + break; + } + case ("mute"): + { + if (buttonEventTypes == null || buttonEventTypes.Keys == null) + _touchpanel.DisableMuteButton(); + else + _touchpanel.EnableMuteButton(); + + + enabledFb = _touchpanel.MuteButtonEnabledFeedBack; + disabledFb = _touchpanel.MuteButtonDisabledFeedBack; + + break; + } + default: + { + if (buttonNumber == 0 || buttonNumber > 9) + break; + + if (buttonEventTypes == null || buttonEventTypes.Keys == null) + _touchpanel.DisableNumericalButton((uint)buttonNumber); + else + _touchpanel.EnableNumericalButton((uint)buttonNumber); + + + if (_touchpanel.NumericalButtonEnabledFeedBack != null) + enabledFb = _touchpanel.NumericalButtonEnabledFeedBack[(uint)buttonNumber]; + + if (_touchpanel.NumericalButtonDisabledFeedBack != null) + disabledFb = _touchpanel.NumericalButtonDisabledFeedBack[(uint)buttonNumber]; + + break; + } + } + + Debug.LogMessage(LogEventLevel.Information, this, "InitializeButton: key-'{0}' enabledFb-'{1}', disabledFb-'{2}'", + key, enabledFb ?? (object)"null", disabledFb ?? (object)"null"); + } + + /// + /// Links button feedback if configured + /// + /// + /// + public void InitializeButtonFeedback(string key, KeypadButton config) + { + if (config == null) + { + Debug.LogMessage(LogEventLevel.Debug, this, "Button '{0}' config is null, skipping.", key); + return; + } + + TryParseInt(key, out int buttonNumber); + + // Link up the button feedbacks to the specified device feedback + var buttonFeedback = config.Feedback; + if (buttonFeedback == null || string.IsNullOrEmpty(buttonFeedback.DeviceKey)) + { + Debug.LogMessage(LogEventLevel.Debug, this, "Button '{0}' feedback not configured, skipping.", + key); + return; + } + + Feedback deviceFeedback; + + try + { + if (!(DeviceManager.GetDeviceForKey(buttonFeedback.DeviceKey) is Device device)) + { + Debug.LogMessage(LogEventLevel.Debug, this, "Button '{0}' feedback deviceKey '{1}' not found.", + key, buttonFeedback.DeviceKey); + return; + } + + deviceFeedback = device.GetFeedbackProperty(buttonFeedback.FeedbackName); + if (deviceFeedback == null) + { + Debug.LogMessage(LogEventLevel.Debug, this, "Button '{0}' feedbackName property '{1}' not found.", + key, buttonFeedback.FeedbackName); + return; + } + } + catch (Exception ex) + { + Debug.LogMessage(LogEventLevel.Debug, this, "InitializeButtonFeedback (button '{1}', deviceKey '{2}') Exception Message: {0}", + ex.Message, key, buttonFeedback.DeviceKey); + Debug.LogMessage(LogEventLevel.Verbose, this, "InitializeButtonFeedback (button '{1}', deviceKey '{2}') Exception StackTrace: {0}", + ex.StackTrace, key, buttonFeedback.DeviceKey); + if (ex.InnerException != null) Debug.LogMessage(LogEventLevel.Verbose, this, "InitializeButtonFeedback (button '{1}', deviceKey '{2}') InnerException: {0}", + ex.InnerException, key, buttonFeedback.DeviceKey); + + return; + } + + var boolFeedback = deviceFeedback as BoolFeedback; + + switch (key) + { + case ("power"): + { + boolFeedback?.LinkCrestronFeedback(_touchpanel.FeedbackPower); + break; + } + case ("volumeup"): + case ("volumedown"): + case ("volumefeedback"): + { + if (deviceFeedback is IntFeedback intFeedback) + { + var volumeFeedback = intFeedback; + volumeFeedback.LinkInputSig(_touchpanel.VolumeBargraph); + } + break; + } + case ("mute"): + { + boolFeedback?.LinkCrestronFeedback(_touchpanel.FeedbackMute); + break; + } + default: + { + boolFeedback?.LinkCrestronFeedback(_touchpanel.Feedbacks[(uint)buttonNumber]); + break; + } + } + } + + /// + /// Try parse int helper method + /// + /// + /// + /// + public bool TryParseInt(string str, out int result) + { + try + { + result = int.Parse(str); + return true; + } + catch + { + result = 0; + return false; + } + } + + private void Touchpanel_BaseEvent(GenericBase device, BaseEventArgs args) + { + Debug.LogMessage(LogEventLevel.Debug, this, "BaseEvent: eventId-'{0}', index-'{1}'", args.EventId, args.Index); + } + + private void Touchpanel_ButtonStateChange(GenericBase device, Crestron.SimplSharpPro.DeviceSupport.ButtonEventArgs args) + { + Debug.LogMessage(LogEventLevel.Debug, this, "ButtonStateChange: buttonNumber-'{0}' buttonName-'{1}', buttonState-'{2}'", args.Button.Number, args.Button.Name, args.NewButtonState); + var type = args.NewButtonState.ToString(); + + if (_buttons.ContainsKey(args.Button.Number.ToString(CultureInfo.InvariantCulture))) + { + Press(args.Button.Number.ToString(CultureInfo.InvariantCulture), type); + } + else if (_buttons.ContainsKey(args.Button.Name.ToString())) + { + Press(args.Button.Name.ToString(), type); + } + } + + private void Touchpanel_PanelStateChange(GenericBase device, BaseEventArgs args) + { + Debug.LogMessage(LogEventLevel.Debug, this, "PanelStateChange: eventId-'{0}', index-'{1}'", args.EventId, args.Index); + } + + /// + /// Runs the function associated with this button/type. One of the following strings: + /// Pressed, Released, Tapped, DoubleTapped, Held, HeldReleased + /// + /// + /// + public void Press(string buttonKey, string type) + { + this.LogVerbose("Press: buttonKey-'{buttonKey}', type-'{type}'", buttonKey, type); + + if (!_buttons.ContainsKey(buttonKey)) return; + + var button = _buttons[buttonKey]; + if (!button.EventTypes.ContainsKey(type)) return; + + foreach (var eventType in button.EventTypes[type]) DeviceJsonApi.DoDeviceAction(eventType); + } + + /// + /// ListButtons method + /// + public void ListButtons() + { + this.LogVerbose("MPC4 Controller {0} - Available Buttons", Key); + + foreach (var button in _buttons) + { + this.LogVerbose("Key: {key}", button.Key); + } + } + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials/ControlSystem.cs b/src/PepperDash.Essentials/ControlSystem.cs index 47de4d3e..f2888b8a 100644 --- a/src/PepperDash.Essentials/ControlSystem.cs +++ b/src/PepperDash.Essentials/ControlSystem.cs @@ -436,6 +436,25 @@ namespace PepperDash.Essentials "WARNING: Config file defines processor type as '{deviceType:l}' but actual processor is '{processorType:l}'! Some ports may not be available", devConf.Type.ToUpper(), Global.ControlSystem.ControllerPrompt.ToUpper()); + // Check if the processor is an MPC4 model + if (prompt.IndexOf("mpc4", StringComparison.OrdinalIgnoreCase) > -1) + { + Debug.LogMessage(LogEventLevel.Information, "MPC4 processor type detected. Adding Mpc4TouchpanelController."); + + var butToken = devConf.Properties["buttons"]; + if (butToken == null) + { + Debug.LogMessage(LogEventLevel.Warning, + "Error: Unable to deserialize buttons collection for device: {deviceKey}", devConf.Key); + continue; + } + + var buttons = Newtonsoft.Json.JsonConvert.DeserializeObject>(butToken.ToString()); + var tpController = new Core.Touchpanels.Mpc4TouchpanelController( + string.Format("{0}-keypadButtons", devConf.Key), devConf.Name, Global.ControlSystem, buttons); + + DeviceManager.AddDevice(tpController); + } continue; }