- Switch to structured logging and add telemetry

This commit is contained in:
Ryan Wagoner 2020-01-05 21:34:03 -05:00
parent 7d87416915
commit 8e637db459
25 changed files with 368 additions and 373 deletions

View file

@ -28,4 +28,4 @@ EXPOSE 8000/tcp
VOLUME /config
WORKDIR /app
COPY --from=build /app .
CMD [ "mono", "OmniLinkBridge.exe", "-i", "-c", "/config/OmniLinkBridge.ini", "-e", "-s", "/config/WebSubscriptions.json" ]
CMD [ "mono", "OmniLinkBridge.exe", "-i", "-c", "/config/OmniLinkBridge.ini", "-e", "-s", "/config/WebSubscriptions.json", "-lf", "disable" ]

View file

@ -1,37 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
<log4net>
<appender name="TraceAppender" type="log4net.Appender.TraceAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date %-5level: %message%newline"/>
</layout>
</appender>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log.txt"/>
<appendToFile value="true"/>
<rollingStyle value="Composite"/>
<datePattern value="yyyyMMdd"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="5MB"/>
<immediateFlush value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="WARN"/>
<levelMax value="FATAL"/>
</filter>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="TraceAppender"/>
<appender-ref ref="RollingFileAppender"/>
</root>
</log4net>
</configuration>

View file

