diff --git a/OmniLinkBridge/MQTT/Alarm.cs b/OmniLinkBridge/MQTT/Alarm.cs index 4a3679e..069290e 100644 --- a/OmniLinkBridge/MQTT/Alarm.cs +++ b/OmniLinkBridge/MQTT/Alarm.cs @@ -8,6 +8,8 @@ namespace OmniLinkBridge.MQTT { public class Alarm : Device { + public string basic_state_topic { get; set; } + public string command_topic { get; set; } //public string code { get; set; } = string.Empty; diff --git a/OmniLinkBridge/MQTT/MappingExtensions.cs b/OmniLinkBridge/MQTT/MappingExtensions.cs index c13fa8b..bec2a66 100644 --- a/OmniLinkBridge/MQTT/MappingExtensions.cs +++ b/OmniLinkBridge/MQTT/MappingExtensions.cs @@ -18,12 +18,39 @@ namespace OmniLinkBridge.MQTT { Alarm ret = new Alarm(); ret.name = area.Name; - ret.state_topic = area.ToTopic(Topic.state); + ret.state_topic = area.ToTopic(Topic.basic_state); ret.command_topic = area.ToTopic(Topic.command); return ret; } public static string ToState(this clsArea area) + { + if (area.AreaBurglaryAlarmText != "OK") + return "triggered"; + else if (area.ExitTimer > 0) + return "pending"; + + switch (area.AreaMode) + { + 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"; + } + } + + public static string ToBasicState(this clsArea area) { if (area.AreaBurglaryAlarmText != "OK") return "triggered"; @@ -57,7 +84,7 @@ namespace OmniLinkBridge.MQTT Sensor ret = new Sensor(); ret.name = zone.Name; ret.device_class = Sensor.DeviceClass.temperature; - ret.state_topic = zone.ToTopic(Topic.state); + ret.state_topic = zone.ToTopic(Topic.current_temperature); ret.unit_of_measurement = "°F"; return ret; } @@ -67,7 +94,7 @@ namespace OmniLinkBridge.MQTT Sensor ret = new Sensor(); ret.name = zone.Name; ret.device_class = Sensor.DeviceClass.humidity; - ret.state_topic = zone.ToTopic(Topic.state); + ret.state_topic = zone.ToTopic(Topic.current_humidity); ret.unit_of_measurement = "%"; return ret; } @@ -114,16 +141,29 @@ namespace OmniLinkBridge.MQTT } } - ret.state_topic = zone.ToTopic(Topic.state); + ret.state_topic = zone.ToTopic(Topic.basic_state); return ret; } public static string ToState(this clsZone zone) { - if (zone.IsTemperatureZone() || zone.IsHumidityZone()) - return zone.TempText(); + if (zone.Status.IsBitSet(5)) + return "bypassed"; + else if (zone.Status.IsBitSet(2)) + return "tripped"; + else if (zone.Status.IsBitSet(4)) + return "armed"; + else if (zone.Status.IsBitSet(1)) + return "trouble"; + else if (zone.Status.IsBitSet(0)) + return "not_ready"; else - return zone.Status.IsBitSet(0) ? "ON" : "OFF"; + return "secure"; + } + + public static string ToBasicState(this clsZone zone) + { + return zone.Status.IsBitSet(0) ? "ON" : "OFF"; } public static string ToTopic(this clsUnit unit, Topic topic) diff --git a/OmniLinkBridge/MQTT/Topics.cs b/OmniLinkBridge/MQTT/Topics.cs index 8f759e0..3af3003 100644 --- a/OmniLinkBridge/MQTT/Topics.cs +++ b/OmniLinkBridge/MQTT/Topics.cs @@ -25,6 +25,8 @@ namespace OmniLinkBridge.MQTT public static Topic state { get { return new Topic("state"); } } public static Topic command { get { return new Topic("command"); } } + public static Topic basic_state { get { return new Topic("basic_state"); } } + public static Topic brightness_state { get { return new Topic("brightness_state"); } } public static Topic brightness_command { get { return new Topic("brightness_command"); } } diff --git a/OmniLinkBridge/Modules/MQTTModule.cs b/OmniLinkBridge/Modules/MQTTModule.cs index 32157fb..4b842d8 100644 --- a/OmniLinkBridge/Modules/MQTTModule.cs +++ b/OmniLinkBridge/Modules/MQTTModule.cs @@ -90,6 +90,10 @@ namespace OmniLinkBridge.Modules { ProcessAreaReceived(OmniLink.Controller.Areas[areaId], match.Groups[3].Value, payload); } + if (match.Groups[1].Value == "zone" && ushort.TryParse(match.Groups[2].Value, out ushort zoneId) && zoneId < OmniLink.Controller.Zones.Count) + { + ProcessZoneReceived(OmniLink.Controller.Zones[zoneId], match.Groups[3].Value, payload); + } else if (match.Groups[1].Value == "unit" && ushort.TryParse(match.Groups[2].Value, out ushort unitId) && unitId < OmniLink.Controller.Units.Count) { ProcessUnitReceived(OmniLink.Controller.Units[unitId], match.Groups[3].Value, payload); @@ -108,38 +112,58 @@ namespace OmniLinkBridge.Modules { if (string.Compare(command, Topic.command.ToString()) == 0) { - switch(payload) + if(string.Compare(payload, "arm_home", true) == 0) { - case "ARM_HOME": - log.Debug("SetArea: " + area.Number + " to home"); - OmniLink.Controller.SendCommand(enuUnitCommand.SecurityDay, 0, (ushort)area.Number); - break; - case "ARM_AWAY": - log.Debug("SetArea: " + area.Number + " to away"); - OmniLink.Controller.SendCommand(enuUnitCommand.SecurityAway, 0, (ushort)area.Number); - break; - case "ARM_NIGHT": - log.Debug("SetArea: " + area.Number + " to night"); - OmniLink.Controller.SendCommand(enuUnitCommand.SecurityNight, 0, (ushort)area.Number); - break; - case "DISARM": - log.Debug("SetArea: " + area.Number + " to disarm"); - OmniLink.Controller.SendCommand(enuUnitCommand.SecurityOff, 0, (ushort)area.Number); - break; + log.Debug("SetArea: " + area.Number + " to home"); + OmniLink.Controller.SendCommand(enuUnitCommand.SecurityDay, 0, (ushort)area.Number); + } + else if (string.Compare(payload, "arm_away", true) == 0) + { + log.Debug("SetArea: " + area.Number + " to away"); + OmniLink.Controller.SendCommand(enuUnitCommand.SecurityAway, 0, (ushort)area.Number); + } + else if (string.Compare(payload, "arm_night", true) == 0) + { + log.Debug("SetArea: " + area.Number + " to night"); + OmniLink.Controller.SendCommand(enuUnitCommand.SecurityNight, 0, (ushort)area.Number); + } + else if (string.Compare(payload, "disarm", true) == 0) + { + log.Debug("SetArea: " + area.Number + " to disarm"); + OmniLink.Controller.SendCommand(enuUnitCommand.SecurityOff, 0, (ushort)area.Number); + } + // The below aren't supported by Home Assistant + else if (string.Compare(payload, "arm_home_instant", true) == 0) + { + log.Debug("SetArea: " + area.Number + " to home instant"); + OmniLink.Controller.SendCommand(enuUnitCommand.SecurityDyi, 0, (ushort)area.Number); + } + else if (string.Compare(payload, "arm_night_delay", true) == 0) + { + log.Debug("SetArea: " + area.Number + " to night delay"); + OmniLink.Controller.SendCommand(enuUnitCommand.SecurityNtd, 0, (ushort)area.Number); + } + else if (string.Compare(payload, "arm_vacation", true) == 0) + { + log.Debug("SetArea: " + area.Number + " to vacation"); + OmniLink.Controller.SendCommand(enuUnitCommand.SecurityVac, 0, (ushort)area.Number); + } + } + } - // The below aren't supported by Home Assistant - case "ARM_HOME_INSTANT": - log.Debug("SetArea: " + area.Number + " to home instant"); - OmniLink.Controller.SendCommand(enuUnitCommand.SecurityDyi, 0, (ushort)area.Number); - break; - case "ARM_NIGHT_DELAY": - log.Debug("SetArea: " + area.Number + " to night delay"); - OmniLink.Controller.SendCommand(enuUnitCommand.SecurityNtd, 0, (ushort)area.Number); - break; - case "ARM_VACATION": - log.Debug("SetArea: " + area.Number + " to vacation"); - OmniLink.Controller.SendCommand(enuUnitCommand.SecurityVac, 0, (ushort)area.Number); - break; + private void ProcessZoneReceived(clsZone zone, string command, string payload) + { + if (string.Compare(command, Topic.command.ToString()) == 0) + { + if (string.Compare(payload, "bypass", true) == 0) + { + log.Debug("SetZone: " + zone.Number + " to " + payload); + OmniLink.Controller.SendCommand(enuUnitCommand.Bypass, 0, (ushort)zone.Number); + } + else if (string.Compare(payload, "restore", true) == 0) + { + log.Debug("SetZone: " + zone.Number + " to " + payload); + OmniLink.Controller.SendCommand(enuUnitCommand.Restore, 0, (ushort)zone.Number); } } } @@ -282,21 +306,15 @@ namespace OmniLinkBridge.Modules PublishZoneState(zone); + MqttClient.PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/omnilink/zone{i.ToString()}/config", + JsonConvert.SerializeObject(zone.ToConfig()), MqttQualityOfServiceLevel.AtMostOnce, true); + if (zone.IsTemperatureZone()) - { MqttClient.PublishAsync($"{Global.mqtt_discovery_prefix}/sensor/omnilink/zone{i.ToString()}/config", JsonConvert.SerializeObject(zone.ToConfigTemp()), MqttQualityOfServiceLevel.AtMostOnce, true); - } else if (zone.IsHumidityZone()) - { MqttClient.PublishAsync($"{Global.mqtt_discovery_prefix}/sensor/omnilink/zone{i.ToString()}/config", JsonConvert.SerializeObject(zone.ToConfigHumidity()), MqttQualityOfServiceLevel.AtMostOnce, true); - } - else - { - MqttClient.PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/omnilink/zone{i.ToString()}/config", - JsonConvert.SerializeObject(zone.ToConfig()), MqttQualityOfServiceLevel.AtMostOnce, true); - } } } @@ -390,11 +408,18 @@ namespace OmniLinkBridge.Modules private void PublishAreaState(clsArea area) { MqttClient.PublishAsync(area.ToTopic(Topic.state), area.ToState(), MqttQualityOfServiceLevel.AtMostOnce, true); + MqttClient.PublishAsync(area.ToTopic(Topic.basic_state), area.ToBasicState(), MqttQualityOfServiceLevel.AtMostOnce, true); } private void PublishZoneState(clsZone zone) { MqttClient.PublishAsync(zone.ToTopic(Topic.state), zone.ToState(), MqttQualityOfServiceLevel.AtMostOnce, true); + MqttClient.PublishAsync(zone.ToTopic(Topic.basic_state), zone.ToBasicState(), MqttQualityOfServiceLevel.AtMostOnce, true); + + if(zone.IsTemperatureZone()) + MqttClient.PublishAsync(zone.ToTopic(Topic.current_temperature), zone.TempText(), MqttQualityOfServiceLevel.AtMostOnce, true); + else if (zone.IsHumidityZone()) + MqttClient.PublishAsync(zone.ToTopic(Topic.current_humidity), zone.TempText(), MqttQualityOfServiceLevel.AtMostOnce, true); } private void PublishUnitState(clsUnit unit) diff --git a/README.md b/README.md index 4cc0b17..6939921 100644 --- a/README.md +++ b/README.md @@ -86,12 +86,38 @@ To test the API you can use your browser to view a page or PowerShell (see below ## MQTT This module will also publish discovery topics for Home Assistant to auto configure devices. +### Areas +``` SUB omnilink/areaX/state +string triggered, pending, armed_night, armed_night_delay, armed_home, armed_home_instant, armed_away, armed_vacation, disarmed + +SUB omnilink/areaX/basic_state string triggered, pending, armed_night, armed_home, armed_away, disarmed PUB omnilink/areaX/command -string ARM_HOME, ARM_AWAY, ARM_NIGHT, DISARM, ARM_HOME_INSTANT, ARM_NIGHT_DELAY, ARM_VACATION +string(insensitive) arm_home, arm_away, arm_night, disarm, arm_home_instant, arm_night_delay, arm_vacation +``` +### Zones +``` +SUB omnilink/zoneX/state +string secure, not_ready, trouble, armed, tripped, bypassed + +SUB omnilink/zoneX/basic_state +string OFF, ON + +SUB omnilink/zoneX/current_temperature (optional) +int Current temperature in degrees fahrenheit + +SUB omnilink/zoneX/current_humidity (optional) +int Current relative humidity + +PUB omnilink/zoneX/command +string(insensitive) bypass, restore +``` + +### Units +``` SUB omnilink/unitX/state PUB omnilink/unitX/command string OFF, ON @@ -99,7 +125,10 @@ string OFF, ON SUB omnilink/unitX/brightness_state PUB omnilink/unitX/brightness_command int Level from 0 to 100 percent +``` +### Thermostats +``` SUB omnilink/thermostatX/current_operation string idle, cool, heat @@ -132,12 +161,16 @@ string auto, on, cycle SUB omnilink/thermostatX/hold_state PUB omnilink/thermostatX/hold_command string off, hold +``` +### Buttons +``` SUB omnilink/buttonX/state string OFF PUB omnilink/buttonX/command string ON +``` ## Change Log Version 1.1.1 - 2018-10-18