From a891363539f84d26d6cd97c6f493b7fa8bf09825 Mon Sep 17 00:00:00 2001 From: Ryan Wagoner Date: Tue, 5 Nov 2019 22:47:24 -0500 Subject: [PATCH] - Add Home Assistant MQTT device registry support --- OmniLinkBridge/MQTT/Device.cs | 6 +++++ OmniLinkBridge/MQTT/DeviceRegistry.cs | 17 +++++++++++++ OmniLinkBridge/MQTT/MappingExtensions.cs | 31 +++++++++++++++++++----- OmniLinkBridge/Modules/MQTTModule.cs | 11 +++++++++ OmniLinkBridge/OmniLinkBridge.csproj | 1 + README.md | 20 ++++++--------- 6 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 OmniLinkBridge/MQTT/DeviceRegistry.cs diff --git a/OmniLinkBridge/MQTT/Device.cs b/OmniLinkBridge/MQTT/Device.cs index 6496731..e917365 100644 --- a/OmniLinkBridge/MQTT/Device.cs +++ b/OmniLinkBridge/MQTT/Device.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using OmniLinkBridge.Modules; using System; using System.Collections.Generic; using System.Linq; @@ -9,11 +10,16 @@ namespace OmniLinkBridge.MQTT { public class Device { + public string unique_id { get; set; } + public string name { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string state_topic { get; set; } public string availability_topic { get; set; } = $"{Global.mqtt_prefix}/status"; + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public DeviceRegistry device { get; set; } = MQTTModule.MqttDeviceRegistry; } } diff --git a/OmniLinkBridge/MQTT/DeviceRegistry.cs b/OmniLinkBridge/MQTT/DeviceRegistry.cs new file mode 100644 index 0000000..4669311 --- /dev/null +++ b/OmniLinkBridge/MQTT/DeviceRegistry.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OmniLinkBridge.MQTT +{ + public class DeviceRegistry + { + public string identifiers { get; set; } + public string name { get; set; } + public string sw_version { get; set; } + public string model { get; set; } + public string manufacturer { get; set; } + } +} diff --git a/OmniLinkBridge/MQTT/MappingExtensions.cs b/OmniLinkBridge/MQTT/MappingExtensions.cs index 9f49096..6463a7d 100644 --- a/OmniLinkBridge/MQTT/MappingExtensions.cs +++ b/OmniLinkBridge/MQTT/MappingExtensions.cs @@ -18,6 +18,7 @@ namespace OmniLinkBridge.MQTT public static Alarm ToConfig(this clsArea area) { Alarm ret = new Alarm(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}"; ret.name = Global.mqtt_discovery_name_prefix + area.Name; ret.state_topic = area.ToTopic(Topic.basic_state); ret.command_topic = area.ToTopic(Topic.command); @@ -82,6 +83,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfigBurglary(this clsArea area) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}burglary"; ret.name = $"{Global.mqtt_discovery_name_prefix}{area.Name} Burglary"; ret.device_class = BinarySensor.DeviceClass.safety; ret.state_topic = area.ToTopic(Topic.json_state); @@ -92,6 +94,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfigFire(this clsArea area) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}fire"; ret.name = $"{Global.mqtt_discovery_name_prefix}{area.Name} Fire"; ret.device_class = BinarySensor.DeviceClass.smoke; ret.state_topic = area.ToTopic(Topic.json_state); @@ -102,6 +105,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfigGas(this clsArea area) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}gas"; ret.name = $"{Global.mqtt_discovery_name_prefix}{area.Name} Gas"; ret.device_class = BinarySensor.DeviceClass.gas; ret.state_topic = area.ToTopic(Topic.json_state); @@ -112,6 +116,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfigAux(this clsArea area) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}auxiliary"; ret.name = $"{Global.mqtt_discovery_name_prefix}{area.Name} Auxiliary"; ret.device_class = BinarySensor.DeviceClass.problem; ret.state_topic = area.ToTopic(Topic.json_state); @@ -122,6 +127,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfigFreeze(this clsArea area) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}freeze"; ret.name = $"{Global.mqtt_discovery_name_prefix}{area.Name} Freeze"; ret.device_class = BinarySensor.DeviceClass.cold; ret.state_topic = area.ToTopic(Topic.json_state); @@ -132,6 +138,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfigWater(this clsArea area) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}water"; ret.name = $"{Global.mqtt_discovery_name_prefix}{area.Name} Water"; ret.device_class = BinarySensor.DeviceClass.moisture; ret.state_topic = area.ToTopic(Topic.json_state); @@ -142,6 +149,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfigDuress(this clsArea area) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}duress"; ret.name = $"{Global.mqtt_discovery_name_prefix}{area.Name} Duress"; ret.device_class = BinarySensor.DeviceClass.safety; ret.state_topic = area.ToTopic(Topic.json_state); @@ -152,6 +160,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfigTemp(this clsArea area) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}area{area.Number.ToString()}temp"; ret.name = $"{Global.mqtt_discovery_name_prefix}{area.Name} Temp"; ret.device_class = BinarySensor.DeviceClass.heat; ret.state_topic = area.ToTopic(Topic.json_state); @@ -211,6 +220,7 @@ namespace OmniLinkBridge.MQTT public static Sensor ToConfigTemp(this clsZone zone, enuTempFormat format) { Sensor ret = new Sensor(); + ret.unique_id = $"{Global.mqtt_prefix}zone{zone.Number.ToString()}temp"; ret.name = $"{Global.mqtt_discovery_name_prefix}{zone.Name} Temp"; ret.device_class = Sensor.DeviceClass.temperature; ret.state_topic = zone.ToTopic(Topic.current_temperature); @@ -221,6 +231,7 @@ namespace OmniLinkBridge.MQTT public static Sensor ToConfigHumidity(this clsZone zone) { Sensor ret = new Sensor(); + ret.unique_id = $"{Global.mqtt_prefix}zone{zone.Number.ToString()}humidity"; ret.name = $"{Global.mqtt_discovery_name_prefix}{zone.Name} Humidity"; ret.device_class = Sensor.DeviceClass.humidity; ret.state_topic = zone.ToTopic(Topic.current_humidity); @@ -231,6 +242,7 @@ namespace OmniLinkBridge.MQTT public static Sensor ToConfigSensor(this clsZone zone) { Sensor ret = new Sensor(); + ret.unique_id = $"{Global.mqtt_prefix}zone{zone.Number.ToString()}"; ret.name = Global.mqtt_discovery_name_prefix + zone.Name; switch (zone.ZoneType) @@ -270,6 +282,7 @@ namespace OmniLinkBridge.MQTT public static BinarySensor ToConfig(this clsZone zone) { BinarySensor ret = new BinarySensor(); + ret.unique_id = $"{Global.mqtt_prefix}zone{zone.Number.ToString()}binary"; ret.name = Global.mqtt_discovery_name_prefix + zone.Name; Global.mqtt_discovery_override_zone.TryGetValue(zone.Number, out OverrideZone override_zone); @@ -342,6 +355,7 @@ namespace OmniLinkBridge.MQTT public static Light ToConfig(this clsUnit unit) { Light ret = new Light(); + ret.unique_id = $"{Global.mqtt_prefix}unit{unit.Number.ToString()}light"; ret.name = Global.mqtt_discovery_name_prefix + unit.Name; ret.state_topic = unit.ToTopic(Topic.state); ret.command_topic = unit.ToTopic(Topic.command); @@ -353,6 +367,7 @@ namespace OmniLinkBridge.MQTT public static Switch ToConfigSwitch(this clsUnit unit) { Switch ret = new Switch(); + ret.unique_id = $"{Global.mqtt_prefix}unit{unit.Number.ToString()}switch"; ret.name = Global.mqtt_discovery_name_prefix + unit.Name; ret.state_topic = unit.ToTopic(Topic.state); ret.command_topic = unit.ToTopic(Topic.command); @@ -379,22 +394,24 @@ namespace OmniLinkBridge.MQTT return $"{Global.mqtt_prefix}/thermostat{thermostat.Number.ToString()}/{topic.ToString()}"; } - public static Sensor ToConfigTemp(this clsThermostat zone, enuTempFormat format) + public static Sensor ToConfigTemp(this clsThermostat thermostat, enuTempFormat format) { Sensor ret = new Sensor(); - ret.name = $"{Global.mqtt_discovery_name_prefix}{zone.Name} Temp"; + ret.unique_id = $"{Global.mqtt_prefix}thermostat{thermostat.Number.ToString()}temp"; + ret.name = $"{Global.mqtt_discovery_name_prefix}{thermostat.Name} Temp"; ret.device_class = Sensor.DeviceClass.temperature; - ret.state_topic = zone.ToTopic(Topic.current_temperature); + ret.state_topic = thermostat.ToTopic(Topic.current_temperature); ret.unit_of_measurement = (format == enuTempFormat.Fahrenheit ? "°F" : "°C"); return ret; } - public static Sensor ToConfigHumidity(this clsThermostat zone) + public static Sensor ToConfigHumidity(this clsThermostat thermostat) { Sensor ret = new Sensor(); - ret.name = $"{Global.mqtt_discovery_name_prefix}{zone.Name} Humidity"; + ret.unique_id = $"{Global.mqtt_prefix}thermostat{thermostat.Number.ToString()}humidity"; + ret.name = $"{Global.mqtt_discovery_name_prefix}{thermostat.Name} Humidity"; ret.device_class = Sensor.DeviceClass.humidity; - ret.state_topic = zone.ToTopic(Topic.current_humidity); + ret.state_topic = thermostat.ToTopic(Topic.current_humidity); ret.unit_of_measurement = "%"; return ret; } @@ -409,6 +426,7 @@ namespace OmniLinkBridge.MQTT ret.max_temp = "35"; } + ret.unique_id = $"{Global.mqtt_prefix}thermostat{thermostat.Number.ToString()}"; ret.name = Global.mqtt_discovery_name_prefix + thermostat.Name; ret.current_temperature_topic = thermostat.ToTopic(Topic.current_temperature); @@ -449,6 +467,7 @@ namespace OmniLinkBridge.MQTT public static Switch ToConfig(this clsButton button) { Switch ret = new Switch(); + ret.unique_id = $"{Global.mqtt_prefix}button{button.Number.ToString()}"; ret.name = Global.mqtt_discovery_name_prefix + button.Name; ret.state_topic = button.ToTopic(Topic.state); ret.command_topic = button.ToTopic(Topic.command); diff --git a/OmniLinkBridge/Modules/MQTTModule.cs b/OmniLinkBridge/Modules/MQTTModule.cs index 3500d9e..be669d7 100644 --- a/OmniLinkBridge/Modules/MQTTModule.cs +++ b/OmniLinkBridge/Modules/MQTTModule.cs @@ -20,6 +20,8 @@ namespace OmniLinkBridge.Modules { private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public static DeviceRegistry MqttDeviceRegistry { get; set; } + private OmniLinkII OmniLink { get; set; } private IManagedMqttClient MqttClient { get; set; } private bool ControllerConnected { get; set; } @@ -57,6 +59,15 @@ namespace OmniLinkBridge.Modules { log.Debug("Connected"); + MqttDeviceRegistry = new DeviceRegistry() + { + identifiers = Global.mqtt_prefix, + name = Global.mqtt_prefix, + sw_version = $"{OmniLink.Controller.GetVersionText()} - OmniLinkBridge {Assembly.GetExecutingAssembly().GetName().Version.ToString()}", + model = OmniLink.Controller.GetModelText(), + manufacturer = "Leviton" + }; + // For the initial connection wait for the controller connected event to publish config // For subsequent connections publish config immediately if(ControllerConnected) diff --git a/OmniLinkBridge/OmniLinkBridge.csproj b/OmniLinkBridge/OmniLinkBridge.csproj index e6e61f7..83abbd8 100644 --- a/OmniLinkBridge/OmniLinkBridge.csproj +++ b/OmniLinkBridge/OmniLinkBridge.csproj @@ -81,6 +81,7 @@ + diff --git a/README.md b/README.md index c4d6bd3..3c7f374 100644 --- a/README.md +++ b/README.md @@ -85,15 +85,7 @@ To test the API you can use your browser to view a page or PowerShell (see below - Invoke-WebRequest -Uri "http://localhost:8000/SetUnit" -Method POST -ContentType "application/json" -Body (convertto-json -InputObject @{"id"=1;"value"=100}) -UseBasicParsing ## MQTT -This module will also publish discovery topics for Home Assistant to auto configure devices. As of writing you will need to add a custom_component override in your Home Assistant config directory for the following pull requests. -- [Add target temperature low high to MQTT climate](https://github.com/home-assistant/home-assistant/pull/17391/) -- [Add night arm mode to MQTT alarm control panel](https://github.com/home-assistant/home-assistant/pull/17390/) - -``` -mkdir -p custom_components/mqtt -wget https://raw.githubusercontent.com/home-assistant/home-assistant/dcfcca77d72b0c35cda9950a69f621b4e8cff81b/homeassistant/components/climate/mqtt.py -O custom_components/mqtt/climate.py -wget https://raw.githubusercontent.com/home-assistant/home-assistant/fa2510f58b40cfea2974530658ee011d984db6c7/homeassistant/components/alarm_control_panel/mqtt.py -O custom_components/mqtt/alarm_control_panel.py -``` +This module will also publish discovery topics for Home Assistant to auto configure devices. ### Areas ``` @@ -182,12 +174,14 @@ string ON ``` ## Change Log -Version 1.1.4 - 2019-10-28 +Version 1.1.4 - 2019-11-06 - Utilize controller temperature format -- Ignore invalid temperature for thermostats -- Always enable first area to fix Omni LTe and Omni IIe -- Fix MQTT id validation and add notice for areas +- Don't publish invalid thermostat temperatures +- Always enable first area to support Omni LTe and Omni IIe +- Fix MQTT id validation and add notice for publishing to area 0 +- Fix missing last area, zone, unit, thermostat, and button - Fix compatibility with Home Assistant 0.95.4 MQTT extra keys +- Add Home Assistant MQTT device registry support Version 1.1.3 - 2019-02-10 - Publish config when reconnecting to MQTT