@ -1,5 +1,7 @@
using log4net;
using OmniLinkBridge.Modules;
using OmniLinkBridge.Modules;
using Serilog;
using Serilog.Context;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
@ -9,11 +11,13 @@ namespace OmniLinkBridge
{
public class CoreServer
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private OmniLinkII omnilink;
private readonly List<IModule> modules = new List<IModule>();
private readonly List<Task> tasks = new List<Task>();
private readonly ManualResetEvent quitEvent = new ManualResetEvent(false);
private DateTime startTime;
public CoreServer()
{
@ -23,11 +27,6 @@ namespace OmniLinkBridge
private void Server()
{
Global.running = true;
log.Debug("Starting up server " +
Assembly.GetExecutingAssembly().GetName().Version.ToString());
// Controller connection
modules.Add(omnilink = new OmniLinkII(Global.controller_address, Global.controller_port, Global.controller_key1, Global.controller_key2));
@ -43,6 +42,12 @@ namespace OmniLinkBridge
if(Global.mqtt_enabled)
modules.Add(new MQTTModule(omnilink));
startTime = DateTime.Now;
using (LogContext.PushProperty("Telemetry", "Startup"))
log.Information("Started version {Version} on {OperatingSystem} with {Modules}",
Assembly.GetExecutingAssembly().GetName().Version, Environment.OSVersion, modules);
// Startup modules
foreach (IModule module in modules)
{
@ -52,14 +57,11 @@ namespace OmniLinkBridge
}));
}
// Wait for all threads to stop
Task.WaitAll(tasks.ToArray());
quitEvent.WaitOne();
}
public void Shutdown()
{
Global.running = false;
// Shutdown modules
foreach (IModule module in modules)
module.Shutdown();
@ -68,7 +70,12 @@ namespace OmniLinkBridge
if (tasks != null)
Task.WaitAll(tasks.ToArray());
log.Debug("Shutdown completed");
using (LogContext.PushProperty("Telemetry", "Shutdown"))
log.Information("Shutdown completed with uptime {Uptime}", (DateTime.Now - startTime).ToString());
Log.CloseAndFlush();
quitEvent.Set();
}
}
}

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace OmniLinkBridge
{
@ -23,6 +24,11 @@ namespace OmniLinkBridge
return (b & (1 << pos)) != 0;
}
public static string ToSpaceTitleCase(this string phrase)
{
return Regex.Replace(phrase, "(\\B[A-Z])", " $1");
}
public static List<int> ParseRanges(this string ranges)
{
string[] groups = ranges.Split(',');

View file

@ -7,10 +7,8 @@ namespace OmniLinkBridge
{
public abstract class Global
{
public static bool running;
// Config File
public static string config_file;
public static bool DebugSettings { get; set; }
public static bool UseEnvironment { get; set; }
// HAI / Leviton Omni Controller
public static string controller_address;

View file

@ -1,6 +1,6 @@
using HAI_Shared;
using log4net;
using OmniLinkBridge.OmniLink;
using Serilog;
using System;
using System.Collections.Generic;
using System.Reflection;
@ -10,7 +10,7 @@ namespace OmniLinkBridge.MQTT
{
public class MessageProcessor
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private readonly Regex regexTopic = new Regex(Global.mqtt_prefix + "/([A-Za-z]+)([0-9]+)/(.*)", RegexOptions.Compiled);
@ -33,7 +33,8 @@ namespace OmniLinkBridge.MQTT
|| !ushort.TryParse(match.Groups[2].Value, out ushort id))
return;
log.Debug($"Received: Type: {type.ToString()}, Id: {id}, Command: {topic.ToString()}, Value: {payload}");
log.Debug("Received: Type: {type}, Id: {id}, Command: {command}, Value: {value}",
type.ToString(), id, topic.ToString(), payload);
if (type == CommandTypes.area && id <= OmniLink.Controller.Areas.Count)
ProcessAreaReceived(OmniLink.Controller.Areas[id], topic, payload);
@ -68,7 +69,7 @@ namespace OmniLinkBridge.MQTT
if (area.Number == 0)
log.Debug("SetArea: 0 implies all areas will be changed");
log.Debug("SetArea: " + area.Number + " to " + cmd.ToString().Replace("arm_", "").Replace("_", " "));
log.Debug("SetArea: {id} to {value}", area.Number, cmd.ToString().Replace("arm_", "").Replace("_", " "));
OmniLink.SendCommand(AreaMapping[cmd], 0, (ushort)area.Number);
}
}
@ -83,7 +84,7 @@ namespace OmniLinkBridge.MQTT
{
if (command == Topic.command && Enum.TryParse(payload, true, out ZoneCommands cmd))
{
log.Debug("SetZone: " + zone.Number + " to " + payload);
log.Debug("SetZone: {id} to {value}", zone.Number, payload);
OmniLink.SendCommand(ZoneMapping[cmd], 0, (ushort)zone.Number);
}
}
@ -100,13 +101,13 @@ namespace OmniLinkBridge.MQTT
{
if (string.Compare(unit.ToState(), cmd.ToString()) != 0)
{
log.Debug("SetUnit: " + unit.Number + " to " + cmd.ToString());
log.Debug("SetUnit: {id} to {value}", unit.Number, cmd.ToString());
OmniLink.SendCommand(UnitMapping[cmd], 0, (ushort)unit.Number);
}
}
else if (command == Topic.brightness_command && int.TryParse(payload, out int unitValue))
{
log.Debug("SetUnit: " + unit.Number + " to " + payload + "%");
log.Debug("SetUnit: {id} to {value}%", unit.Number, payload);
OmniLink.SendCommand(enuUnitCommand.Level, BitConverter.GetBytes(unitValue)[0], (ushort)unit.Number);
@ -129,7 +130,8 @@ namespace OmniLinkBridge.MQTT
}
int temp = tempLow.ToOmniTemp();
log.Debug("SetThermostatHeatSetpoint: " + thermostat.Number + " to " + payload + tempUnit + "(" + temp + ")");
log.Debug("SetThermostatHeatSetpoint: {id} to {value}{temperatureUnit} ({temp})",
thermostat.Number, payload, tempUnit, temp);
OmniLink.SendCommand(enuUnitCommand.SetLowSetPt, BitConverter.GetBytes(temp)[0], (ushort)thermostat.Number);
}
else if (command == Topic.temperature_cool_command && double.TryParse(payload, out double tempHigh))
@ -142,35 +144,36 @@ namespace OmniLinkBridge.MQTT
}
int temp = tempHigh.ToOmniTemp();
log.Debug("SetThermostatCoolSetpoint: " + thermostat.Number + " to " + payload + tempUnit + "(" + temp + ")");
log.Debug("SetThermostatCoolSetpoint: {id} to {value}{temperatureUnit} ({temp})",
thermostat.Number, payload, tempUnit, temp);
OmniLink.SendCommand(enuUnitCommand.SetHighSetPt, BitConverter.GetBytes(temp)[0], (ushort)thermostat.Number);
}
else if (command == Topic.humidify_command && double.TryParse(payload, out double humidify))
{
// Humidity is reported where Fahrenheit temperatures 0-100 correspond to 0-100% relative humidity
int level = humidify.ToCelsius().ToOmniTemp();
log.Debug("SetThermostatHumidifySetpoint: " + thermostat.Number + " to " + payload + "% (" + level + ")");
log.Debug("SetThermostatHumidifySetpoint: {id} to {value}% ({level})", thermostat.Number, payload, level);
OmniLink.SendCommand(enuUnitCommand.SetHumidifySetPt, BitConverter.GetBytes(level)[0], (ushort)thermostat.Number);
}
else if (command == Topic.dehumidify_command && double.TryParse(payload, out double dehumidify))
{
int level = dehumidify.ToCelsius().ToOmniTemp();
log.Debug("SetThermostatDehumidifySetpoint: " + thermostat.Number + " to " + payload + "% (" + level + ")");
log.Debug("SetThermostatDehumidifySetpoint: {id} to {value}% ({level})", thermostat.Number, payload, level);
OmniLink.SendCommand(enuUnitCommand.SetDeHumidifySetPt, BitConverter.GetBytes(level)[0], (ushort)thermostat.Number);
}
else if (command == Topic.mode_command && Enum.TryParse(payload, true, out enuThermostatMode mode))
{
log.Debug("SetThermostatMode: " + thermostat.Number + " to " + payload);
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))
{
log.Debug("SetThermostatFanMode: " + thermostat.Number + " to " + payload);
log.Debug("SetThermostatFanMode: {id} to {value}", thermostat.Number, payload);
OmniLink.SendCommand(enuUnitCommand.Fan, BitConverter.GetBytes((int)fanMode)[0], (ushort)thermostat.Number);
}
else if (command == Topic.hold_command && Enum.TryParse(payload, true, out enuThermostatHoldMode holdMode))
{
log.Debug("SetThermostatHold: " + thermostat.Number + " to " + payload);
log.Debug("SetThermostatHold: {id} to {value}", thermostat.Number, payload);
OmniLink.SendCommand(enuUnitCommand.Hold, BitConverter.GetBytes((int)holdMode)[0], (ushort)thermostat.Number);
}
}
@ -179,7 +182,7 @@ namespace OmniLinkBridge.MQTT
{
if (command == Topic.command && Enum.TryParse(payload, true, out UnitCommands cmd) && cmd == UnitCommands.ON)
{
log.Debug("PushButton: " + button.Number);
log.Debug("PushButton: {id}", button.Number);
OmniLink.SendCommand(enuUnitCommand.Button, 0, (ushort)button.Number);
}
}
@ -196,7 +199,7 @@ namespace OmniLinkBridge.MQTT
{
if (command == Topic.command && Enum.TryParse(payload, true, out MessageCommands cmd))
{
log.Debug("SetMessage: " + message.Number + " to " + cmd.ToString().Replace("_", " "));
log.Debug("SetMessage: {id} to {value}", message.Number, cmd.ToString().Replace("_", " "));
byte par = 0;
if (cmd == MessageCommands.show_no_beep)

View file

@ -1,6 +1,6 @@
using log4net;
using OmniLinkBridge.Notifications;
using OmniLinkBridge.Notifications;
using OmniLinkBridge.OmniLink;
using Serilog;
using System;
using System.Collections.Generic;
using System.Data;
@ -12,7 +12,9 @@ namespace OmniLinkBridge.Modules
{
public class LoggerModule : IModule
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private bool running = true;
private readonly OmniLinkII omnilink;
private readonly List<string> alarms = new List<string>();
@ -41,26 +43,22 @@ namespace OmniLinkBridge.Modules
{
if (Global.mysql_logging)
{
log.Info("Connecting to database");
log.Information("Connecting to database");
mysql_conn = new OdbcConnection(Global.mysql_connection);
// Must make an initial connection
if (!DBOpen())
Environment.Exit(1);
}
while (true)
{
// End gracefully when not logging or database queue empty
if (!Global.running && (!Global.mysql_logging || DBQueueCount() == 0))
if (!running && (!Global.mysql_logging || DBQueueCount() == 0))
break;
// Make sure database connection is active
if (Global.mysql_logging && mysql_conn.State != ConnectionState.Open)
{
// Nothing we can do if shutting down
if (!Global.running)
if (!running)
break;
if (mysql_retry < DateTime.Now)
@ -100,11 +98,11 @@ namespace OmniLinkBridge.Modules
{
if (mysql_conn.State != ConnectionState.Open)
{
log.Warn("Lost connection to database");
log.Warning("Lost connection to database");
}
else
{
log.Error("Error executing query\r\n" + query, ex);
log.Error(ex, "Error executing {query}", query);
// Prevent an endless loop from failed query
lock (mysql_lock)
@ -119,6 +117,7 @@ namespace OmniLinkBridge.Modules
public void Shutdown()
{
running = false;
trigger.Set();
}
@ -198,7 +197,7 @@ namespace OmniLinkBridge.Modules
e.Area.AreaDuressAlarmText + "','" + status + "')");
if (Global.verbose_area)
log.Debug("AreaStatus " + e.ID + " " + e.Area.Name + ", Status: " + status);
log.Verbose("AreaStatus {id} {name}, Status: {status}", e.ID, e.Area.Name, status);
if (Global.notify_area && e.Area.LastMode != e.Area.AreaMode)
Notification.Notify("Security", e.Area.Name + " " + e.Area.ModeText());
@ -213,9 +212,9 @@ namespace OmniLinkBridge.Modules
if (Global.verbose_zone)
{
if (e.Zone.IsTemperatureZone())
log.Debug("ZoneStatus " + e.ID + " " + e.Zone.Name + ", Temp: " + e.Zone.TempText());
log.Verbose("ZoneStatus {id} {name}, Temp: {temp}", e.ID, e.Zone.Name, e.Zone.TempText());
else
log.Debug("ZoneStatus " + e.ID + " " + e.Zone.Name + ", Status: " + e.Zone.StatusText());
log.Verbose("ZoneStatus {id} {name}, Status: {status}", e.ID, e.Zone.Name, e.Zone.StatusText());
}
}
@ -241,13 +240,15 @@ namespace OmniLinkBridge.Modules
// Ignore events fired by thermostat polling
if (!e.EventTimer && Global.verbose_thermostat)
log.Debug("ThermostatStatus " + e.ID + " " + e.Thermostat.Name +
", Status: " + e.Thermostat.TempText() + " " + e.Thermostat.HorC_StatusText() +
", Heat: " + e.Thermostat.HeatSetpointText() +
", Cool: " + e.Thermostat.CoolSetpointText() +
", Mode: " + e.Thermostat.ModeText() +
", Fan: " + e.Thermostat.FanModeText() +
", Hold: " + e.Thermostat.HoldStatusText());
log.Verbose("ThermostatStatus {id} {name}, Status: {temp} {status}, " +
"Heat {heat}, Cool: {cool}, Mode: {mode}, Fan: {fan}, Hold: {hold}",
e.ID, e.Thermostat.Name,
e.Thermostat.TempText(), e.Thermostat.HorC_StatusText(),
e.Thermostat.HeatSetpointText(),
e.Thermostat.CoolSetpointText(),
e.Thermostat.ModeText(),
e.Thermostat.FanModeText(),
e.Thermostat.HoldStatusText());
}
private void Omnilink_OnUnitStatus(object sender, UnitStatusEventArgs e)
@ -266,7 +267,7 @@ namespace OmniLinkBridge.Modules
status + "','" + e.Unit.Status + "','" + e.Unit.StatusTime + "')");
if (Global.verbose_unit)
log.Debug("UnitStatus " + e.ID + " " + e.Unit.Name + ", Status: " + status);
log.Verbose("UnitStatus {id} {name}, Status: {status}", e.ID, e.Unit.Name, status);
}
private void Omnilink_OnMessageStatus(object sender, MessageStatusEventArgs e)
@ -276,7 +277,7 @@ namespace OmniLinkBridge.Modules
VALUES ('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + e.ID + "','" + e.Message.Name + "','" + e.Message.StatusText() + "')");
if (Global.verbose_message)
log.Debug("MessageStatus " + e.ID + " " + e.Message.Name + ", " + e.Message.StatusText());
log.Verbose("MessageStatus {id} {name}, Status: {status}", e.ID, e.Message.Name, e.Message.StatusText());
if (Global.notify_message)
Notification.Notify("Message", e.ID + " " + e.Message.Name + ", " + e.Message.StatusText());
@ -289,7 +290,7 @@ namespace OmniLinkBridge.Modules
VALUES ('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + e.Type.ToString() + "','" + e.Value + "')");
if (Global.verbose_event)
log.Debug("SystemEvent " + e.Type.ToString() + " " + e.Value);
log.Verbose("SystemEvent {name} {status}", e.Type.ToString(), e.Value);
if (e.SendNotification)
Notification.Notify("SystemEvent", e.Type.ToString() + " " + e.Value);
@ -306,7 +307,7 @@ namespace OmniLinkBridge.Modules
}
catch (Exception ex)
{
log.Error("Failed to connect to database", ex);
log.Error(ex, "Failed to connect to database");
mysql_retry = DateTime.Now.AddMinutes(1);
return false;
}

View file

@ -1,5 +1,4 @@
using HAI_Shared;
using log4net;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
@ -11,6 +10,7 @@ using MQTTnet.Protocol;
using Newtonsoft.Json;
using OmniLinkBridge.MQTT;
using OmniLinkBridge.OmniLink;
using Serilog;
using System;
using System.Collections.Generic;
using System.Reflection;
@ -22,7 +22,7 @@ namespace OmniLinkBridge.Modules
{
public class MQTTModule : IModule
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
public static DeviceRegistry MqttDeviceRegistry { get; set; }
@ -90,7 +90,7 @@ namespace OmniLinkBridge.Modules
if (ControllerConnected)
PublishConfig();
});
MqttClient.ConnectingFailedHandler = new ConnectingFailedHandlerDelegate((e) => log.Debug("Error connecting " + e.Exception.Message));
MqttClient.ConnectingFailedHandler = new ConnectingFailedHandlerDelegate((e) => log.Error("Error connecting {reason}", e.Exception.Message));
MqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate((e) => log.Debug("Disconnected"));
MqttClient.StartAsync(manoptions);
@ -118,8 +118,7 @@ namespace OmniLinkBridge.Modules
// Wait until shutdown
trigger.WaitOne();
log.Debug("Publishing controller offline");
PublishAsync($"{Global.mqtt_prefix}/status", "offline");
PublishControllerStatus("offline");
MqttClient.StopAsync();
}
@ -142,10 +141,13 @@ namespace OmniLinkBridge.Modules
ControllerConnected = false;
if (MqttClient.IsConnected)
{
log.Debug("Publishing controller offline");
PublishAsync($"{Global.mqtt_prefix}/status", "offline");
}
PublishControllerStatus("offline");
}
private void PublishControllerStatus(string status)
{
log.Information("Publishing controller {status}", status);
PublishAsync($"{Global.mqtt_prefix}/status", status);
}
private void PublishConfig()
@ -157,15 +159,14 @@ namespace OmniLinkBridge.Modules
PublishButtons();
PublishMessages();
log.Debug("Publishing controller online");
PublishAsync($"{Global.mqtt_prefix}/status", "online");
PublishControllerStatus("online");
PublishAsync($"{Global.mqtt_prefix}/model", OmniLink.Controller.GetModelText());
PublishAsync($"{Global.mqtt_prefix}/version", OmniLink.Controller.GetVersionText());
}
private void PublishAreas()
{
log.Debug("Publishing areas");
log.Debug("Publishing {type}", "areas");
for (ushort i = 1; i <= OmniLink.Controller.Areas.Count; i++)
{
@ -214,7 +215,7 @@ namespace OmniLinkBridge.Modules
private void PublishZones()
{
log.Debug("Publishing zones");
log.Debug("Publishing {type}", "zones");
for (ushort i = 1; i <= OmniLink.Controller.Zones.Count; i++)
{
@ -255,7 +256,7 @@ namespace OmniLinkBridge.Modules
private void PublishUnits()
{
log.Debug("Publishing units");
log.Debug("Publishing {type}", "units");
for (ushort i = 1; i <= OmniLink.Controller.Units.Count; i++)
{
@ -289,7 +290,7 @@ namespace OmniLinkBridge.Modules
private void PublishThermostats()
{
log.Debug("Publishing thermostats");
log.Debug("Publishing {type}", "thermostats");
for (ushort i = 1; i <= OmniLink.Controller.Thermostats.Count; i++)
{
@ -318,7 +319,7 @@ namespace OmniLinkBridge.Modules
private void PublishButtons()
{
log.Debug("Publishing buttons");
log.Debug("Publishing {type}", "buttons");
for (ushort i = 1; i <= OmniLink.Controller.Buttons.Count; i++)
{
@ -342,7 +343,7 @@ namespace OmniLinkBridge.Modules
private void PublishMessages()
{
log.Debug("Publishing messages");
log.Debug("Publishing {type}", "messages");
for (ushort i = 1; i <= OmniLink.Controller.Messages.Count; i++)
{

View file

@ -1,6 +1,7 @@
using HAI_Shared;
using OmniLinkBridge.OmniLink;
using log4net;
using Serilog;
using Serilog.Context;
using System;
using System.Collections.Generic;
using System.Reflection;
@ -12,7 +13,9 @@ namespace OmniLinkBridge.Modules
{
public class OmniLinkII : IModule, IOmniLinkII
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private bool running = true;
// OmniLink Controller
public clsHAC Controller { get; private set; }
@ -54,8 +57,8 @@ namespace OmniLinkBridge.Modules
public void Startup()
{
while (Global.running)
{
while(running)
{
// Make sure controller connection is active
if (Controller.Connection.ConnectionState == enuOmniLinkConnectionState.Offline &&
retry < DateTime.Now)
@ -71,6 +74,7 @@ namespace OmniLinkBridge.Modules
public void Shutdown()
{
running = false;
trigger.Set();
}
@ -92,7 +96,7 @@ namespace OmniLinkBridge.Modules
private void Disconnect()
{
log.Info("CONNECTION STATUS: Disconnecting");
log.Debug("Controller Status: {connectionStatus}", "Disconnecting");
if (Controller.Connection.ConnectionState != enuOmniLinkConnectionState.Offline)
Controller.Connection.Disconnect();
@ -100,187 +104,86 @@ namespace OmniLinkBridge.Modules
private void HandleConnectStatus(enuOmniLinkCommStatus CS)
{
var status = CS.ToString().ToSpaceTitleCase();
switch (CS)
{
case enuOmniLinkCommStatus.NoReply:
log.Error("CONNECTION STATUS: No Reply");
break;
case enuOmniLinkCommStatus.UnrecognizedReply:
log.Error("CONNECTION STATUS: Unrecognized Reply");
break;
case enuOmniLinkCommStatus.UnsupportedProtocol:
log.Error("CONNECTION STATUS: Unsupported Protocol");
break;
case enuOmniLinkCommStatus.ClientSessionTerminated:
log.Error("CONNECTION STATUS: Client Session Terminated");
break;
case enuOmniLinkCommStatus.ControllerSessionTerminated:
log.Error("CONNECTION STATUS: Controller Session Terminated");
break;
case enuOmniLinkCommStatus.CannotStartNewSession:
log.Error("CONNECTION STATUS: Cannot Start New Session");
break;
case enuOmniLinkCommStatus.LoginFailed:
log.Error("CONNECTION STATUS: Login Failed");
break;
case enuOmniLinkCommStatus.UnableToOpenSocket:
log.Error("CONNECTION STATUS: Unable To Open Socket");
break;
case enuOmniLinkCommStatus.UnableToConnect:
log.Error("CONNECTION STATUS: Unable To Connect");
break;
case enuOmniLinkCommStatus.SocketClosed:
log.Error("CONNECTION STATUS: Socket Closed");
break;
case enuOmniLinkCommStatus.UnexpectedError:
log.Error("CONNECTION STATUS: Unexpected Error");
break;
case enuOmniLinkCommStatus.UnableToCreateSocket:
log.Error("CONNECTION STATUS: Unable To Create Socket");
break;
case enuOmniLinkCommStatus.Retrying:
log.Warn("CONNECTION STATUS: Retrying");
case enuOmniLinkCommStatus.Connecting:
log.Debug("Controller Status: {connectionStatus}", status);
break;
case enuOmniLinkCommStatus.Connected:
IdentifyController();
break;
case enuOmniLinkCommStatus.Connecting:
log.Info("CONNECTION STATUS: Connecting");
break;
case enuOmniLinkCommStatus.Disconnected:
log.Info("CONNECTION STATUS: Disconnected");
log.Information("Controller Status: {connectionStatus}", status);
OnDisconnect?.Invoke(this, new EventArgs());
break;
case enuOmniLinkCommStatus.InterruptedFunctionCall:
if (Global.running)
log.Error("CONNECTION STATUS: Interrupted Function Call");
break;
case enuOmniLinkCommStatus.PermissionDenied:
log.Error("CONNECTION STATUS: Permission Denied");
break;
case enuOmniLinkCommStatus.BadAddress:
log.Error("CONNECTION STATUS: Bad Address");
break;
case enuOmniLinkCommStatus.InvalidArgument:
log.Error("CONNECTION STATUS: Invalid Argument");
break;
case enuOmniLinkCommStatus.TooManyOpenFiles:
log.Error("CONNECTION STATUS: Too Many Open Files");
break;
case enuOmniLinkCommStatus.ResourceTemporarilyUnavailable:
log.Error("CONNECTION STATUS: Resource Temporarily Unavailable");
if (running)
log.Error("Controller Status: {connectionStatus}", status);
break;
case enuOmniLinkCommStatus.Retrying:
case enuOmniLinkCommStatus.OperationNowInProgress:
log.Warn("CONNECTION STATUS: Operation Now In Progress");
break;
case enuOmniLinkCommStatus.OperationAlreadyInProgress:
log.Warn("CONNECTION STATUS: Operation Already In Progress");
break;
case enuOmniLinkCommStatus.SocketOperationOnNonSocket:
log.Error("CONNECTION STATUS: Socket Operation On Non Socket");
break;
case enuOmniLinkCommStatus.DestinationAddressRequired:
log.Error("CONNECTION STATUS: Destination Address Required");
break;
case enuOmniLinkCommStatus.MessgeTooLong:
log.Error("CONNECTION STATUS: Message Too Long");
break;
case enuOmniLinkCommStatus.WrongProtocolType:
log.Error("CONNECTION STATUS: Wrong Protocol Type");
break;
case enuOmniLinkCommStatus.BadProtocolOption:
log.Error("CONNECTION STATUS: Bad Protocol Option");
break;
case enuOmniLinkCommStatus.ProtocolNotSupported:
log.Error("CONNECTION STATUS: Protocol Not Supported");
break;
case enuOmniLinkCommStatus.SocketTypeNotSupported:
log.Error("CONNECTION STATUS: Socket Type Not Supported");
break;
case enuOmniLinkCommStatus.OperationNotSupported:
log.Error("CONNECTION STATUS: Operation Not Supported");
break;
case enuOmniLinkCommStatus.ProtocolFamilyNotSupported:
log.Error("CONNECTION STATUS: Protocol Family Not Supported");
break;
case enuOmniLinkCommStatus.AddressFamilyNotSupported:
log.Error("CONNECTION STATUS: Address Family Not Supported");
break;
case enuOmniLinkCommStatus.AddressInUse:
log.Error("CONNECTION STATUS: Address In Use");
break;
case enuOmniLinkCommStatus.AddressNotAvailable:
log.Error("CONNECTION STATUS: Address Not Available");
break;
case enuOmniLinkCommStatus.NetworkIsDown:
log.Error("CONNECTION STATUS: Network Is Down");
break;
case enuOmniLinkCommStatus.NetworkIsUnreachable:
log.Error("CONNECTION STATUS: Network Is Unreachable");
break;
case enuOmniLinkCommStatus.NetworkReset:
log.Error("CONNECTION STATUS: Network Reset");
break;
case enuOmniLinkCommStatus.ConnectionAborted:
log.Error("CONNECTION STATUS: Connection Aborted");
break;
case enuOmniLinkCommStatus.ConnectionResetByPeer:
log.Error("CONNECTION STATUS: Connection Reset By Peer");
break;
case enuOmniLinkCommStatus.NoBufferSpaceAvailable:
log.Error("CONNECTION STATUS: No Buffer Space Available");
break;
case enuOmniLinkCommStatus.AlreadyConnected:
log.Warn("CONNECTION STATUS: Already Connected");
log.Warning("Controller Status: {connectionStatus}", status);
break;
case enuOmniLinkCommStatus.NoReply:
case enuOmniLinkCommStatus.UnrecognizedReply:
case enuOmniLinkCommStatus.UnsupportedProtocol:
case enuOmniLinkCommStatus.ClientSessionTerminated:
case enuOmniLinkCommStatus.ControllerSessionTerminated:
case enuOmniLinkCommStatus.CannotStartNewSession:
case enuOmniLinkCommStatus.LoginFailed:
case enuOmniLinkCommStatus.UnableToOpenSocket:
case enuOmniLinkCommStatus.UnableToConnect:
case enuOmniLinkCommStatus.SocketClosed:
case enuOmniLinkCommStatus.UnexpectedError:
case enuOmniLinkCommStatus.UnableToCreateSocket:
case enuOmniLinkCommStatus.PermissionDenied:
case enuOmniLinkCommStatus.BadAddress:
case enuOmniLinkCommStatus.InvalidArgument:
case enuOmniLinkCommStatus.TooManyOpenFiles:
case enuOmniLinkCommStatus.ResourceTemporarilyUnavailable:
case enuOmniLinkCommStatus.SocketOperationOnNonSocket:
case enuOmniLinkCommStatus.DestinationAddressRequired:
case enuOmniLinkCommStatus.MessgeTooLong:
case enuOmniLinkCommStatus.WrongProtocolType:
case enuOmniLinkCommStatus.BadProtocolOption:
case enuOmniLinkCommStatus.ProtocolNotSupported:
case enuOmniLinkCommStatus.SocketTypeNotSupported:
case enuOmniLinkCommStatus.OperationNotSupported:
case enuOmniLinkCommStatus.ProtocolFamilyNotSupported:
case enuOmniLinkCommStatus.AddressFamilyNotSupported:
case enuOmniLinkCommStatus.AddressInUse:
case enuOmniLinkCommStatus.AddressNotAvailable:
case enuOmniLinkCommStatus.NetworkIsDown:
case enuOmniLinkCommStatus.NetworkIsUnreachable:
case enuOmniLinkCommStatus.NetworkReset:
case enuOmniLinkCommStatus.ConnectionAborted:
case enuOmniLinkCommStatus.ConnectionResetByPeer:
case enuOmniLinkCommStatus.NoBufferSpaceAvailable:
case enuOmniLinkCommStatus.NotConnected:
log.Error("CONNECTION STATUS: Not Connected");
break;
case enuOmniLinkCommStatus.CannotSendAfterShutdown:
log.Error("CONNECTION STATUS: Cannot Send After Shutdown");
break;
case enuOmniLinkCommStatus.ConnectionTimedOut:
log.Error("CONNECTION STATUS: Connection Timed Out");
break;
case enuOmniLinkCommStatus.ConnectionRefused:
log.Error("CONNECTION STATUS: Connection Refused");
break;
case enuOmniLinkCommStatus.HostIsDown:
log.Error("CONNECTION STATUS: Host Is Down");
break;
case enuOmniLinkCommStatus.HostUnreachable:
log.Error("CONNECTION STATUS: Host Unreachable");
break;
case enuOmniLinkCommStatus.TooManyProcesses:
log.Error("CONNECTION STATUS: Too Many Processes");
break;
case enuOmniLinkCommStatus.NetworkSubsystemIsUnavailable:
log.Error("CONNECTION STATUS: Network Subsystem Is Unavailable");
break;
case enuOmniLinkCommStatus.UnsupportedVersion:
log.Error("CONNECTION STATUS: Unsupported Version");
break;
case enuOmniLinkCommStatus.NotInitialized:
log.Error("CONNECTION STATUS: Not Initialized");
break;
case enuOmniLinkCommStatus.ShutdownInProgress:
log.Error("CONNECTION STATUS: Shutdown In Progress");
break;
case enuOmniLinkCommStatus.ClassTypeNotFound:
log.Error("CONNECTION STATUS: Class Type Not Found");
break;
case enuOmniLinkCommStatus.HostNotFound:
log.Error("CONNECTION STATUS: Host Not Found");
break;
case enuOmniLinkCommStatus.HostNotFoundTryAgain:
log.Error("CONNECTION STATUS: Host Not Found Try Again");
break;
case enuOmniLinkCommStatus.NonRecoverableError:
log.Error("CONNECTION STATUS: Non Recoverable Error");
break;
case enuOmniLinkCommStatus.NoDataOfRequestedType:
log.Error("CONNECTION STATUS: No Data Of Requested Type");
log.Error("Controller Status: {connectionStatus}", status);
break;
default:
break;
}
@ -316,7 +219,10 @@ namespace OmniLinkBridge.Modules
if (Controller.Model == MSG.ModelNumber)
{
Controller.CopySystemInformation(MSG);
log.Info("CONTROLLER IS: " + Controller.GetModelText() + " (" + Controller.GetVersionText() + ")");
using (LogContext.PushProperty("Telemetry", "Controller"))
log.Information("Controller is {ControllerModel} firmware {ControllerVersion}",
Controller.GetModelText(), Controller.GetVersionText());
_ = Connected();
@ -371,7 +277,7 @@ namespace OmniLinkBridge.Modules
private async Task GetNamed(enuObjectType type)
{
log.Debug("Waiting for named units " + type.ToString());
log.Debug("Waiting for named units {unitType}", type.ToString());
GetNextNamed(type, 0);
@ -415,7 +321,9 @@ namespace OmniLinkBridge.Modules
Controller.TimeFormat = MSG2.Time;
Controller.TempFormat = MSG2.Temp;
log.Debug("Temperature format: " + (Controller.TempFormat == enuTempFormat.Fahrenheit ? "Fahrenheit" : "Celsius"));
using (LogContext.PushProperty("Telemetry", "TemperatureFormat"))
log.Debug("Temperature format is {TemperatureFormat}",
(Controller.TempFormat == enuTempFormat.Fahrenheit ? "Fahrenheit" : "Celsius"));
nameWait.Set();
break;
@ -443,7 +351,8 @@ namespace OmniLinkBridge.Modules
tstats[MSG.ObjectNumber] = DateTime.MinValue;
Controller.Connection.Send(new clsOL2MsgRequestExtendedStatus(Controller.Connection, enuObjectType.Thermostat, MSG.ObjectNumber, MSG.ObjectNumber), HandleRequestThermostatStatus);
log.Debug("Added thermostat to watch list " + Controller.Thermostats[MSG.ObjectNumber].Name);
log.Debug("Added thermostat to watch list {thermostatName}",
Controller.Thermostats[MSG.ObjectNumber].Name);
break;
case enuObjectType.Unit:
Controller.Units.CopyProperties(MSG);
@ -471,7 +380,7 @@ namespace OmniLinkBridge.Modules
#region Notifications
private void UnsolicitedNotifications(bool enable)
{
log.Info("Unsolicited notifications " + (enable ? "enabled" : "disabled"));
log.Debug("Unsolicited notifications {status}", (enable ? "enabled" : "disabled"));
Controller.Connection.Send(new clsOL2EnableNotifications(Controller.Connection, enable), null);
}
@ -676,7 +585,7 @@ namespace OmniLinkBridge.Modules
StringBuilder sb = new StringBuilder();
for (int i = 0; i < MSG.MessageLength; i++)
sb.Append(MSG.Data[i].ToString() + " ");
log.Debug("Unhandled SystemEvent Raw: " + sb.ToString() + "Num: " + MSG.SystemEvent);
log.Debug("Unhandled SystemEvent Raw: {raw}, Num: {num}", sb.ToString(), MSG.SystemEvent);
int num = ((int)MSG.MessageLength - 1) / 2;
for (int i = 0; i < num; i++)
@ -745,7 +654,8 @@ namespace OmniLinkBridge.Modules
});
}
else if (Global.verbose_thermostat_timer)
log.Warn("Ignoring unsolicited unknown temp for Thermostat " + Controller.Thermostats[MSG.ObjectNumber(i)].Name);
log.Debug("Ignoring unsolicited unknown temp for Thermostat {thermostatName}",
Controller.Thermostats[MSG.ObjectNumber(i)].Name);
if (!tstats.ContainsKey(MSG.ObjectNumber(i)))
tstats.Add(MSG.ObjectNumber(i), DateTime.Now);
@ -753,7 +663,8 @@ namespace OmniLinkBridge.Modules
tstats[MSG.ObjectNumber(i)] = DateTime.Now;
if (Global.verbose_thermostat_timer)
log.Debug("Unsolicited status received for Thermostat " + Controller.Thermostats[MSG.ObjectNumber(i)].Name);
log.Debug("Unsolicited status received for Thermostat {thermostatName}",
Controller.Thermostats[MSG.ObjectNumber(i)].Name);
}
}
break;
@ -817,7 +728,8 @@ namespace OmniLinkBridge.Modules
Controller.Connection.Send(new clsOL2MsgRequestExtendedStatus(Controller.Connection, enuObjectType.Thermostat, tstat.Key, tstat.Key), HandleRequestThermostatStatus);
if (Global.verbose_thermostat_timer)
log.Debug("Polling status for Thermostat " + Controller.Thermostats[tstat.Key].Name);
log.Debug("Polling status for Thermostat {thermostatName}",
Controller.Thermostats[tstat.Key].Name);
}
// Log every minute if update within 5 minutes and connected
@ -836,10 +748,12 @@ namespace OmniLinkBridge.Modules
});
}
else if (Global.verbose_thermostat_timer)
log.Warn("Ignoring unknown temp for Thermostat " + Controller.Thermostats[tstat.Key].Name);
log.Warning("Ignoring unknown temp for Thermostat {thermostatName}",
Controller.Thermostats[tstat.Key].Name);
}
else if (Global.verbose_thermostat_timer)
log.Warn("Not logging out of date status for Thermostat " + Controller.Thermostats[tstat.Key].Name);
log.Warning("Not logging out of date status for Thermostat {thermostatName}",
Controller.Thermostats[tstat.Key].Name);
}
}
@ -866,7 +780,8 @@ namespace OmniLinkBridge.Modules
tstats[MSG.ObjectNumber(i)] = DateTime.Now;
if (Global.verbose_thermostat_timer)
log.Debug("Polling status received for Thermostat " + Controller.Thermostats[MSG.ObjectNumber(i)].Name);
log.Debug("Polling status received for Thermostat {thermostatName}",
Controller.Thermostats[MSG.ObjectNumber(i)].Name);
}
}
}

View file

@ -1,5 +1,5 @@
using HAI_Shared;
using log4net;
using Serilog;
using System;
using System.Reflection;
using System.Threading;
@ -8,7 +8,7 @@ namespace OmniLinkBridge.Modules
{
public class TimeSyncModule : IModule
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private OmniLinkII OmniLink { get; set; }
@ -78,7 +78,7 @@ namespace OmniLinkBridge.Modules
}
catch
{
log.Warn("Controller time could not be parsed");
log.Warning("Controller time could not be parsed");
DateTime now = DateTime.Now;
OmniLink.Controller.Connection.Send(new clsOL2MsgSetTime(OmniLink.Controller.Connection, (byte)(now.Year % 100), (byte)now.Month, (byte)now.Day, (byte)now.DayOfWeek,
@ -91,7 +91,8 @@ namespace OmniLinkBridge.Modules
if (adj > Global.time_drift)
{
log.Warn("Controller time " + time.ToString("MM/dd/yyyy HH:mm:ss") + " out of sync by " + adj + " seconds");
log.Warning("Controller time {controllerTime} out of sync by {driftSeconds} seconds",
time.ToString("MM/dd/yyyy HH:mm:ss"), adj);
DateTime now = DateTime.Now;
OmniLink.Controller.Connection.Send(new clsOL2MsgSetTime(OmniLink.Controller.Connection, (byte)(now.Year % 100), (byte)now.Month, (byte)now.Day, (byte)now.DayOfWeek,

View file

@ -1,8 +1,8 @@
using log4net;
using Newtonsoft.Json;
using Newtonsoft.Json;
using OmniLinkBridge.Modules;
using OmniLinkBridge.OmniLink;
using OmniLinkBridge.WebAPI;
using Serilog;
using System;
using System.Reflection;
using System.ServiceModel;
@ -14,7 +14,7 @@ namespace OmniLinkBridge
{
public class WebServiceModule : IModule
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
public static OmniLinkII OmniLink { get; private set; }
@ -43,11 +43,11 @@ namespace OmniLinkBridge
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IOmniLinkService), new WebHttpBinding(), "");
host.Open();
log.Info("Listening on " + uri.ToString());
log.Information("Listening on {url}", uri.ToString());
}
catch (CommunicationException ex)
{
log.Error("An exception occurred starting web service", ex);
log.Error(ex, "An exception occurred starting web service");
host.Abort();
}

View file

@ -1,4 +1,5 @@
using log4net;
using OmniLinkBridge.Modules;
using Serilog;
using System;
using System.Net;
using System.Net.Mail;
@ -8,7 +9,7 @@ namespace OmniLinkBridge.Notifications
{
public class EmailNotification : INotification
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
public void Notify(string source, string description, NotificationPriority priority)
{
@ -40,7 +41,7 @@ namespace OmniLinkBridge.Notifications
}
catch (Exception ex)
{
log.Error("An error occurred sending email notification", ex);
log.Error(ex, "An error occurred sending email notification");
}
}
}

View file

@ -1,4 +1,4 @@
using log4net;
using Serilog;
using System;
using System.Collections.Generic;
using System.Reflection;
@ -8,7 +8,7 @@ namespace OmniLinkBridge.Notifications
{
public static class Notification
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly List<INotification> providers = new List<INotification>()
{
@ -27,7 +27,7 @@ namespace OmniLinkBridge.Notifications
}
catch (Exception ex)
{
log.Error("Failed to send notification", ex);
log.Error(ex, "Failed to send notification");
}
});
}

View file

@ -1,4 +1,4 @@
using log4net;
using Serilog;
using System;
using System.Collections.Generic;
using System.Net;
@ -8,7 +8,7 @@ namespace OmniLinkBridge.Notifications
{
public class ProwlNotification : INotification
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly Uri URI = new Uri("https://api.prowlapp.com/publicapi/add");
@ -37,7 +37,7 @@ namespace OmniLinkBridge.Notifications
private void Client_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
if (e.Error != null)
log.Error("An error occurred sending prowl notification", e.Error);
log.Error(e.Error, "An error occurred sending prowl notification");
}
}
}

View file

@ -1,4 +1,4 @@
using log4net;
using Serilog;
using System;
using System.Collections.Specialized;
using System.Net;
@ -8,7 +8,7 @@ namespace OmniLinkBridge.Notifications
{
public class PushoverNotification : INotification
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly Uri URI = new Uri("https://api.pushover.net/1/messages.json");
@ -35,7 +35,7 @@ namespace OmniLinkBridge.Notifications
private void Client_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
if (e.Error != null)
log.Error("An error occurred sending pushover notification", e.Error);
log.Error(e.Error, "An error occurred sending pushover notification");
}
}
}

View file

@ -161,9 +161,6 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="log4net">
<Version>2.0.8</Version>
</PackageReference>
<PackageReference Include="Mono.Posix-4.5">
<Version>4.5.0</Version>
</PackageReference>
@ -173,6 +170,24 @@
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
<PackageReference Include="Serilog">
<Version>2.9.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Formatting.Compact">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.Async">
<Version>1.4.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.Console">
<Version>3.1.1</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.File">
<Version>4.1.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.Http">
<Version>5.2.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<StartArguments>
</StartArguments>
</PropertyGroup>
</Project>

View file

@ -1,5 +1,5 @@
# HAI / Leviton Omni Controller
controller_address =
controller_address =
controller_port = 4369
controller_key1 = 00-00-00-00-00-00-00-00
controller_key2 = 00-00-00-00-00-00-00-00

View file

@ -1,7 +1,11 @@
using Mono.Unix;
using Serilog;
using Serilog.Events;
using Serilog.Filters;
using Serilog.Formatting.Compact;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.ServiceProcess;
using System.Threading.Tasks;
@ -12,10 +16,15 @@ namespace OmniLinkBridge
{
static CoreServer server;
static void Main(string[] args)
static int Main(string[] args)
{
bool interactive = false;
string config_file = "OmniLinkBridge.ini";
string log_file = "log.txt";
bool log_clef = false;
LogEventLevel log_level = LogEventLevel.Information;
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
@ -24,15 +33,27 @@ namespace OmniLinkBridge
case "-h":
case "-help":
ShowHelp();
return;
return 0;
case "-c":
Global.config_file = args[++i];
config_file = args[++i];
break;
case "-e":
Settings.UseEnvironment = true;
Global.UseEnvironment = true;
break;
case "-d":
Settings.ShowDebug = true;
Global.DebugSettings = true;
break;
case "-lf":
log_file = args[++i];
if (string.Compare(log_file, "disable", true) == 0)
log_file = null;
break;
case "-lj":
log_clef = true;
break;
case "-ll":
Enum.TryParse(args[++i], out log_level);
break;
case "-s":
Global.webapi_subscriptions_file = args[++i];
@ -43,27 +64,53 @@ namespace OmniLinkBridge
}
}
if (string.IsNullOrEmpty(Global.config_file))
Global.config_file = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) +
Path.DirectorySeparatorChar + "OmniLinkBridge.ini";
config_file = GetFullPath(config_file);
if (string.IsNullOrEmpty(Global.webapi_subscriptions_file))
Global.webapi_subscriptions_file = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) +
Path.DirectorySeparatorChar + "WebSubscriptions.json";
Global.webapi_subscriptions_file = GetFullPath(Global.webapi_subscriptions_file ?? "WebSubscriptions.json");
log4net.Config.XmlConfigurator.Configure();
// Use TLS 1.2 as default connection
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
if(Environment.UserInteractive || interactive)
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
string log_format = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{SourceContext} {Level:u3}] {Message:lj}{NewLine}{Exception}";
var log_config = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("Application", "OmniLinkBridge")
.Enrich.WithProperty("Session", Guid.NewGuid())
.Enrich.WithProperty("User", (Environment.UserName + Environment.MachineName).GetHashCode())
.Enrich.FromLogContext();
if (log_file != null)
{
log_file = GetFullPath(log_file);
if (log_clef)
log_config = log_config.WriteTo.Async(a => a.File(new CompactJsonFormatter(), log_file, log_level,
rollingInterval: RollingInterval.Day, retainedFileCountLimit: 15));
else
log_config = log_config.WriteTo.Async(a => a.File(log_file, log_level, log_format,
rollingInterval: RollingInterval.Day, retainedFileCountLimit: 15));
}
if (UseTelemetry())
log_config = log_config.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(Matching.WithProperty("Telemetry"))
.WriteTo.Http("https://telemetry.excalibur-partners.com"));
if (Environment.UserInteractive || interactive)
log_config = log_config.WriteTo.Console(outputTemplate: log_format);
Log.Logger = log_config.CreateLogger();
try
{
Settings.LoadSettings(Global.config_file);
Settings.LoadSettings(config_file);
}
catch
{
// Errors are logged in LoadSettings();
Environment.Exit(1);
Log.CloseAndFlush();
return -1;
}
if (Environment.UserInteractive || interactive)
@ -104,6 +151,16 @@ namespace OmniLinkBridge
ServiceBase.Run(ServicesToRun);
}
return 0;
}
static string GetFullPath(string file)
{
if (Path.IsPathRooted(file))
return file;
return Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), file);
}
protected static void myHandler(object sender, ConsoleCancelEventArgs args)
@ -117,16 +174,28 @@ namespace OmniLinkBridge
return Type.GetType("Mono.Runtime") != null;
}
static bool UseTelemetry()
{
return string.Compare(Environment.GetEnvironmentVariable("TELEMETRY_OPTOUT"), "1") != 0;
}
static void ShowHelp()
{
Console.WriteLine(
AppDomain.CurrentDomain.FriendlyName + " [-c config_file] [-e] [-d] [-s subscriptions_file] [-i]\n" +
"\t[-debug-config] [-ignore-env]\n" +
"\t-c Specifies the configuration file. Default is OmniLinkBridge.ini\n" +
"\t-e Check environment variables for configuration settings\n" +
"\t-d Show debug output for configuration loading\n" +
"\t-s Specifies the web api subscriptions file. Default is WebSubscriptions.json\n" +
"\t-i Run in interactive mode");
AppDomain.CurrentDomain.FriendlyName + " [-c config_file] [-e] [-d] [-j] [-s subscriptions_file]\n" +
"\t[-lf log_file|disable] [-lj [-ll verbose|debug|information|warning|error] [-i]\n" +
"\t-c Specifies the configuration file. Default is OmniLinkBridge.ini\n" +
"\t-e Check environment variables for configuration settings\n" +
"\t-d Show debug ouput for configuration loading\n" +
"\t-s Specifies the web api subscriptions file. Default is WebSubscriptions.json\n" +
"\t-lf Specifies the rolling log file. Retention is 15 days. Default is log.txt.\n" +
"\t-lj Write logs as CLEF (compact log event format) JSON.\n" +
"\t-ll Minimum level at which events will be logged. Default is information.\n" +
"\t-i Run in interactive mode");
Console.WriteLine(
"\nOmniLink Bridge collects anonymous telemetry data to help improve the software.\n" +
"You can opt of telemetry by setting a TELEMETRY_OPTOUT environment variable to 1.");
}
}
}

