From 1cf77ba179de388e31cf5501925652a0aa0b9391 Mon Sep 17 00:00:00 2001 From: Ryan Wagoner Date: Thu, 5 Nov 2020 21:59:30 -0500 Subject: [PATCH] 1.1.9 - Add MQTT system trouble, thermostat type, and area alarm activation --- OmniLinkBridge/MQTT/AlarmCommands.cs | 9 ++ OmniLinkBridge/MQTT/MappingExtensions.cs | 75 +++++------ OmniLinkBridge/MQTT/MessageProcessor.cs | 23 +++- OmniLinkBridge/MQTT/Topic.cs | 3 +- OmniLinkBridge/Modules/LoggerModule.cs | 2 +- OmniLinkBridge/Modules/MQTTModule.cs | 55 +++++++- OmniLinkBridge/Modules/OmniLinkII.cs | 123 ++++++++++++------ OmniLinkBridge/OmniLink/SystemEventType.cs | 21 +++ .../OmniLink/SystemStatusEventArgs.cs | 3 +- OmniLinkBridge/OmniLinkBridge.csproj | 12 +- OmniLinkBridge/Properties/AssemblyInfo.cs | 4 +- OmniLinkBridgeTest/MQTTTest.cs | 64 +++++++++ OmniLinkBridgeTest/Mock/MockOmniLinkII.cs | 8 +- .../Mock/SendCommandEventArgs.cs | 10 +- OmniLinkBridgeTest/OmniLinkBridgeTest.csproj | 2 + README.md | 20 +++ 16 files changed, 331 insertions(+), 103 deletions(-) create mode 100644 OmniLinkBridge/MQTT/AlarmCommands.cs create mode 100644 OmniLinkBridge/OmniLink/SystemEventType.cs diff --git a/OmniLinkBridge/MQTT/AlarmCommands.cs b/OmniLinkBridge/MQTT/AlarmCommands.cs new file mode 100644 index 0000000..ce12964 --- /dev/null +++ b/OmniLinkBridge/MQTT/AlarmCommands.cs @@ -0,0 +1,9 @@ +namespace OmniLinkBridge.MQTT +{ + enum AlarmCommands + { + burglary = 1, + fire = 2, + auxiliary = 3 + } +} diff --git a/OmniLinkBridge/MQTT/MappingExtensions.cs b/OmniLinkBridge/MQTT/MappingExtensions.cs index 04f705e..54f0dd0 100644 --- a/OmniLinkBridge/MQTT/MappingExtensions.cs +++ b/OmniLinkBridge/MQTT/MappingExtensions.cs @@ -1,5 +1,6 @@ using HAI_Shared; using Newtonsoft.Json; +using System.Collections.Generic; namespace OmniLinkBridge.MQTT { @@ -31,24 +32,16 @@ namespace OmniLinkBridge.MQTT else if (area.ExitTimer > 0) return "pending"; - switch (area.AreaMode) + return area.AreaMode switch { - case enuSecurityMode.Night: - return "armed_night"; - case enuSecurityMode.NightDly: - return "armed_night_delay"; - case enuSecurityMode.Day: - return "armed_home"; - case enuSecurityMode.DayInst: - return "armed_home_instant"; - case enuSecurityMode.Away: - return "armed_away"; - case enuSecurityMode.Vacation: - return "armed_vacation"; - case enuSecurityMode.Off: - default: - return "disarmed"; - } + enuSecurityMode.Night => "armed_night", + enuSecurityMode.NightDly => "armed_night_delay", + enuSecurityMode.Day => "armed_home", + enuSecurityMode.DayInst => "armed_home_instant", + enuSecurityMode.Away => "armed_away", + enuSecurityMode.Vacation => "armed_vacation", + _ => "disarmed", + }; } public static string ToBasicState(this clsArea area) @@ -196,32 +189,16 @@ namespace OmniLinkBridge.MQTT temperature_alarm = area.AreaAlarms.IsBitSet(7) }; - switch (area.AreaMode) + state.mode = area.AreaMode switch { - case enuSecurityMode.Night: - state.mode = "night"; - break; - case enuSecurityMode.NightDly: - state.mode = "night_delay"; - break; - case enuSecurityMode.Day: - state.mode = "home"; - break; - case enuSecurityMode.DayInst: - state.mode = "home_instant"; - break; - case enuSecurityMode.Away: - state.mode = "away"; - break; - case enuSecurityMode.Vacation: - state.mode = "vacation"; - break; - case enuSecurityMode.Off: - default: - state.mode = "off"; - break; - } - + enuSecurityMode.Night => "night", + enuSecurityMode.NightDly => "night_delay", + enuSecurityMode.Day => "home", + enuSecurityMode.DayInst => "home_instant", + enuSecurityMode.Away => "away", + enuSecurityMode.Vacation => "vacation", + _ => "off", + }; return JsonConvert.SerializeObject(state); } @@ -456,9 +433,19 @@ namespace OmniLinkBridge.MQTT public static Climate ToConfig(this clsThermostat thermostat, enuTempFormat format) { - Climate ret = new Climate(); + Climate ret = new Climate + { + modes = thermostat.Type switch + { + enuThermostatType.AutoHeatCool => new List(new string[] { "auto", "off", "cool", "heat" }), + enuThermostatType.HeatCool => new List(new string[] { "off", "cool", "heat" }), + enuThermostatType.HeatOnly => new List(new string[] { "off", "heat" }), + enuThermostatType.CoolOnly => new List(new string[] { "off", "cool" }), + _ => new List(new string[] { "off" }), + } + }; - if(format == enuTempFormat.Celsius) + if (format == enuTempFormat.Celsius) { ret.min_temp = "7"; ret.max_temp = "35"; diff --git a/OmniLinkBridge/MQTT/MessageProcessor.cs b/OmniLinkBridge/MQTT/MessageProcessor.cs index d62c470..c025886 100644 --- a/OmniLinkBridge/MQTT/MessageProcessor.cs +++ b/OmniLinkBridge/MQTT/MessageProcessor.cs @@ -72,6 +72,16 @@ namespace OmniLinkBridge.MQTT log.Debug("SetArea: {id} to {value}", area.Number, cmd.ToString().Replace("arm_", "").Replace("_", " ")); OmniLink.SendCommand(AreaMapping[cmd], 0, (ushort)area.Number); } + else if (command == Topic.alarm_command && area.Number > 0 && Enum.TryParse(payload, true, out AlarmCommands alarm)) + { + log.Debug("SetAreaAlarm: {id} to {value}", area.Number, payload); + + OmniLink.Controller.Connection.Send(new clsOL2MsgActivateKeypadEmg(OmniLink.Controller.Connection) + { + Area = (byte)area.Number, + EmgType = (byte)alarm + }, (M, B, Timeout) => { }); + } } private static readonly IDictionary ZoneMapping = new Dictionary @@ -169,8 +179,17 @@ namespace OmniLinkBridge.MQTT } else if (command == Topic.mode_command && Enum.TryParse(payload, true, out enuThermostatMode mode)) { - log.Debug("SetThermostatMode: {id} to {value}", thermostat.Number, payload); - OmniLink.SendCommand(enuUnitCommand.Mode, BitConverter.GetBytes((int)mode)[0], (ushort)thermostat.Number); + if (thermostat.Type == enuThermostatType.AutoHeatCool || + (thermostat.Type == enuThermostatType.HeatCool && mode != enuThermostatMode.Auto) || + (thermostat.Type == enuThermostatType.CoolOnly && + (mode == enuThermostatMode.Off || mode == enuThermostatMode.Cool)) || + (thermostat.Type == enuThermostatType.HeatOnly && + (mode == enuThermostatMode.Off || mode == enuThermostatMode.Heat || mode == enuThermostatMode.E_Heat)) || + mode == enuThermostatMode.Off) + { + log.Debug("SetThermostatMode: {id} to {value}", thermostat.Number, payload); + OmniLink.SendCommand(enuUnitCommand.Mode, BitConverter.GetBytes((int)mode)[0], (ushort)thermostat.Number); + } } else if (command == Topic.fan_mode_command && Enum.TryParse(payload, true, out enuThermostatFanMode fanMode)) { diff --git a/OmniLinkBridge/MQTT/Topic.cs b/OmniLinkBridge/MQTT/Topic.cs index f540fdc..793bff7 100644 --- a/OmniLinkBridge/MQTT/Topic.cs +++ b/OmniLinkBridge/MQTT/Topic.cs @@ -5,6 +5,7 @@ name, state, command, + alarm_command, basic_state, json_state, brightness_state, @@ -27,6 +28,6 @@ fan_mode_state, fan_mode_command, hold_state, - hold_command + hold_command, } } diff --git a/OmniLinkBridge/Modules/LoggerModule.cs b/OmniLinkBridge/Modules/LoggerModule.cs index f17dbfb..45a7917 100644 --- a/OmniLinkBridge/Modules/LoggerModule.cs +++ b/OmniLinkBridge/Modules/LoggerModule.cs @@ -298,7 +298,7 @@ namespace OmniLinkBridge.Modules VALUES ('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + e.Type.ToString() + "','" + e.Value + "')"); if (Global.verbose_event) - log.Verbose("SystemEvent {name} {status}", e.Type.ToString(), e.Value); + log.Verbose("SystemEvent {name}, Status: {status}", e.Type.ToString(), e.Value); if (e.SendNotification) Notification.Notify("SystemEvent", e.Type.ToString() + " " + e.Value); diff --git a/OmniLinkBridge/Modules/MQTTModule.cs b/OmniLinkBridge/Modules/MQTTModule.cs index 7aecad6..2e0f640 100644 --- a/OmniLinkBridge/Modules/MQTTModule.cs +++ b/OmniLinkBridge/Modules/MQTTModule.cs @@ -47,6 +47,7 @@ namespace OmniLinkBridge.Modules OmniLink.OnThermostatStatus += Omnilink_OnThermostatStatus; OmniLink.OnButtonStatus += OmniLink_OnButtonStatus; OmniLink.OnMessageStatus += OmniLink_OnMessageStatus; + OmniLink.OnSystemStatus += OmniLink_OnSystemStatus; MessageProcessor = new MessageProcessor(omni); } @@ -105,6 +106,7 @@ namespace OmniLinkBridge.Modules List toSubscribe = new List() { Topic.command, + Topic.alarm_command, Topic.brightness_command, Topic.scene_command, Topic.temperature_heat_command, @@ -117,7 +119,7 @@ namespace OmniLinkBridge.Modules }; toSubscribe.ForEach((command) => MqttClient.SubscribeAsync( - new TopicFilterBuilder().WithTopic($"{Global.mqtt_prefix}/+/{command}").Build())); + new MqttTopicFilterBuilder().WithTopic($"{Global.mqtt_prefix}/+/{command}").Build())); // Wait until shutdown trigger.WaitOne(); @@ -156,6 +158,7 @@ namespace OmniLinkBridge.Modules private void PublishConfig() { + PublishSystem(); PublishAreas(); PublishZones(); PublishUnits(); @@ -168,6 +171,41 @@ namespace OmniLinkBridge.Modules PublishAsync($"{Global.mqtt_prefix}/version", OmniLink.Controller.GetVersionText()); } + private void PublishSystem() + { + log.Debug("Publishing {type}", "system"); + + PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/{Global.mqtt_prefix}/system_phone/config", + JsonConvert.SerializeObject(SystemTroubleConfig("phone", "Phone"))); + PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/{Global.mqtt_prefix}/system_ac/config", + JsonConvert.SerializeObject(SystemTroubleConfig("ac", "AC"))); + PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/{Global.mqtt_prefix}/system_battery/config", + JsonConvert.SerializeObject(SystemTroubleConfig("battery", "Battery"))); + PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/{Global.mqtt_prefix}/system_dcm/config", + JsonConvert.SerializeObject(SystemTroubleConfig("dcm", "DCM"))); + + PublishAsync(SystemTroubleTopic("phone"), OmniLink.TroublePhone ? "trouble" : "secure"); + PublishAsync(SystemTroubleTopic("ac"), OmniLink.TroubleAC ? "trouble" : "secure"); + PublishAsync(SystemTroubleTopic("battery"), OmniLink.TroubleBattery ? "trouble" : "secure"); + PublishAsync(SystemTroubleTopic("dcn"), OmniLink.TroubleDCM ? "trouble" : "secure"); + } + + public string SystemTroubleTopic(string type) + { + return $"{Global.mqtt_prefix}/system/{type}/{Topic.state}"; + } + + public BinarySensor SystemTroubleConfig(string type, string name) + { + return new BinarySensor + { + unique_id = $"{Global.mqtt_prefix}system{type}", + name = $"{Global.mqtt_discovery_name_prefix} System {name}", + state_topic = SystemTroubleTopic(type), + device_class = BinarySensor.DeviceClass.problem + }; + } + private void PublishAreas() { log.Debug("Publishing {type}", "areas"); @@ -441,6 +479,21 @@ namespace OmniLinkBridge.Modules PublishMessageState(e.Message); } + private void OmniLink_OnSystemStatus(object sender, SystemStatusEventArgs e) + { + if (!MqttClient.IsConnected) + return; + + if(e.Type == SystemEventType.Phone) + PublishAsync(SystemTroubleTopic("phone"), e.Trouble ? "trouble" : "secure"); + else if (e.Type == SystemEventType.AC) + PublishAsync(SystemTroubleTopic("ac"), e.Trouble ? "trouble" : "secure"); + else if (e.Type == SystemEventType.Button) + PublishAsync(SystemTroubleTopic("battery"), e.Trouble ? "trouble" : "secure"); + else if (e.Type == SystemEventType.DCM) + PublishAsync(SystemTroubleTopic("dcm"), e.Trouble ? "trouble" : "secure"); + } + private void PublishAreaState(clsArea area) { PublishAsync(area.ToTopic(Topic.state), area.ToState()); diff --git a/OmniLinkBridge/Modules/OmniLinkII.cs b/OmniLinkBridge/Modules/OmniLinkII.cs index 07125e0..c08949d 100644 --- a/OmniLinkBridge/Modules/OmniLinkII.cs +++ b/OmniLinkBridge/Modules/OmniLinkII.cs @@ -21,6 +21,11 @@ namespace OmniLinkBridge.Modules public clsHAC Controller { get; private set; } private DateTime retry = DateTime.MinValue; + public bool TroublePhone { get; set; } + public bool TroubleAC { get; set; } + public bool TroubleBattery { get; set; } + public bool TroubleDCM { get; set; } + // Thermostats private readonly Dictionary tstats = new Dictionary(); private readonly System.Timers.Timer tstat_timer = new System.Timers.Timer(); @@ -254,6 +259,7 @@ namespace OmniLinkBridge.Modules log.Debug("Retrieving named units"); await GetSystemFormats(); + await GetSystemTroubles(); await GetNamed(enuObjectType.Area); await GetNamed(enuObjectType.Zone); await GetNamed(enuObjectType.Thermostat); @@ -266,7 +272,20 @@ namespace OmniLinkBridge.Modules { log.Debug("Waiting for system formats"); - clsOL2MsgRequestSystemFormats MSG = new clsOL2MsgRequestSystemFormats(Controller.Connection); + var MSG = new clsOL2MsgRequestSystemFormats(Controller.Connection); + Controller.Connection.Send(MSG, HandleNamedPropertiesResponse); + + await Task.Run(() => + { + nameWait.WaitOne(new TimeSpan(0, 0, 10)); + }); + } + + private async Task GetSystemTroubles() + { + log.Debug("Waiting for system troubles"); + + var MSG = new clsOL2MsgRequestSystemTroubles(Controller.Connection); Controller.Connection.Send(MSG, HandleNamedPropertiesResponse); await Task.Run(() => @@ -289,7 +308,7 @@ namespace OmniLinkBridge.Modules private void GetNextNamed(enuObjectType type, int ix) { - clsOL2MsgRequestProperties MSG = new clsOL2MsgRequestProperties(Controller.Connection) + var MSG = new clsOL2MsgRequestProperties(Controller.Connection) { ObjectType = type, IndexNumber = (UInt16)ix, @@ -315,16 +334,26 @@ namespace OmniLinkBridge.Modules nameWait.Set(); break; case enuOmniLink2MessageType.SystemFormats: - clsOL2MsgSystemFormats MSG2 = new clsOL2MsgSystemFormats(Controller.Connection, B); + var systemFormats = new clsOL2MsgSystemFormats(Controller.Connection, B); - Controller.DateFormat = MSG2.Date; - Controller.TimeFormat = MSG2.Time; - Controller.TempFormat = MSG2.Temp; + Controller.DateFormat = systemFormats.Date; + Controller.TimeFormat = systemFormats.Time; + Controller.TempFormat = systemFormats.Temp; using (LogContext.PushProperty("Telemetry", "TemperatureFormat")) log.Debug("Temperature format is {TemperatureFormat}", (Controller.TempFormat == enuTempFormat.Fahrenheit ? "Fahrenheit" : "Celsius")); + nameWait.Set(); + break; + case enuOmniLink2MessageType.SystemTroubles: + var systemTroubles = new clsOL2MsgSystemTroubles(Controller.Connection, B); + + TroublePhone = systemTroubles.Contains(enuTroubles.PhoneLine); + TroubleAC = systemTroubles.Contains(enuTroubles.AC); + TroubleBattery = systemTroubles.Contains(enuTroubles.BatteryLow); + TroubleDCM = systemTroubles.Contains(enuTroubles.DCM); + nameWait.Set(); break; case enuOmniLink2MessageType.Properties: @@ -473,7 +502,7 @@ namespace OmniLinkBridge.Modules if (MSG.SystemEvent >= 1 && MSG.SystemEvent <= 255) { - eventargs.Type = enuEventType.USER_MACRO_BUTTON; + eventargs.Type = SystemEventType.Button; eventargs.Value = ((int)MSG.SystemEvent).ToString() + " " + Controller.Buttons[MSG.SystemEvent].Name; OnSystemStatus?.Invoke(this, eventargs); @@ -484,87 +513,103 @@ namespace OmniLinkBridge.Modules Button = Controller.Buttons[MSG.SystemEvent] }); } - else if (MSG.SystemEvent >= 768 && MSG.SystemEvent <= 771) + else if (MSG.SystemEvent >= (ushort)enuEventType.PHONE_LINE_DEAD && + MSG.SystemEvent <= (ushort)enuEventType.PHONE_LINE_ON_HOOK) { - eventargs.Type = enuEventType.PHONE_; + eventargs.Type = SystemEventType.Phone; - if (MSG.SystemEvent == 768) + if (MSG.SystemEvent == (ushort)enuEventType.PHONE_) { eventargs.Value = "DEAD"; + eventargs.Trouble = true; eventargs.SendNotification = true; } - else if (MSG.SystemEvent == 769) + else if (MSG.SystemEvent == (ushort)enuEventType.PHONE_LINE_RING) eventargs.Value = "RING"; - else if (MSG.SystemEvent == 770) + else if (MSG.SystemEvent == (ushort)enuEventType.PHONE_LINE_OFF_HOOK) eventargs.Value = "OFF HOOK"; - else if (MSG.SystemEvent == 771) + else if (MSG.SystemEvent == (ushort)enuEventType.PHONE_LINE_ON_HOOK) eventargs.Value = "ON HOOK"; OnSystemStatus?.Invoke(this, eventargs); } - else if (MSG.SystemEvent >= 772 && MSG.SystemEvent <= 773) + else if (MSG.SystemEvent >= (ushort)enuEventType.AC_POWER_OFF && + MSG.SystemEvent <= (ushort)enuEventType.AC_POWER_RESTORED) { - eventargs.Type = enuEventType.AC_POWER_; + eventargs.Type = SystemEventType.AC; eventargs.SendNotification = true; - if (MSG.SystemEvent == 772) + if (MSG.SystemEvent == (ushort)enuEventType.AC_POWER_OFF) + { eventargs.Value = "OFF"; - else if (MSG.SystemEvent == 773) + eventargs.Trouble = true; + } + else if (MSG.SystemEvent == (ushort)enuEventType.AC_POWER_RESTORED) eventargs.Value = "RESTORED"; OnSystemStatus?.Invoke(this, eventargs); } - else if (MSG.SystemEvent >= 774 && MSG.SystemEvent <= 775) + else if (MSG.SystemEvent >= (ushort)enuEventType.BATTERY_LOW && + MSG.SystemEvent <= (ushort)enuEventType.BATTERY_OK) { - eventargs.Type = enuEventType.BATTERY_; + eventargs.Type = SystemEventType.Battery; eventargs.SendNotification = true; - if (MSG.SystemEvent == 774) + if (MSG.SystemEvent == (ushort)enuEventType.BATTERY_LOW) + { eventargs.Value = "LOW"; - else if (MSG.SystemEvent == 775) + eventargs.Trouble = true; + } + else if (MSG.SystemEvent == (ushort)enuEventType.BATTERY_OK) eventargs.Value = "OK"; OnSystemStatus?.Invoke(this, eventargs); } - else if (MSG.SystemEvent >= 776 && MSG.SystemEvent <= 777) + else if (MSG.SystemEvent >= (ushort)enuEventType.DCM_TROUBLE && + MSG.SystemEvent <= (ushort)enuEventType.DCM_OK) { - eventargs.Type = enuEventType.DCM_; + eventargs.Type = SystemEventType.DCM; + eventargs.SendNotification = true; - if (MSG.SystemEvent == 776) + if (MSG.SystemEvent == (ushort)enuEventType.DCM_TROUBLE) + { eventargs.Value = "TROUBLE"; - else if (MSG.SystemEvent == 777) + eventargs.Trouble = true; + } + else if (MSG.SystemEvent == (ushort)enuEventType.DCM_OK) eventargs.Value = "OK"; OnSystemStatus?.Invoke(this, eventargs); } - else if (MSG.SystemEvent >= 778 && MSG.SystemEvent <= 781) + else if (MSG.SystemEvent >= (ushort)enuEventType.ENERGY_COST_LOW && + MSG.SystemEvent <= (ushort)enuEventType.ENERGY_COST_CRITICAL) { - eventargs.Type = enuEventType.ENERGY_COST_; + eventargs.Type = SystemEventType.EnergyCost; - if (MSG.SystemEvent == 778) + if (MSG.SystemEvent == (ushort)enuEventType.ENERGY_COST_LOW) eventargs.Value = "LOW"; - else if (MSG.SystemEvent == 779) + else if (MSG.SystemEvent == (ushort)enuEventType.ENERGY_COST_MID) eventargs.Value = "MID"; - else if (MSG.SystemEvent == 780) + else if (MSG.SystemEvent == (ushort)enuEventType.ENERGY_COST_HIGH) eventargs.Value = "HIGH"; - else if (MSG.SystemEvent == 781) + else if (MSG.SystemEvent == (ushort)enuEventType.ENERGY_COST_CRITICAL) eventargs.Value = "CRITICAL"; OnSystemStatus?.Invoke(this, eventargs); } else if (MSG.SystemEvent >= 782 && MSG.SystemEvent <= 787) { - eventargs.Type = enuEventType.CAMERA; + eventargs.Type = SystemEventType.Camera; eventargs.Value = (MSG.SystemEvent - 781).ToString(); OnSystemStatus?.Invoke(this, eventargs); } else if (MSG.SystemEvent >= 61440 && MSG.SystemEvent <= 64511) { - eventargs.Type = enuEventType.SWITCH_PRESS; - int state = (int)MSG.Data[1] - 240; - int id = (int)MSG.Data[2]; + eventargs.Type = SystemEventType.SwitchPress; + int state = MSG.Data[1] - 240; + int id = MSG.Data[2]; eventargs.Value = "Unit: " + id + ", State: " + state; @@ -572,9 +617,9 @@ namespace OmniLinkBridge.Modules } else if (MSG.SystemEvent >= 64512 && MSG.SystemEvent <= 65535) { - eventargs.Type = enuEventType.UPB_LINK; - int state = (int)MSG.Data[1] - 252; - int id = (int)MSG.Data[2]; + eventargs.Type = SystemEventType.UPBLink; + int state = MSG.Data[1] - 252; + int id = MSG.Data[2]; eventargs.Value = "Link: " + id + ", State: " + state; @@ -587,7 +632,7 @@ namespace OmniLinkBridge.Modules sb.Append(MSG.Data[i].ToString() + " "); log.Debug("Unhandled SystemEvent Raw: {raw}, Num: {num}", sb.ToString(), MSG.SystemEvent); - int num = ((int)MSG.MessageLength - 1) / 2; + int num = (MSG.MessageLength - 1) / 2; for (int i = 0; i < num; i++) { log.Debug("Unhandled SystemEvent: " + diff --git a/OmniLinkBridge/OmniLink/SystemEventType.cs b/OmniLinkBridge/OmniLink/SystemEventType.cs new file mode 100644 index 0000000..eb57ab8 --- /dev/null +++ b/OmniLinkBridge/OmniLink/SystemEventType.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OmniLinkBridge.OmniLink +{ + public enum SystemEventType + { + Button, + Phone, + AC, + Battery, + DCM, + EnergyCost, + Camera, + SwitchPress, + UPBLink, + } +} diff --git a/OmniLinkBridge/OmniLink/SystemStatusEventArgs.cs b/OmniLinkBridge/OmniLink/SystemStatusEventArgs.cs index 5a62f5e..07116da 100644 --- a/OmniLinkBridge/OmniLink/SystemStatusEventArgs.cs +++ b/OmniLinkBridge/OmniLink/SystemStatusEventArgs.cs @@ -5,8 +5,9 @@ namespace OmniLinkBridge.OmniLink { public class SystemStatusEventArgs : EventArgs { - public enuEventType Type { get; set; } + public SystemEventType Type { get; set; } public string Value { get; set; } + public bool Trouble { get; set; } public bool SendNotification { get; set; } } } diff --git a/OmniLinkBridge/OmniLinkBridge.csproj b/OmniLinkBridge/OmniLinkBridge.csproj index c3c7d1e..09a164d 100644 --- a/OmniLinkBridge/OmniLinkBridge.csproj +++ b/OmniLinkBridge/OmniLinkBridge.csproj @@ -24,6 +24,7 @@ prompt 4 false + 8.0 x86 @@ -34,6 +35,7 @@ prompt 4 false + 8.0 true @@ -44,6 +46,7 @@ prompt MinimumRecommendedRules.ruleset IDE1006 + 8.0 bin\Release\ @@ -54,6 +57,7 @@ prompt MinimumRecommendedRules.ruleset IDE1006 + 8.0 true @@ -79,6 +83,7 @@ + @@ -103,6 +108,7 @@ + @@ -167,13 +173,13 @@ 4.5.0 - 3.0.8 + 3.0.13 12.0.3 - 2.9.0 + 2.10.0 1.1.0 @@ -188,7 +194,7 @@ 4.1.0 - 5.2.0 + 7.2.0 diff --git a/OmniLinkBridge/Properties/AssemblyInfo.cs b/OmniLinkBridge/Properties/AssemblyInfo.cs index d89c221..2b0dbb2 100644 --- a/OmniLinkBridge/Properties/AssemblyInfo.cs +++ b/OmniLinkBridge/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.8.0")] -[assembly: AssemblyFileVersion("1.1.8.0")] +[assembly: AssemblyVersion("1.1.9.0")] +[assembly: AssemblyFileVersion("1.1.9.0")] diff --git a/OmniLinkBridgeTest/MQTTTest.cs b/OmniLinkBridgeTest/MQTTTest.cs index f22bebe..1f1238c 100644 --- a/OmniLinkBridgeTest/MQTTTest.cs +++ b/OmniLinkBridgeTest/MQTTTest.cs @@ -133,6 +133,70 @@ namespace OmniLinkBridgeTest check(1, "50", enuUnitCommand.Level, 50); } + [TestMethod] + public void ThermostatModeCommandInvalid() + { + SendCommandEventArgs actual = null; + omniLink.OnSendCommand += (sender, e) => { actual = e; }; + + omniLink.Controller.Thermostats[1].Type = enuThermostatType.HeatCool; + messageProcessor.Process($"omnilink/thermostat1/mode_command", "auto"); + Assert.IsNull(actual); + + omniLink.Controller.Thermostats[1].Type = enuThermostatType.CoolOnly; + messageProcessor.Process($"omnilink/thermostat1/mode_command", "auto"); + Assert.IsNull(actual); + messageProcessor.Process($"omnilink/thermostat1/mode_command", "heat"); + Assert.IsNull(actual); + + omniLink.Controller.Thermostats[1].Type = enuThermostatType.HeatOnly; + messageProcessor.Process($"omnilink/thermostat1/mode_command", "auto"); + Assert.IsNull(actual); + messageProcessor.Process($"omnilink/thermostat1/mode_command", "cool"); + Assert.IsNull(actual); + } + + [TestMethod] + public void ThermostatModeCommand() + { + void check(ushort id, string payload, enuUnitCommand command, int mode) + { + SendCommandEventArgs actual = null; + omniLink.OnSendCommand += (sender, e) => { actual = e; }; + messageProcessor.Process($"omnilink/thermostat{id}/mode_command", payload); + SendCommandEventArgs expected = new SendCommandEventArgs() + { + Cmd = command, + Par = (byte)mode, + Pr2 = id + }; + Assert.AreEqual(expected, actual); + } + + omniLink.Controller.Thermostats[1].Type = enuThermostatType.AutoHeatCool; + + check(1, "auto", enuUnitCommand.Mode, (int)enuThermostatMode.Auto); + check(1, "cool", enuUnitCommand.Mode, (int)enuThermostatMode.Cool); + check(1, "heat", enuUnitCommand.Mode, (int)enuThermostatMode.Heat); + check(1, "off", enuUnitCommand.Mode, (int)enuThermostatMode.Off); + + omniLink.Controller.Thermostats[1].Type = enuThermostatType.HeatCool; + + check(1, "cool", enuUnitCommand.Mode, (int)enuThermostatMode.Cool); + check(1, "heat", enuUnitCommand.Mode, (int)enuThermostatMode.Heat); + check(1, "off", enuUnitCommand.Mode, (int)enuThermostatMode.Off); + + omniLink.Controller.Thermostats[1].Type = enuThermostatType.CoolOnly; + + check(1, "cool", enuUnitCommand.Mode, (int)enuThermostatMode.Cool); + check(1, "off", enuUnitCommand.Mode, (int)enuThermostatMode.Off); + + omniLink.Controller.Thermostats[1].Type = enuThermostatType.HeatOnly; + + check(1, "heat", enuUnitCommand.Mode, (int)enuThermostatMode.Heat); + check(1, "off", enuUnitCommand.Mode, (int)enuThermostatMode.Off); + } + [TestMethod] public void ButtonCommand() { diff --git a/OmniLinkBridgeTest/Mock/MockOmniLinkII.cs b/OmniLinkBridgeTest/Mock/MockOmniLinkII.cs index 84759ab..6df958c 100644 --- a/OmniLinkBridgeTest/Mock/MockOmniLinkII.cs +++ b/OmniLinkBridgeTest/Mock/MockOmniLinkII.cs @@ -16,9 +16,11 @@ namespace OmniLinkBridgeTest.Mock public MockOmniLinkII() { - Controller = new clsHAC(); - Controller.Model = enuModel.OMNI_PRO_II; - Controller.TempFormat = enuTempFormat.Fahrenheit; + Controller = new clsHAC + { + Model = enuModel.OMNI_PRO_II, + TempFormat = enuTempFormat.Fahrenheit + }; } public bool SendCommand(enuUnitCommand Cmd, byte Par, ushort Pr2) diff --git a/OmniLinkBridgeTest/Mock/SendCommandEventArgs.cs b/OmniLinkBridgeTest/Mock/SendCommandEventArgs.cs index 74fb937..fbc5d05 100644 --- a/OmniLinkBridgeTest/Mock/SendCommandEventArgs.cs +++ b/OmniLinkBridgeTest/Mock/SendCommandEventArgs.cs @@ -15,14 +15,12 @@ namespace OmniLinkBridgeTest.Mock public override bool Equals(object other) { - var toCompareWith = other as SendCommandEventArgs; - - if (toCompareWith == null) + if (!(other is SendCommandEventArgs toCompareWith)) return false; - return this.Cmd == toCompareWith.Cmd && - this.Par == toCompareWith.Par && - this.Pr2 == toCompareWith.Pr2; + return Cmd == toCompareWith.Cmd && + Par == toCompareWith.Par && + Pr2 == toCompareWith.Pr2; } public override int GetHashCode() diff --git a/OmniLinkBridgeTest/OmniLinkBridgeTest.csproj b/OmniLinkBridgeTest/OmniLinkBridgeTest.csproj index 046b598..07a0fa3 100644 --- a/OmniLinkBridgeTest/OmniLinkBridgeTest.csproj +++ b/OmniLinkBridgeTest/OmniLinkBridgeTest.csproj @@ -29,6 +29,7 @@ DEBUG;TRACE prompt 4 + 8.0 pdbonly @@ -37,6 +38,7 @@ TRACE prompt 4 + 8.0 diff --git a/README.md b/README.md index a4d4376..0c2062d 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,20 @@ systemctl start omnilinkbridge.service ``` ## MQTT +### System +``` +SUB omnilink/system/phone/state +string secure, trouble + +SUB omnilink/system/ac/state +string secure, trouble + +SUB omnilink/system/battery/state +string secure, trouble + +SUB omnilink/system/dcm/state +string secure, trouble +``` ### Areas ``` @@ -158,8 +172,14 @@ string triggered, pending, armed_night, armed_night_delay, armed_home, armed_hom SUB omnilink/areaX/basic_state string triggered, pending, armed_night, armed_home, armed_away, disarmed +SUB omnilink/areaX/json_state +string json + PUB omnilink/areaX/command string arm_home, arm_away, arm_night, disarm, arm_home_instant, arm_night_delay, arm_vacation + +PUB omnilink/areaX/alarm_command +string burglary, fire, auxiliary ``` ### Zones