1.1.5 - Update readme, fix thermostat logging interval, and cleanup code

This commit is contained in:
Ryan Wagoner 2019-12-14 22:11:23 -05:00
parent cb24322bef
commit 336e02e8e8
10 changed files with 167 additions and 119 deletions

View file

@ -12,17 +12,17 @@ namespace OmniLinkBridge.Modules
{ {
public class LoggerModule : IModule public class LoggerModule : IModule
{ {
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private OmniLinkII omnilink; private readonly OmniLinkII omnilink;
private List<string> alarms = new List<string>(); private readonly List<string> alarms = new List<string>();
// mySQL Database // mySQL Database
private OdbcConnection mysql_conn = null; private OdbcConnection mysql_conn = null;
private DateTime mysql_retry = DateTime.MinValue; private DateTime mysql_retry = DateTime.MinValue;
private OdbcCommand mysql_command = null; private OdbcCommand mysql_command = null;
private Queue<string> mysql_queue = new Queue<string>(); private readonly Queue<string> mysql_queue = new Queue<string>();
private object mysql_lock = new object(); private readonly object mysql_lock = new object();
private readonly AutoResetEvent trigger = new AutoResetEvent(false); private readonly AutoResetEvent trigger = new AutoResetEvent(false);
@ -221,18 +221,14 @@ namespace OmniLinkBridge.Modules
private void Omnilink_OnThermostatStatus(object sender, ThermostatStatusEventArgs e) private void Omnilink_OnThermostatStatus(object sender, ThermostatStatusEventArgs e)
{ {
if (e.EventTimer) int.TryParse(e.Thermostat.TempText(), out int temp);
return; int.TryParse(e.Thermostat.HeatSetpointText(), out int heat);
int.TryParse(e.Thermostat.CoolSetpointText(), out int cool);
int temp, heat, cool, humidity, humidify, dehumidify; int.TryParse(e.Thermostat.HumidityText(), out int humidity);
int.TryParse(e.Thermostat.HumidifySetpointText(), out int humidify);
Int32.TryParse(e.Thermostat.TempText(), out temp); int.TryParse(e.Thermostat.DehumidifySetpointText(), out int dehumidify);
Int32.TryParse(e.Thermostat.HeatSetpointText(), out heat);
Int32.TryParse(e.Thermostat.CoolSetpointText(), out cool);
Int32.TryParse(e.Thermostat.HumidityText(), out humidity);
Int32.TryParse(e.Thermostat.HumidifySetpointText(), out humidify);
Int32.TryParse(e.Thermostat.DehumidifySetpointText(), out dehumidify);
// Log all events including thermostat polling
DBQueue(@" DBQueue(@"
INSERT INTO log_thermostats (timestamp, id, name, INSERT INTO log_thermostats (timestamp, id, name,
status, temp, heat, cool, status, temp, heat, cool,
@ -243,7 +239,8 @@ namespace OmniLinkBridge.Modules
humidity + "','" + humidify + "','" + dehumidify + "','" + humidity + "','" + humidify + "','" + dehumidify + "','" +
e.Thermostat.ModeText() + "','" + e.Thermostat.FanModeText() + "','" + e.Thermostat.HoldStatusText() + "')"); e.Thermostat.ModeText() + "','" + e.Thermostat.FanModeText() + "','" + e.Thermostat.HoldStatusText() + "')");
if (Global.verbose_thermostat) // Ignore events fired by thermostat polling
if (!e.EventTimer && Global.verbose_thermostat)
log.Debug("ThermostatStatus " + e.ID + " " + e.Thermostat.Name + log.Debug("ThermostatStatus " + e.ID + " " + e.Thermostat.Name +
", Status: " + e.Thermostat.TempText() + " " + e.Thermostat.HorC_StatusText() + ", Status: " + e.Thermostat.TempText() + " " + e.Thermostat.HorC_StatusText() +
", Heat: " + e.Thermostat.HeatSetpointText() + ", Heat: " + e.Thermostat.HeatSetpointText() +

View file

@ -18,14 +18,13 @@ namespace OmniLinkBridge.Modules
{ {
public class MQTTModule : IModule public class MQTTModule : IModule
{ {
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static DeviceRegistry MqttDeviceRegistry { get; set; } public static DeviceRegistry MqttDeviceRegistry { get; set; }
private OmniLinkII OmniLink { get; set; } private OmniLinkII OmniLink { get; set; }
private IManagedMqttClient MqttClient { get; set; } private IManagedMqttClient MqttClient { get; set; }
private bool ControllerConnected { get; set; } private bool ControllerConnected { get; set; }
private MessageProcessor MessageProcessor { get; set; } private MessageProcessor MessageProcessor { get; set; }
private readonly AutoResetEvent trigger = new AutoResetEvent(false); private readonly AutoResetEvent trigger = new AutoResetEvent(false);
@ -371,9 +370,8 @@ namespace OmniLinkBridge.Modules
if (!MqttClient.IsConnected) if (!MqttClient.IsConnected)
return; return;
// Ignore events fired by thermostat polling and when temperature is invalid // Ignore events fired by thermostat polling
// An invalid temperature can occur when a Zigbee thermostat is unreachable if (!e.EventTimer)
if (!e.EventTimer && e.Thermostat.Temp > 0)
PublishThermostatState(e.Thermostat); PublishThermostatState(e.Thermostat);
} }

View file

@ -12,16 +12,16 @@ namespace OmniLinkBridge.Modules
{ {
public class OmniLinkII : IModule, IOmniLinkII public class OmniLinkII : IModule, IOmniLinkII
{ {
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// OmniLink Controller // OmniLink Controller
public clsHAC Controller { get; private set; } public clsHAC Controller { get; private set; }
private DateTime retry = DateTime.MinValue; private DateTime retry = DateTime.MinValue;
// Thermostats // Thermostats
private Dictionary<ushort, DateTime> tstats = new Dictionary<ushort, DateTime>(); private readonly Dictionary<ushort, DateTime> tstats = new Dictionary<ushort, DateTime>();
private System.Timers.Timer tstat_timer = new System.Timers.Timer(); private readonly System.Timers.Timer tstat_timer = new System.Timers.Timer();
private object tstat_lock = new object(); private readonly object tstat_lock = new object();
// Events // Events
public event EventHandler<EventArgs> OnConnect; public event EventHandler<EventArgs> OnConnect;
@ -382,13 +382,15 @@ namespace OmniLinkBridge.Modules
private void GetNextNamed(enuObjectType type, int ix) private void GetNextNamed(enuObjectType type, int ix)
{ {
clsOL2MsgRequestProperties MSG = new clsOL2MsgRequestProperties(Controller.Connection); clsOL2MsgRequestProperties MSG = new clsOL2MsgRequestProperties(Controller.Connection)
MSG.ObjectType = type; {
MSG.IndexNumber = (UInt16)ix; ObjectType = type,
MSG.RelativeDirection = 1; // next object after IndexNumber IndexNumber = (UInt16)ix,
MSG.Filter1 = 1; // (0=Named or Unnamed, 1=Named, 2=Unnamed). RelativeDirection = 1, // next object after IndexNumber
MSG.Filter2 = 0; // Any Area Filter1 = 1, // (0=Named or Unnamed, 1=Named, 2=Unnamed).
MSG.Filter3 = 0; // Any Room Filter2 = 0, // Any Area
Filter3 = 0 // Any Room
};
Controller.Connection.Send(MSG, HandleNamedPropertiesResponse); Controller.Connection.Send(MSG, HandleNamedPropertiesResponse);
} }
@ -724,12 +726,19 @@ namespace OmniLinkBridge.Modules
lock (tstat_lock) lock (tstat_lock)
{ {
Controller.Thermostats[MSG.ObjectNumber(i)].CopyExtendedStatus(MSG, i); Controller.Thermostats[MSG.ObjectNumber(i)].CopyExtendedStatus(MSG, i);
// Don't fire event when invalid temperature of 0 is sometimes received
if (Controller.Thermostats[MSG.ObjectNumber(i)].Temp > 0)
{
OnThermostatStatus?.Invoke(this, new ThermostatStatusEventArgs() OnThermostatStatus?.Invoke(this, new ThermostatStatusEventArgs()
{ {
ID = MSG.ObjectNumber(i), ID = MSG.ObjectNumber(i),
Thermostat = Controller.Thermostats[MSG.ObjectNumber(i)], Thermostat = Controller.Thermostats[MSG.ObjectNumber(i)],
EventTimer = false EventTimer = false
}); });
}
else if (Global.verbose_thermostat_timer)
log.Warn("Ignoring unsolicited unknown temp for Thermostat " + Controller.Thermostats[MSG.ObjectNumber(i)].Name);
if (!tstats.ContainsKey(MSG.ObjectNumber(i))) if (!tstats.ContainsKey(MSG.ObjectNumber(i)))
tstats.Add(MSG.ObjectNumber(i), DateTime.Now); tstats.Add(MSG.ObjectNumber(i), DateTime.Now);
@ -809,6 +818,7 @@ namespace OmniLinkBridge.Modules
(Controller.Connection.ConnectionState == enuOmniLinkConnectionState.Online || (Controller.Connection.ConnectionState == enuOmniLinkConnectionState.Online ||
Controller.Connection.ConnectionState == enuOmniLinkConnectionState.OnlineSecure)) Controller.Connection.ConnectionState == enuOmniLinkConnectionState.OnlineSecure))
{ {
// Don't fire event when invalid temperature of 0 is sometimes received
if (Controller.Thermostats[tstat.Key].Temp > 0) if (Controller.Thermostats[tstat.Key].Temp > 0)
{ {
OnThermostatStatus?.Invoke(this, new ThermostatStatusEventArgs() OnThermostatStatus?.Invoke(this, new ThermostatStatusEventArgs()
@ -819,7 +829,7 @@ namespace OmniLinkBridge.Modules
}); });
} }
else if (Global.verbose_thermostat_timer) else if (Global.verbose_thermostat_timer)
log.Warn("Not logging unknown temp for Thermostat " + Controller.Thermostats[tstat.Key].Name); log.Warn("Ignoring unknown temp for Thermostat " + Controller.Thermostats[tstat.Key].Name);
} }
else if (Global.verbose_thermostat_timer) else if (Global.verbose_thermostat_timer)
log.Warn("Not logging out of date status for Thermostat " + Controller.Thermostats[tstat.Key].Name); log.Warn("Not logging out of date status for Thermostat " + Controller.Thermostats[tstat.Key].Name);

View file

@ -8,11 +8,11 @@ namespace OmniLinkBridge.Modules
{ {
public class TimeSyncModule : IModule public class TimeSyncModule : IModule
{ {
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private OmniLinkII OmniLink { get; set; } private OmniLinkII OmniLink { get; set; }
private System.Timers.Timer tsync_timer = new System.Timers.Timer(); private readonly System.Timers.Timer tsync_timer = new System.Timers.Timer();
private DateTime tsync_check = DateTime.MinValue; private DateTime tsync_check = DateTime.MinValue;
private readonly AutoResetEvent trigger = new AutoResetEvent(false); private readonly AutoResetEvent trigger = new AutoResetEvent(false);

View file

@ -14,7 +14,7 @@ namespace OmniLinkBridge
{ {
public class WebServiceModule : IModule public class WebServiceModule : IModule
{ {
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static OmniLinkII OmniLink { get; private set; } public static OmniLinkII OmniLink { get; private set; }

View file

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.1.4.0")] [assembly: AssemblyVersion("1.1.5.0")]
[assembly: AssemblyFileVersion("1.1.4.0")] [assembly: AssemblyFileVersion("1.1.5.0")]

View file

@ -13,7 +13,7 @@ namespace OmniLinkBridge
{ {
public static class Settings public static class Settings
{ {
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static void LoadSettings() public static void LoadSettings()
{ {
@ -60,7 +60,7 @@ namespace OmniLinkBridge
Global.mqtt_discovery_name_prefix = settings["mqtt_discovery_name_prefix"] ?? string.Empty; Global.mqtt_discovery_name_prefix = settings["mqtt_discovery_name_prefix"] ?? string.Empty;
if (!string.IsNullOrEmpty(Global.mqtt_discovery_name_prefix)) if (!string.IsNullOrEmpty(Global.mqtt_discovery_name_prefix))
Global.mqtt_discovery_name_prefix = Global.mqtt_discovery_name_prefix + " "; Global.mqtt_discovery_name_prefix += " ";
Global.mqtt_discovery_ignore_zones = ValidateRange(settings, "mqtt_discovery_ignore_zones"); Global.mqtt_discovery_ignore_zones = ValidateRange(settings, "mqtt_discovery_ignore_zones");
Global.mqtt_discovery_ignore_units = ValidateRange(settings, "mqtt_discovery_ignore_units"); Global.mqtt_discovery_ignore_units = ValidateRange(settings, "mqtt_discovery_ignore_units");
@ -103,7 +103,7 @@ namespace OmniLinkBridge
.Select(s => s.Split('=')) .Select(s => s.Split('='))
.ToDictionary(a => a[0].Trim(), a => a[1].Trim(), StringComparer.InvariantCultureIgnoreCase); .ToDictionary(a => a[0].Trim(), a => a[1].Trim(), StringComparer.InvariantCultureIgnoreCase);
if (!attributes.ContainsKey("id") || !Int32.TryParse(attributes["id"], out int attrib_id)) if (!attributes.ContainsKey("id") || !int.TryParse(attributes["id"], out int attrib_id))
throw new Exception("Missing or invalid id attribute"); throw new Exception("Missing or invalid id attribute");
T override_zone = new T(); T override_zone = new T();
@ -139,7 +139,7 @@ namespace OmniLinkBridge
{ {
try try
{ {
return Int32.Parse(settings[section]); return int.Parse(settings[section]);
} }
catch catch
{ {
@ -165,7 +165,7 @@ namespace OmniLinkBridge
{ {
try try
{ {
int port = Int32.Parse(settings[section]); int port = int.Parse(settings[section]);
if (port < 1 || port > 65534) if (port < 1 || port > 65534)
throw new Exception(); throw new Exception();
@ -179,54 +179,6 @@ namespace OmniLinkBridge
} }
} }
private static bool ValidateBool(NameValueCollection settings, string section)
{
try
{
return Boolean.Parse(settings[section]);
}
catch
{
log.Error("Invalid bool specified for " + section);
throw;
}
}
private static IPAddress ValidateIP(NameValueCollection settings, string section)
{
if (settings[section] == "*")
return IPAddress.Any;
if (settings[section] == "")
return IPAddress.None;
try
{
return IPAddress.Parse(section);
}
catch
{
log.Error("Invalid IP specified for " + section);
throw;
}
}
private static string ValidateDirectory(NameValueCollection settings, string section)
{
try
{
if (!Directory.Exists(settings[section]))
Directory.CreateDirectory(settings[section]);
return settings[section];
}
catch
{
log.Error("Invalid directory specified for " + section);
throw;
}
}
private static MailAddress ValidateMailFrom(NameValueCollection settings, string section) private static MailAddress ValidateMailFrom(NameValueCollection settings, string section)
{ {
try try

View file

@ -12,7 +12,7 @@ namespace OmniLinkBridge.WebAPI
[ServiceBehavior(IncludeExceptionDetailInFaults = true)] [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class OmniLinkService : IOmniLinkService public class OmniLinkService : IOmniLinkService
{ {
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public void Subscribe(SubscribeContract contract) public void Subscribe(SubscribeContract contract)
{ {

View file

@ -10,10 +10,10 @@ namespace OmniLinkBridge.WebAPI
{ {
static class WebNotification static class WebNotification
{ {
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static List<string> subscriptions = new List<string>(); private static List<string> subscriptions = new List<string>();
private static object subscriptions_lock = new object(); private static readonly object subscriptions_lock = new object();
public static void AddSubscription(string callback) public static void AddSubscription(string callback)
{ {

125
README.md
View file

@ -1,25 +1,59 @@
# OmniLink Bridge # 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 SmarthThings via web service API](https://github.com/excaliburpartners/SmartThings-OmniPro) and [Home Assistant via MQTT](https://www.home-assistant.io/components/mqtt/). 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/).
## Download ## Download
You can use docker to build an image from git or download the [binary here](http://www.excalibur-partners.com/downloads/OmniLinkBridge_1_1_4.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).
## Requirements ## Requirements
- [Docker](https://www.docker.com/) - [Docker](https://www.docker.com/)
- .NET Framework 4.5.2 (or Mono equivalent) - .NET Framework 4.5.2 (or Mono equivalent)
## Operation ## Operation
- Area, Messages, Units, and Zones are logged to mySQL when status changes OmniLinkBridge is divided into the following modules
- Thermostats are logged to mySQL once per minute
- If no notifications are received within 4 minutes a request is issued
- After 5 minutes of no updates a warning will be logged and mySQL will not be updated
- If the temp is 0 a warning will be logged and mySQL will not be updated
- Controller time is checked and compared to the local computer time disregarding time zones
## Notifications - OmniLinkII
- Supports email, prowl, and pushover - Settings: controller_
- Maintains connection to the OmniLink controller
- Thermostats
- If no status update has been received after 4 minutes a request is issued
- A status update containing a temperature of 0 is ignored
- This can occur when a ZigBee thermostat has lost communication
- Logger
- Console output
- Settings: verbose_
- Thermostats (verbose_thermostat_timer)
- After 5 minutes of no status updates a warning will be logged
- When a current temperature of 0 is received a warning will be logged
- MySQL logging
- Settings: mysql_
- Thermostats are logged every minute and when an event is received
- Push notifications
- Settings: notify_
- Always sent for area alarms and critical system events - Always sent for area alarms and critical system events
- Optionally enable for area status changes and console messages - Optionally enable for area status changes and console messages
- Email
- Settings: mail_
- Prowl
- Settings: prowl_
- Pushover
- Settings: pushover_
- Time Sync
- Settings: time_
- Controller time is checked and compared to the local computer time disregarding time zones
- Web API
- Settings: webapi_
- 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
- Requests to GET endpoints return status from the OmniLinkII module
- Requests to POST endpoints send commands to the OmniLinkII module
- MQTT
- Settings: mqtt_
- Maintains connection to the MQTT broker
- Publishes discovery topics for [Home Assistant](https://www.home-assistant.io/components/mqtt/) to auto configure devices
- Publishes topics for status received from the OmniLinkII module
- Subscribes to command topics and sends commands to the OmniLinkII module
## Docker Hub (preferred) ## Docker Hub (preferred)
1. Configure at a minimum the controller IP and encryptions keys. The web service port must be 8000. 1. Configure at a minimum the controller IP and encryptions keys. The web service port must be 8000.
@ -62,7 +96,7 @@ docker logs omnilink-bridge
## Installation Windows ## Installation Windows
1. Copy files to your desired location like C:\OmniLinkBridge 1. Copy files to your desired location like C:\OmniLinkBridge
2. Edit OmniLinkBridge.ini and define at a minimum the controller IP and encryptions keys 2. Edit OmniLinkBridge.ini and define at a minimum the controller IP and encryptions keys
3. Run OmniLinkBridge.exe to verify connectivity 3. Run OmniLinkBridge.exe from the command prompt to verify connectivity
4. Add Windows service 4. Add Windows service
``` ```
sc create OmniLinkBridge binpath=C:\OmniLinkBridge\OmniLinkBridge.exe sc create OmniLinkBridge binpath=C:\OmniLinkBridge\OmniLinkBridge.exe
@ -95,7 +129,6 @@ systemctl start omnilinkbridge.service
``` ```
## MQTT ## MQTT
This module will also publish discovery topics for Home Assistant to auto configure devices.
### Areas ### Areas
``` ```
@ -183,16 +216,69 @@ PUB omnilink/buttonX/command
string ON string ON
``` ```
## Web Service API ## Web API
To test the API you can use your browser to view a page or PowerShell (see below) to change a value. To test the web service API you can use your browser to view a page or PowerShell (see below) to change a value.
- http://localhost:8000/ListUnits
- http://localhost:8000/GetUnit?id=1
``` ```
Invoke-WebRequest -Uri "http://localhost:8000/SetUnit" -Method POST -ContentType "application/json" -Body (convertto-json -InputObject @{"id"=1;"value"=100}) -UseBasicParsing Invoke-WebRequest -Uri "http://localhost:8000/SetUnit" -Method POST -ContentType "application/json" -Body (convertto-json -InputObject @{"id"=1;"value"=100}) -UseBasicParsing
``` ```
### Subscription
```
POST /Subscribe
{ "callback": url }
Callback is a POST request with Type header added and json body identical to the related /Get
Type: area, contact, motion, water, smoke, co, temp, unit, thermostat
```
### Areas
```
GET /ListAreas
GET /GetArea?id=X
```
### Zones
```
GET /ListZonesContact
GET /ListZonesMotion
GET /ListZonesWater
GET /ListZonesSmoke
GET /ListZonesCO
GET /ListZonesTemp
GET /GetZone?id=X
```
### Units
```
GET /ListUnits
GET /GetZone?id=X
POST /SetUnit
POST /SetUnitKeypadPress
{ "id":X, "value":0-100 }
```
### Thermostats
```
GET /ListThermostats
GET /GetThermostat?id=X
POST /SetThermostatCoolSetpoint
POST /SetThermostatHeatSetpoint
POST /SetThermostatMode
POST /SetThermostatFanMode
POST /SetThermostatHold
{ "id":X, "value": }
int mode 0=off, 1=heat, 2=cool, 3=auto, 4=emergency heat
int fanmode 0=auto, 1=on, 2=circulate
int hold 0=off, 1=on
```
### Thermostats
```
GET /ListButtons
POST /PushButton
{ "id":X, "value":1 }
```
## MySQL Setup ## MySQL Setup
You will want to install the MySQL Community Server, Workbench, and ODBC Connector. The Workbench software provides a graphical interface to administer the MySQL server. The OmniLink Bridge uses ODBC to communicate with the database. The MySQL ODBC Connector library is needed for Windows ODBC to communicate with MySQL. You will want to install the MySQL Community Server, Workbench, and ODBC Connector. The Workbench software provides a graphical interface to administer the MySQL server. The OmniLink Bridge uses ODBC to communicate with the database. The MySQL ODBC Connector library is needed for Windows ODBC to communicate with MySQL.
@ -215,6 +301,11 @@ mysql_connection = DRIVER={MySQL};SERVER=localhost;DATABASE=OmniLinkBridge;USER=
``` ```
## Change Log ## Change Log
Version 1.1.5 - 2019-12-14
- Fix SQL logging for areas, units, and thermostats
- Refactor MQTT parser and add unit tests
- Update readme, fix thermostat logging interval, and cleanup code
Version 1.1.4 - 2019-11-22 Version 1.1.4 - 2019-11-22
- Utilize controller temperature format - Utilize controller temperature format
- Don't publish invalid thermostat temperatures - Don't publish invalid thermostat temperatures