View file

@ -5,12 +5,12 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OmniLinkBridge")]
[assembly: AssemblyTitle("OmniLink Bridge")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Excalibur Partners, LLC")]
[assembly: AssemblyProduct("OmniLinkBridge")]
[assembly: AssemblyCopyright("Copyright © Excalibur Partners, LLC 2019")]
[assembly: AssemblyCopyright("Copyright © Excalibur Partners, LLC 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View file

@ -1,4 +1,4 @@
using log4net;
using Serilog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -7,14 +7,13 @@ using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Reflection;
using System.Threading;
namespace OmniLinkBridge
{
public static class Settings
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static bool ShowDebug { get; set; }
public static bool UseEnvironment { get; set; }
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
public static void LoadSettings(string file)
{
@ -115,10 +114,10 @@ namespace OmniLinkBridge
private static string CheckEnv(this NameValueCollection settings, string name)
{
string env = UseEnvironment ? Environment.GetEnvironmentVariable(name.ToUpper()) : null;
string env = Global.UseEnvironment ? Environment.GetEnvironmentVariable(name.ToUpper()) : null;
string value = !string.IsNullOrEmpty(env) ? env : settings[name];
if (ShowDebug)
if (Global.DebugSettings)
log.Debug((!string.IsNullOrEmpty(env) ? "ENV" : "CONF").PadRight(5) + $"{name}: {value}");
return value;
@ -170,7 +169,7 @@ namespace OmniLinkBridge
}
catch (Exception ex)
{
log.Error("Invalid override zone specified for " + section, ex);
log.Error(ex, "Invalid override zone specified for {section}", section);
throw;
}
}
@ -181,7 +180,7 @@ namespace OmniLinkBridge
if(string.IsNullOrEmpty(value))
{
log.Error("Empty string specified for " + section);
log.Error("Empty string specified for {section}", section);
throw new Exception();
}
@ -196,7 +195,7 @@ namespace OmniLinkBridge
}
catch
{
log.Error("Invalid integer specified for " + section);
log.Error("Invalid integer specified for {section}", section);
throw;
}
}
@ -214,7 +213,7 @@ namespace OmniLinkBridge
}
catch
{
log.Error("Invalid range specified for " + section);
log.Error("Invalid range specified for {section}", section);
throw;
}
}
@ -232,7 +231,7 @@ namespace OmniLinkBridge
}
catch
{
log.Error("Invalid port specified for " + section);
log.Error("Invalid port specified for {section}", section);
throw;
}
}
@ -245,7 +244,7 @@ namespace OmniLinkBridge
}
catch
{
log.Error("Invalid email specified for " + section);
log.Error("Invalid email specified for {section}", section);
throw;
}
}
@ -269,7 +268,7 @@ namespace OmniLinkBridge
}
catch
{
log.Error("Invalid email specified for " + section);
log.Error("Invalid email specified for {section}", section);
throw;
}
}
@ -285,7 +284,7 @@ namespace OmniLinkBridge
}
catch
{
log.Error("Invalid string specified for " + section);
log.Error("Invalid string specified for {section}", section);
throw;
}
}
@ -302,7 +301,7 @@ namespace OmniLinkBridge
return false;
else
{
log.Error("Invalid yes/no or true/false specified for " + section);
log.Error("Invalid yes/no or true/false specified for {section}", section);
throw new Exception();
}
}
@ -330,24 +329,24 @@ namespace OmniLinkBridge
return settings;
}
private static NameValueCollection LoadCollection(string sFile)
private static NameValueCollection LoadCollection(string file)
{
if (ShowDebug)
log.Debug($"Using settings file {sFile}");
if (Global.DebugSettings)
log.Debug("Using settings file {file}", file);
if(!File.Exists(sFile))
if(!File.Exists(file))
{
log.Warn($"Unable to locate settings file {sFile}");
log.Warning("Unable to locate settings file {file}", file);
return new NameValueCollection();
}
try
{
return LoadCollection(File.ReadAllLines(sFile));
return LoadCollection(File.ReadAllLines(file));
}
catch (FileNotFoundException ex)
{
log.Error("Error parsing settings file " + sFile, ex);
log.Error(ex, "Error parsing settings file {file}", file);
throw;
}
}

