diff --git a/OmniLinkBridge/ControllerEnricher.cs b/OmniLinkBridge/ControllerEnricher.cs new file mode 100644 index 0000000..3d9dfc0 --- /dev/null +++ b/OmniLinkBridge/ControllerEnricher.cs @@ -0,0 +1,16 @@ +using System; +using Serilog.Core; +using Serilog.Events; + +namespace OmniLinkBridge +{ + public class ControllerEnricher : ILogEventEnricher + { + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + if(Global.controller_id != Guid.Empty) + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty( + "ControllerId", Global.controller_id)); + } + } +} diff --git a/OmniLinkBridge/Extensions.cs b/OmniLinkBridge/Extensions.cs index 117e719..7c6420a 100644 --- a/OmniLinkBridge/Extensions.cs +++ b/OmniLinkBridge/Extensions.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography; +using System.Text; using System.Text.RegularExpressions; namespace OmniLinkBridge @@ -43,5 +45,14 @@ namespace OmniLinkBridge .Select(t => int.Parse(t)).ToList(); // digit to int return RangeNums.Count.Equals(2) ? Enumerable.Range(RangeNums.Min(), (RangeNums.Max() + 1) - RangeNums.Min()).ToList() : RangeNums; } + + public static Guid ComputeGuid(this string data) + { + using SHA256 hash = SHA256.Create(); + byte[] bytes = hash.ComputeHash(Encoding.UTF8.GetBytes(data)); + byte[] guidBytes = new byte[16]; + Array.Copy(bytes, guidBytes, 16); + return new Guid(guidBytes); + } } } diff --git a/OmniLinkBridge/Global.cs b/OmniLinkBridge/Global.cs index 1c8ad4c..05646a5 100644 --- a/OmniLinkBridge/Global.cs +++ b/OmniLinkBridge/Global.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net.Mail; @@ -16,6 +17,7 @@ namespace OmniLinkBridge public static string controller_key1; public static string controller_key2; public static string controller_name; + public static Guid controller_id; // Time Sync public static bool time_sync; diff --git a/OmniLinkBridge/Modules/LoggerModule.cs b/OmniLinkBridge/Modules/LoggerModule.cs index 8b2f05f..cbed488 100644 --- a/OmniLinkBridge/Modules/LoggerModule.cs +++ b/OmniLinkBridge/Modules/LoggerModule.cs @@ -47,6 +47,7 @@ namespace OmniLinkBridge.Modules { if (Global.mysql_logging) { + log.Warning("MySQL logging is deprecated"); log.Information("Connecting to database"); mysql_conn = new OdbcConnection(Global.mysql_connection); @@ -214,10 +215,34 @@ namespace OmniLinkBridge.Modules log.Verbose("Initial LockStatus {id} {name}, Status: {status}", i, reader.Name, reader.LockStatusText()); } + ushort audioSourceUsage = 0; + for (ushort i = 1; i <= omnilink.Controller.AudioSources.Count; i++) + { + clsAudioSource audioSource = omnilink.Controller.AudioSources[i]; + + if (audioSource.DefaultProperties == true) + continue; + + audioSourceUsage++; + } + + ushort audioZoneUsage = 0; + for (ushort i = 1; i <= omnilink.Controller.AudioZones.Count; i++) + { + clsAudioZone audioZone = omnilink.Controller.AudioZones[i]; + + if (audioZone.DefaultProperties == true) + continue; + + audioZoneUsage++; + } + using (LogContext.PushProperty("Telemetry", "ControllerUsage")) log.Debug("Controller has {AreaUsage} areas, {ZoneUsage} zones, {UnitUsage} units, " + - "{OutputUsage} outputs, {FlagUsage} flags, {ThermostatUsage} thermostats, {LockUsage} locks", - areaUsage, zoneUsage, unitUsage, outputUsage, flagUsage, thermostatUsage, lockUsage); + "{OutputUsage} outputs, {FlagUsage} flags, {ThermostatUsage} thermostats, {LockUsage} locks, " + + "{AudioSourceUsage} audio sources, {AudioZoneUsage} audio zones", + areaUsage, zoneUsage, unitUsage, outputUsage, flagUsage, thermostatUsage, lockUsage, + audioSourceUsage, audioZoneUsage); } private void Omnilink_OnAreaStatus(object sender, AreaStatusEventArgs e) diff --git a/OmniLinkBridge/Modules/WebServiceModule.cs b/OmniLinkBridge/Modules/WebServiceModule.cs index 52dfe82..2484e88 100644 --- a/OmniLinkBridge/Modules/WebServiceModule.cs +++ b/OmniLinkBridge/Modules/WebServiceModule.cs @@ -33,6 +33,8 @@ namespace OmniLinkBridge public void Startup() { + log.Warning("WebAPI is deprecated"); + WebNotification.RestoreSubscriptions(); Uri uri = new Uri("http://0.0.0.0:" + Global.webapi_port + "/"); diff --git a/OmniLinkBridge/OmniLinkBridge.csproj b/OmniLinkBridge/OmniLinkBridge.csproj index 88e60aa..d33972b 100644 --- a/OmniLinkBridge/OmniLinkBridge.csproj +++ b/OmniLinkBridge/OmniLinkBridge.csproj @@ -80,6 +80,7 @@ + @@ -185,19 +186,19 @@ 3.1.2 - 13.0.1 + 13.0.3 - 2.12.0 + 3.1.1 - 1.1.0 + 2.0.0 1.5.0 - 4.1.0 + 5.0.1 5.0.0 diff --git a/OmniLinkBridge/Program.cs b/OmniLinkBridge/Program.cs index a0e77ad..75278ff 100644 --- a/OmniLinkBridge/Program.cs +++ b/OmniLinkBridge/Program.cs @@ -77,7 +77,7 @@ namespace OmniLinkBridge .MinimumLevel.Verbose() .Enrich.WithProperty("Application", "OmniLinkBridge") .Enrich.WithProperty("Session", Guid.NewGuid()) - .Enrich.WithProperty("User", (Environment.UserName + Environment.MachineName).GetHashCode()) + .Enrich.With() .Enrich.FromLogContext(); if (log_file != null) diff --git a/OmniLinkBridge/Properties/AssemblyInfo.cs b/OmniLinkBridge/Properties/AssemblyInfo.cs index 8875857..398d642 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.14.0")] -[assembly: AssemblyFileVersion("1.1.14.0")] +[assembly: AssemblyVersion("1.1.15.0")] +[assembly: AssemblyFileVersion("1.1.15.0")] diff --git a/OmniLinkBridge/Settings.cs b/OmniLinkBridge/Settings.cs index 8ffa577..601a0fc 100644 --- a/OmniLinkBridge/Settings.cs +++ b/OmniLinkBridge/Settings.cs @@ -30,9 +30,10 @@ namespace OmniLinkBridge // HAI / Leviton Omni Controller Global.controller_address = settings.ValidateHasValue("controller_address"); Global.controller_port = settings.ValidatePort("controller_port"); - Global.controller_key1 = settings.ValidateHasValue("controller_key1"); - Global.controller_key2 = settings.ValidateHasValue("controller_key2"); + Global.controller_key1 = settings.ValidateEncryptionKey("controller_key1"); + Global.controller_key2 = settings.ValidateEncryptionKey("controller_key2"); Global.controller_name = settings.CheckEnv("controller_name") ?? "OmniLinkBridge"; + Global.controller_id = (Global.controller_address + Global.controller_key1 + Global.controller_key2).ComputeGuid(); // Controller Time Sync Global.time_sync = settings.ValidateBool("time_sync"); @@ -234,6 +235,19 @@ namespace OmniLinkBridge return value; } + private static string ValidateEncryptionKey(this NameValueCollection settings, string section) + { + string value = settings.CheckEnv(section).Replace("-",""); + + if (string.IsNullOrEmpty(value) || value.Length != 16) + { + log.Error("Invalid encryption key specified for {section}", section); + throw new Exception(); + } + + return value; + } + private static int ValidateInt(this NameValueCollection settings, string section) { try diff --git a/README.md b/README.md index 7b95dc8..18aa641 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # OmniLink Bridge Provides MQTT bridge, web service API, time sync, and logging for [HAI/Leviton OmniPro II controllers](https://www.leviton.com/en/products/brands/omni-security-automation). Provides integration with [Samsung SmartThings via web service API](https://github.com/excaliburpartners/SmartThings-OmniPro) and [Home Assistant via MQTT](https://www.home-assistant.io/components/mqtt/). +Please note that OmniLink Bridge is not in active development. The MQTT and Home Assistant integrations are in maintenance mode. The SmartThings Web API and MySQL logging are deprecated and not feature consistent with MQTT. + ## Download -You can use docker to build an image from git or download the [binary here](https://github.com/excaliburpartners/OmniLinkBridge/releases/latest/download/OmniLinkBridge.zip). +You can use docker to build an image from git or download the [binary here](https://github.com/excaliburpartners/OmniLinkBridge/releases/latest/download/OmniLinkBridge.zip). You can also install it as a [Home Assistant Add-on](https://github.com/excaliburpartners/hassio-addons). ## Requirements - [Docker](https://www.docker.com/) @@ -28,7 +30,7 @@ OmniLink Bridge is divided into the following modules and configurable settings. - Provides integration with [Samsung SmartThings](https://github.com/excaliburpartners/SmartThings-OmniPro) - Allows an application to subscribe to receive POST notifications status updates are received from the OmniLinkII module - On failure to POST to callback URL subscription is removed - - Recommended for application to send subscribe reqeusts every few minutes + - Recommended for application to send subscribe requests every few minutes - Requests to GET endpoints return status from the OmniLinkII module - Requests to POST endpoints send commands to the OmniLinkII module - Logger @@ -388,7 +390,7 @@ POST /PushButton ``` ## MySQL -The [MySQL ODBC Connector](http://dev.mysql.com/downloads/connector/odbc/) is required for MySQL logging. The docker image comes with the MySQL ODBC connector installed. For Windows and Linux you will need to download and install it. +The [MySQL ODBC Connector](http://dev.mysql.com/downloads/connector/odbc/) is required for MySQL logging. The docker image comes with the MySQL ODBC connector installed. For Windows and Linux you will need to download and install it. The Home Assistant Add-on does not support MySQL logging. Configure mysql_connection in OmniLinkBridge.ini. For Windows change DRIVER={MySQL} to name of the driver shown in the ODBC Data Source Administrator. ```