View file

@ -1,6 +1,6 @@
using HAI_Shared;
using log4net;
using OmniLinkBridge.WebAPI;
using Serilog;
using System;
using System.Collections.Generic;
using System.Reflection;
@ -12,7 +12,7 @@ namespace OmniLinkBridge.WebAPI
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class OmniLinkService : IOmniLinkService
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
public void Subscribe(SubscribeContract contract)
{
@ -39,7 +39,7 @@ namespace OmniLinkBridge.WebAPI
public AreaContract GetArea(ushort id)
{
log.Debug("GetArea: " + id);
log.Debug("GetArea: {id}", id);
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("type", "area");
@ -139,7 +139,7 @@ namespace OmniLinkBridge.WebAPI
public ZoneContract GetZone(ushort id)
{
log.Debug("GetZone: " + id);
log.Debug("GetZone: {id}", id);
WebOperationContext ctx = WebOperationContext.Current;
@ -173,7 +173,7 @@ namespace OmniLinkBridge.WebAPI
public UnitContract GetUnit(ushort id)
{
log.Debug("GetUnit: " + id);
log.Debug("GetUnit: {id}", id);
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("type", "unit");
@ -183,7 +183,7 @@ namespace OmniLinkBridge.WebAPI
public void SetUnit(CommandContract unit)
{
log.Debug("SetUnit: " + unit.id + " to " + unit.value + "%");
log.Debug("SetUnit: {id} to {value}%", unit.id, unit.value);
if (unit.value == 0)
WebServiceModule.OmniLink.Controller.SendCommand(enuUnitCommand.Off, 0, unit.id);
@ -196,7 +196,7 @@ namespace OmniLinkBridge.WebAPI
public void SetUnitKeypadPress(CommandContract unit)
{
log.Debug("SetUnitKeypadPress: " + unit.id + " to " + unit.value + " button");
log.Debug("SetUnitKeypadPress: {id} to {value}", unit.id, unit.value);
WebServiceModule.OmniLink.Controller.SendCommand(enuUnitCommand.LutronHomeWorksKeypadButtonPress, BitConverter.GetBytes(unit.value)[0], unit.id);
}
@ -217,7 +217,7 @@ namespace OmniLinkBridge.WebAPI
public ThermostatContract GetThermostat(ushort id)
{
log.Debug("GetThermostat: " + id);
log.Debug("GetThermostat: {id}", id);
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("type", "thermostat");
@ -236,7 +236,7 @@ namespace OmniLinkBridge.WebAPI
}
int temp = tempHigh.ToOmniTemp();
log.Debug("SetThermostatCoolSetpoint: " + unit.id + " to " + unit.value + tempUnit + "(" + temp + ")");
log.Debug("SetThermostatCoolSetpoint: {id} to {value}{tempUnit} {temp}", unit.id, unit.value, tempUnit, temp);
WebServiceModule.OmniLink.Controller.SendCommand(enuUnitCommand.SetHighSetPt, BitConverter.GetBytes(temp)[0], unit.id);
}
@ -251,25 +251,25 @@ namespace OmniLinkBridge.WebAPI
}
int temp = tempLoad.ToOmniTemp();
log.Debug("SetThermostatHeatSetpoint: " + unit.id + " to " + unit.value + tempUnit + "(" + temp + ")");
log.Debug("SetThermostatHeatSetpoint: {id} to {value}{tempUnit} {temp}", unit.id, unit.value, tempUnit, temp);
WebServiceModule.OmniLink.Controller.SendCommand(enuUnitCommand.SetLowSetPt, BitConverter.GetBytes(temp)[0], unit.id);
}
public void SetThermostatMode(CommandContract unit)
{
log.Debug("SetThermostatMode: " + unit.id + " to " + unit.value);
log.Debug("SetThermostatMode: {id} to {value}", unit.id, unit.value);
WebServiceModule.OmniLink.Controller.SendCommand(enuUnitCommand.Mode, BitConverter.GetBytes(unit.value)[0], unit.id);
}
public void SetThermostatFanMode(CommandContract unit)
{
log.Debug("SetThermostatFanMode: " + unit.id + " to " + unit.value);
log.Debug("SetThermostatFanMode: {id} to {value}", unit.id, unit.value);
WebServiceModule.OmniLink.Controller.SendCommand(enuUnitCommand.Fan, BitConverter.GetBytes(unit.value)[0], unit.id);
}
public void SetThermostatHold(CommandContract unit)
{
log.Debug("SetThermostatHold: " + unit.id + " to " + unit.value);
log.Debug("SetThermostatHold: {id} to {value}", unit.id, unit.value);
WebServiceModule.OmniLink.Controller.SendCommand(enuUnitCommand.Hold, BitConverter.GetBytes(unit.value)[0], unit.id);
}
@ -290,7 +290,7 @@ namespace OmniLinkBridge.WebAPI
public void PushButton(CommandContract unit)
{
log.Debug("PushButton: " + unit.id);
log.Debug("PushButton: {id}", unit.id);
WebServiceModule.OmniLink.Controller.SendCommand(enuUnitCommand.Button, 0, unit.id);
}
}

View file

@ -1,5 +1,5 @@
using log4net;
using Newtonsoft.Json;
using Newtonsoft.Json;
using Serilog;
using System;
using System.Collections.Generic;
using System.IO;
@ -10,7 +10,7 @@ namespace OmniLinkBridge.WebAPI
{
static class WebNotification
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger log = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType);
private static List<string> subscriptions = new List<string>();
private static readonly object subscriptions_lock = new object();
@ -52,7 +52,7 @@ namespace OmniLinkBridge.WebAPI
}
catch (Exception ex)
{
log.Error("An error occurred sending notification to " + subscription, ex);
log.Error(ex, "An error occurred sending notification to {client}", subscription);
subscriptions.Remove(subscription);
SaveSubscriptions();
}
@ -63,7 +63,7 @@ namespace OmniLinkBridge.WebAPI
{
if (e.Error != null)
{
log.Error("An error occurred sending notification to " + e.UserState.ToString(), e.Error);
log.Error(e.Error, "An error occurred sending notification to {client}", e.UserState.ToString());
lock (subscriptions_lock)
subscriptions.Remove(e.UserState.ToString());
@ -88,7 +88,7 @@ namespace OmniLinkBridge.WebAPI
}
catch (Exception ex)
{
log.Error("An error occurred restoring subscriptions", ex);
log.Error(ex, "An error occurred restoring subscriptions");
}
}
@ -107,7 +107,7 @@ namespace OmniLinkBridge.WebAPI
}
catch (Exception ex)
{
log.Error("An error occurred saving subscriptions", ex);
log.Error(ex, "An error occurred saving subscriptions");
}
}
}

View file

@ -2,12 +2,12 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("OmniLinkBridgeTest")]
[assembly: AssemblyTitle("OmniLink Bridge Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Excalibur Partners, LLC")]
[assembly: AssemblyProduct("OmniLinkBridgeTest")]
[assembly: AssemblyCopyright("Copyright © Excalibur Partners, LLC 2019")]
[assembly: AssemblyCopyright("Copyright © Excalibur Partners, LLC 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View file

@ -9,7 +9,7 @@ You can use docker to build an image from git or download the [binary here](http
- .NET Framework 4.5.2 (or Mono equivalent)
## Operation
OmniLinkBridge is divided into the following modules and configurable settings. Configuration settings can also be set as environment variables by using their name in uppercase. Refer to [OmniLinkBridge.ini](https://github.com/excaliburpartners/OmniLinkBridge/blob/master/OmniLinkBridge/OmniLinkBridge.ini) for specifics.
OmniLink Bridge is divided into the following modules and configurable settings. Configuration settings can also be set as environment variables by using their name in uppercase. Refer to [OmniLinkBridge.ini](https://github.com/excaliburpartners/OmniLinkBridge/blob/master/OmniLinkBridge/OmniLinkBridge.ini) for specifics.
- OmniLinkII: controller_
- Maintains connection to the OmniLink controller
@ -329,4 +329,7 @@ The [MySQL ODBC Connector](http://dev.mysql.com/downloads/connector/odbc/) is re
Configure mysql_connection in OmniLinkBridge.ini. For Windows change DRIVER={MySQL} to name of the driver shown in the ODBC Data Source Administrator.
```
mysql_connection = DRIVER={MySQL};SERVER=localhost;DATABASE=OmniLinkBridge;USER=root;PASSWORD=myPassword;OPTION=3;
```
```
## Telemetry
OmniLink Bridge collects anonymous telemetry data to help improve the software. You can opt of telemetry by setting a TELEMETRY_OPTOUT environment variable to 1.